react-lite-rich-text-editor 1.1.5 → 1.1.7
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 +48 -1
- package/dist/index.cjs.js +489 -235
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +489 -235
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -231,6 +231,27 @@ const FaFont = ({
|
|
|
231
231
|
}
|
|
232
232
|
});
|
|
233
233
|
};
|
|
234
|
+
const FaLink = ({
|
|
235
|
+
className,
|
|
236
|
+
size,
|
|
237
|
+
color,
|
|
238
|
+
style
|
|
239
|
+
}) => {
|
|
240
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
241
|
+
className: className,
|
|
242
|
+
style: {
|
|
243
|
+
display: 'inline-flex',
|
|
244
|
+
alignItems: 'center',
|
|
245
|
+
justifyContent: 'center',
|
|
246
|
+
fontSize: size || '1em',
|
|
247
|
+
color: color || 'inherit',
|
|
248
|
+
...style
|
|
249
|
+
},
|
|
250
|
+
dangerouslySetInnerHTML: {
|
|
251
|
+
__html: `<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"></path></svg>`
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
};
|
|
234
255
|
const FaTable = ({
|
|
235
256
|
className,
|
|
236
257
|
size,
|
|
@@ -388,7 +409,7 @@ function styleInject(css, ref) {
|
|
|
388
409
|
}
|
|
389
410
|
}
|
|
390
411
|
|
|
391
|
-
var css_248z = ".rte-container{background-color:#fff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,.05);overflow:hidden;transition:all .2s cubic-bezier(.4,0,.2,1)}.rte-container:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59,130,246,.1)}.rte-toolbar{align-items:center;background-color:#f9fafb;border-bottom:1px solid #f3f4f6;display:flex;flex-wrap:wrap;gap:4px;padding:8px}.rte-toolbar-button{align-items:center;background:transparent;border:none;border-radius:6px;color:#4b5563;cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all .15s ease;width:32px}.rte-toolbar-button:hover{background-color:#f3f4f6;color:#111827}.rte-toolbar-button.active{background-color:#eff6ff;color:#2563eb}.rte-toolbar-button:disabled{cursor:not-allowed;opacity:.5}.rte-toolbar-button-danger:hover{background-color:#fef2f2!important;color:#dc2626!important}.rte-toolbar-select{background-color:#fff;border:1px solid #e5e7eb;border-radius:6px;color:#374151;cursor:pointer;font-size:14px;height:32px;outline:none;padding:0 8px;transition:border-color .15s ease}.rte-toolbar-select:hover{border-color:#d1d5db}.rte-toolbar-select:focus{border-color:#3b82f6}.rte-color-picker-label{align-items:center;border-radius:6px;cursor:pointer;display:flex;height:32px;justify-content:center;position:relative;transition:background-color .15s ease;width:32px}.rte-color-picker-label:hover{background-color:#f3f4f6}.rte-color-input{cursor:pointer;height:100%;inset:0;opacity:0;position:absolute;width:100%}.rte-content{color:#1f2937;font-family:inherit;font-size:16px;line-height:1.6;min-height:150px;outline:none;overflow-y:auto;padding:12px;word-break:break-word}.rte-content ul{list-style-type:disc;margin-left:1.5rem}.rte-content ol{list-style-type:decimal;margin-left:1.5rem}.rte-content img{border-radius:8px;display:block;height:auto;max-width:100%}.rte-content table{border-collapse:collapse;margin:16px 0;width:100%}.rte-content td,.rte-content th{border:1px solid #e5e7eb;min-width:40px;padding:12px;word-break:break-word}.video-container{border-radius:12px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1);height:0;margin:16px 0;max-width:100%;overflow:hidden;padding-bottom:56.25%;position:relative}.video-container iframe{height:100%;left:0;position:absolute;top:0;width:100%}.rte-modal-overlay{align-items:center;animation:rte-fade-in .2s ease-out;backdrop-filter:blur(4px);background-color:rgba(0,0,0,.5);display:flex;inset:0;justify-content:center;position:fixed;z-index:9999}.rte-modal{animation:rte-zoom-in .2s ease-out;background-color:#fff;border:1px solid #f3f4f6;border-radius:16px;box-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04);display:flex;flex-direction:column;gap:16px;max-width:400px;padding:0;width:100%}.rte-modal-title{color:#111827;flex:1;font-size:18px;font-weight:600;margin:0;text-align:center}.rte-modal-header{align-items:center;border-bottom:1px solid #f3f4f6;display:flex;justify-content:space-between;padding:20px 24px 16px}.rte-form-group{display:flex;flex-direction:column;gap:8px;padding:16px 24px}.rte-label{color:#374151;font-size:14px;font-weight:600}.rte-input{border:1px solid #d1d5db;border-radius:8px;outline:none;padding:8px 12px;transition:all .15s ease;width:
|
|
412
|
+
var css_248z = ".rte-container{background-color:#fff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,.05);overflow:hidden;transition:all .2s cubic-bezier(.4,0,.2,1)}.rte-container:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59,130,246,.1)}.rte-toolbar{align-items:center;background-color:#f9fafb;border-bottom:1px solid #f3f4f6;display:flex;flex-wrap:wrap;gap:4px;padding:8px}.rte-toolbar-button{align-items:center;background:transparent;border:none;border-radius:6px;color:#4b5563;cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all .15s ease;width:32px}.rte-toolbar-button:hover{background-color:#f3f4f6;color:#111827}.rte-toolbar-button.active{background-color:#eff6ff;color:#2563eb}.rte-toolbar-button:disabled{cursor:not-allowed;opacity:.5}.rte-toolbar-button-danger:hover{background-color:#fef2f2!important;color:#dc2626!important}.rte-toolbar-select{background-color:#fff;border:1px solid #e5e7eb;border-radius:6px;color:#374151;cursor:pointer;font-size:14px;height:32px;outline:none;padding:0 8px;transition:border-color .15s ease}.rte-toolbar-select:hover{border-color:#d1d5db}.rte-toolbar-select:focus{border-color:#3b82f6}.rte-heading-select{width:112px}.rte-toolbar-button-text{font-size:12px;font-weight:700;min-width:32px;padding:0 8px;width:auto}.rte-color-picker-label{align-items:center;border-radius:6px;cursor:pointer;display:flex;height:32px;justify-content:center;position:relative;transition:background-color .15s ease;width:32px}.rte-color-picker-label:hover{background-color:#f3f4f6}.rte-color-input{cursor:pointer;height:100%;inset:0;opacity:0;position:absolute;width:100%}.rte-content{color:#1f2937;font-family:inherit;font-size:16px;line-height:1.6;min-height:150px;outline:none;overflow-y:auto;padding:12px;word-break:break-word}.rte-content-wrapper{position:relative}.rte-placeholder{color:#9ca3af;font-size:16px;line-height:1.6;pointer-events:none;position:absolute;top:12px;user-select:none}.rte-content ul{list-style-type:disc;margin-left:1.5rem}.rte-content ol{list-style-type:decimal;margin-left:1.5rem}.rte-content blockquote{background:#f8fafc;border-left:4px solid #bfdbfe;border-radius:0 8px 8px 0;color:#475569;margin:12px 0;padding:8px 14px}.rte-content img{border-radius:8px;display:block;height:auto;max-width:100%}.rte-content table{border-collapse:collapse;margin:16px 0;width:100%}.rte-content td,.rte-content th{border:1px solid #e5e7eb;min-width:40px;padding:12px;word-break:break-word}.video-container{border-radius:12px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1);display:block;height:0;line-height:0;margin:16px 0;max-width:100%;overflow:hidden;padding-bottom:56.25%;position:relative;width:100%}.video-container iframe{height:100%;left:0;position:absolute;top:0;width:100%}.rte-modal-overlay{align-items:center;animation:rte-fade-in .2s ease-out;backdrop-filter:blur(4px);background-color:rgba(0,0,0,.5);display:flex;inset:0;justify-content:center;position:fixed;z-index:9999}.rte-modal{animation:rte-zoom-in .2s ease-out;background-color:#fff;border:1px solid #f3f4f6;border-radius:16px;box-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 10px 10px -5px rgba(0,0,0,.04);display:flex;flex-direction:column;gap:16px;max-width:400px;padding:0;width:100%}.rte-modal-title{color:#111827;flex:1;font-size:18px;font-weight:600;margin:0;text-align:center}.rte-modal-header{align-items:center;border-bottom:1px solid #f3f4f6;display:flex;justify-content:space-between;padding:20px 24px 16px}.rte-form-group{display:flex;flex-direction:column;gap:8px;padding:16px 24px}.rte-label{color:#374151;font-size:14px;font-weight:600}.rte-input{border:1px solid #d1d5db;border-radius:8px;box-sizing:border-box;font-size:14px;outline:none;padding:8px 12px;transition:all .15s ease;width:100%}.rte-input:focus{border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59,130,246,.1)}.rte-modal-actions{border-top:1px solid #f3f4f6;display:flex;gap:12px;justify-content:flex-end;padding:16px 24px 20px}.rte-button{border:none;border-radius:8px;cursor:pointer;font-weight:500;padding:8px 16px;transition:all .15s ease}.rte-button-secondary{background-color:#f3f4f6;color:#4b5563}.rte-button-secondary:hover{background-color:#e5e7eb}.rte-button-primary{background-color:#2563eb;box-shadow:0 1px 2px rgba(0,0,0,.05);color:#fff}.rte-button-primary:hover{background-color:#1d4ed8}.rte-button-primary:disabled{cursor:not-allowed;opacity:.5}.rte-spinner-container{align-items:center;display:flex;justify-content:center;padding:16px}.rte-spinner{animation:rte-spin .8s linear infinite;border:3px solid #eff6ff;border-radius:50%;border-top-color:#3b82f6;height:32px;width:32px}@keyframes rte-spin{to{transform:rotate(1turn)}}@keyframes rte-fade-in{0%{opacity:0}to{opacity:1}}@keyframes rte-zoom-in{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.image-container{display:block;line-height:0;margin:16px 0;max-width:100%;position:relative;width:fit-content}.image-container.image-align-center,.video-container.image-align-center{margin-left:auto;margin-right:auto}.image-container.image-align-left,.video-container.image-align-left{margin-left:0;margin-right:auto}.image-container.image-align-right,.video-container.image-align-right{margin-left:auto;margin-right:0}.image-container.rte-media-selected,.video-container.rte-media-selected{border-radius:12px;outline:2px solid #3b82f6;outline-offset:2px}.image-media-frame{display:block;line-height:0;max-width:100%;position:relative;width:100%}.image-media-frame img{border-radius:12px;display:block;height:auto;margin:0;max-width:100%;width:auto}.image-container[data-width-percent] .image-media-frame,.image-container[data-width-percent] .image-media-frame img{width:100%}.image-delete-button{align-items:center;background:#ef4444;border:3px solid #fff;border-radius:9999px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);color:#fff;cursor:pointer;display:none;font-size:18px;font-weight:700;height:26px;justify-content:center;line-height:1;padding:0;pointer-events:none;position:absolute;right:0;top:0;transform:translate(50%,-50%);transition:all .2s cubic-bezier(.4,0,.2,1);width:26px;z-index:50}.rte-content.rte-is-editable.rte-is-focused .image-delete-button,.rte-content.rte-is-editable.rte-is-focused .video-delete-button{display:flex;pointer-events:auto}.rte-content.rte-is-editable .video-container.rte-media-selected iframe{pointer-events:none}.image-delete-button:hover{background:#b91c1c;box-shadow:0 10px 15px -3px rgba(0,0,0,.1);transform:translate(50%,-50%) scale(1.1)}.media-resize-handle{background:#3b82f6;border:2px solid #fff;border-radius:3px;bottom:2px;box-shadow:0 1px 3px rgba(15,23,42,.2);cursor:nwse-resize;height:12px;pointer-events:auto;position:absolute;right:2px;width:12px;z-index:60}.media-resize-handle:hover{background:#2563eb}.video-container .media-resize-handle{bottom:6px;right:6px}.video-container .video-delete-button{z-index:70}.rte-table-delete-btn,.rte-table-delete-hover{align-items:center;display:flex;justify-content:center}.rte-table-delete-btn{background:#fff;border:1px solid #ef4444;border-radius:6px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);color:#ef4444;cursor:pointer;height:28px;padding:0;transition:all .2s ease;width:28px}.rte-table-delete-btn:hover{background:#ef4444;color:#fff;transform:scale(1.1)}.rte-table-delete-btn:active{transform:scale(.95)}.rte-media-toolbar{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);display:flex;gap:2px;padding:4px;pointer-events:auto}.rte-media-toolbar button{align-items:center;background:transparent;border:none;border-radius:4px;color:#4b5563;cursor:pointer;display:flex;font-size:11px;font-weight:600;height:28px;justify-content:center;min-width:28px;padding:0 6px;transition:all .15s ease}.rte-media-toolbar button.active,.rte-media-toolbar button:hover{background-color:#eff6ff;color:#2563eb}.rte-media-toolbar button.danger{color:#ef4444}.rte-media-toolbar button.danger:hover{background-color:#fef2f2;color:#dc2626}.rte-media-toolbar-divider{background:#e5e7eb;height:18px;margin:0 2px;width:1px}.image-container:after{clear:both;content:\"\";display:table}.rte-footer{background-color:#fcfcfd;border-top:1px solid #f3f4f6;display:flex;justify-content:flex-end;padding:6px 16px;user-select:none}.rte-footer-content{align-items:center;color:#9ca3af;display:flex;font-size:11px;gap:10px;letter-spacing:.025em}.rte-footer-separator{color:#e5e7eb;font-size:14px;line-height:1}.rte-footer-item b{color:#6b7280;font-weight:600}";
|
|
392
413
|
styleInject(css_248z);
|
|
393
414
|
|
|
394
415
|
// Helper functions for HTML escaping
|
|
@@ -405,11 +426,12 @@ function RichTextEditor({
|
|
|
405
426
|
showEditButton,
|
|
406
427
|
onBlur,
|
|
407
428
|
disabled = false,
|
|
408
|
-
editable: initialEditable =
|
|
429
|
+
editable: initialEditable = true,
|
|
409
430
|
value,
|
|
410
431
|
isLoading,
|
|
411
432
|
isList = false,
|
|
412
433
|
label,
|
|
434
|
+
placeholder = "Type here...",
|
|
413
435
|
showBorder = true,
|
|
414
436
|
paddingLeft,
|
|
415
437
|
minHeight,
|
|
@@ -427,6 +449,7 @@ function RichTextEditor({
|
|
|
427
449
|
const [editable, setEditable] = useState(initialEditable);
|
|
428
450
|
const [editorFocused, setEditorFocused] = useState(false);
|
|
429
451
|
const lastSynchronizedHtmlRef = useRef("");
|
|
452
|
+
const syncProcessedMediaRef = useRef(() => {});
|
|
430
453
|
useEffect(() => {
|
|
431
454
|
setEditable(initialEditable);
|
|
432
455
|
}, [initialEditable]);
|
|
@@ -445,6 +468,7 @@ function RichTextEditor({
|
|
|
445
468
|
// NEW: Track current line height
|
|
446
469
|
const [currentLineHeight, setCurrentLineHeight] = useState("");
|
|
447
470
|
const [activeAlign, setActiveAlign] = useState(null);
|
|
471
|
+
const [currentBlockFormat, setCurrentBlockFormat] = useState("div");
|
|
448
472
|
const [imageModalOpen, setImageModalOpen] = useState(false);
|
|
449
473
|
const [selectedImageUrl, setSelectedImageUrl] = useState("");
|
|
450
474
|
const [zoomLevel, setZoomLevel] = useState(1);
|
|
@@ -456,11 +480,12 @@ function RichTextEditor({
|
|
|
456
480
|
const [tableRows, setTableRows] = useState(3);
|
|
457
481
|
const [tableCols, setTableCols] = useState(3);
|
|
458
482
|
const [selectionVersion, setSelectionVersion] = useState(0);
|
|
459
|
-
const [
|
|
483
|
+
const [selectedMedia, setSelectedMedia] = useState(null);
|
|
460
484
|
const [metrics, setMetrics] = useState({
|
|
461
485
|
words: 0,
|
|
462
486
|
chars: 0
|
|
463
487
|
});
|
|
488
|
+
const [isEmpty, setIsEmpty] = useState(!value);
|
|
464
489
|
const updateMetrics = useCallback(() => {
|
|
465
490
|
if (!editorRef.current) return;
|
|
466
491
|
// Calculate metrics immediately but outside of render path
|
|
@@ -472,6 +497,11 @@ function RichTextEditor({
|
|
|
472
497
|
words,
|
|
473
498
|
chars
|
|
474
499
|
});
|
|
500
|
+
|
|
501
|
+
// Track emptiness for the placeholder. Account for media-only content.
|
|
502
|
+
const stripped = text.replace(/[\u200B\u00A0\s]/g, "");
|
|
503
|
+
const hasMedia = !!editorRef.current.querySelector("img, table, iframe");
|
|
504
|
+
setIsEmpty(stripped.length === 0 && !hasMedia);
|
|
475
505
|
}, []);
|
|
476
506
|
const openImageModal = url => {
|
|
477
507
|
if (editorRef.current) {
|
|
@@ -533,16 +563,15 @@ function RichTextEditor({
|
|
|
533
563
|
}, [imageModalOpen]);
|
|
534
564
|
useEffect(() => {
|
|
535
565
|
if (editorRef.current && value && value !== lastSynchronizedHtmlRef.current) {
|
|
536
|
-
requestAnimationFrame(() =>
|
|
566
|
+
requestAnimationFrame(() => syncProcessedMediaRef.current(editorRef.current));
|
|
537
567
|
}
|
|
538
568
|
}, [value]);
|
|
539
|
-
|
|
540
|
-
// Runs whenever editable changes (toggles delete icon visibility)
|
|
541
569
|
useEffect(() => {
|
|
542
570
|
if (!editable) {
|
|
543
571
|
setEditorFocused(false);
|
|
572
|
+
clearMediaSelection();
|
|
544
573
|
}
|
|
545
|
-
|
|
574
|
+
syncProcessedMediaRef.current(editorRef.current);
|
|
546
575
|
}, [editable]);
|
|
547
576
|
useEffect(() => {
|
|
548
577
|
if (!editorRef.current) return;
|
|
@@ -573,7 +602,7 @@ function RichTextEditor({
|
|
|
573
602
|
if (editorRef.current && editorRef.current.innerHTML !== newContent) {
|
|
574
603
|
editorRef.current.innerHTML = newContent;
|
|
575
604
|
}
|
|
576
|
-
requestAnimationFrame(() =>
|
|
605
|
+
requestAnimationFrame(() => syncProcessedMediaRef.current(editorRef.current));
|
|
577
606
|
updateMetrics();
|
|
578
607
|
}
|
|
579
608
|
} catch (e) {
|
|
@@ -588,10 +617,47 @@ function RichTextEditor({
|
|
|
588
617
|
}
|
|
589
618
|
}
|
|
590
619
|
}, [value, initialEditable, updateMetrics]);
|
|
620
|
+
const LIST_BLOCK_MEDIA_SELECTOR = ".video-container, .image-container, table";
|
|
621
|
+
const isListItemEffectivelyEmpty = listItem => {
|
|
622
|
+
if (!listItem) return true;
|
|
623
|
+
const clone = listItem.cloneNode(true);
|
|
624
|
+
clone.querySelectorAll(LIST_BLOCK_MEDIA_SELECTOR).forEach(el => el.remove());
|
|
625
|
+
clone.querySelectorAll("br").forEach(el => el.remove());
|
|
626
|
+
return clone.textContent.replace(/[\u200B\u00A0\s]/g, "").length === 0;
|
|
627
|
+
};
|
|
628
|
+
const hoistBlockMediaOutOfListItems = container => {
|
|
629
|
+
if (!container) return false;
|
|
630
|
+
let changed = false;
|
|
631
|
+
container.querySelectorAll("ol, ul").forEach(list => {
|
|
632
|
+
const items = Array.from(list.children).filter(child => child.tagName === "LI");
|
|
633
|
+
items.forEach(listItem => {
|
|
634
|
+
const blockMedia = Array.from(listItem.querySelectorAll(LIST_BLOCK_MEDIA_SELECTOR));
|
|
635
|
+
if (blockMedia.length === 0) return;
|
|
636
|
+
const hadText = !isListItemEffectivelyEmpty(listItem);
|
|
637
|
+
blockMedia.forEach(media => {
|
|
638
|
+
listItem.removeChild(media);
|
|
639
|
+
if (list.parentNode) {
|
|
640
|
+
list.parentNode.insertBefore(media, list.nextSibling);
|
|
641
|
+
}
|
|
642
|
+
changed = true;
|
|
643
|
+
});
|
|
644
|
+
if (!hadText || isListItemEffectivelyEmpty(listItem)) {
|
|
645
|
+
listItem.remove();
|
|
646
|
+
changed = true;
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
if (list.children.length === 0 && list.parentNode) {
|
|
650
|
+
list.remove();
|
|
651
|
+
changed = true;
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
return changed;
|
|
655
|
+
};
|
|
591
656
|
const processExistingMedia = container => {
|
|
592
|
-
if (!container) return;
|
|
657
|
+
if (!container) return false;
|
|
593
658
|
processExistingImages(container);
|
|
594
659
|
processExistingVideos(container);
|
|
660
|
+
return hoistBlockMediaOutOfListItems(container);
|
|
595
661
|
};
|
|
596
662
|
const getCleanHtml = () => {
|
|
597
663
|
if (!editorRef.current) return "";
|
|
@@ -607,6 +673,11 @@ function RichTextEditor({
|
|
|
607
673
|
lastSynchronizedHtmlRef.current = next;
|
|
608
674
|
onChange && onChange(next);
|
|
609
675
|
}, [onChange]);
|
|
676
|
+
syncProcessedMediaRef.current = container => {
|
|
677
|
+
if (processExistingMedia(container)) {
|
|
678
|
+
triggerChange();
|
|
679
|
+
}
|
|
680
|
+
};
|
|
610
681
|
|
|
611
682
|
// Helper to walk up DOM to find style tags or CSS style:
|
|
612
683
|
const isParentStyle = (node, ...tagNames) => {
|
|
@@ -662,17 +733,15 @@ function RichTextEditor({
|
|
|
662
733
|
return rgbToHex(computedColor);
|
|
663
734
|
};
|
|
664
735
|
const stripEditorChrome = root => {
|
|
665
|
-
root.querySelectorAll(".image-delete-button, .video-delete-button, .video-edit-overlay, .media-resize-
|
|
736
|
+
root.querySelectorAll(".image-delete-button, .video-delete-button, .video-edit-overlay, .media-resize-handle").forEach(element => element.remove());
|
|
737
|
+
root.querySelectorAll(".rte-media-selected").forEach(element => {
|
|
738
|
+
element.classList.remove("rte-media-selected");
|
|
739
|
+
});
|
|
666
740
|
return root;
|
|
667
741
|
};
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
return
|
|
671
|
-
minWidth: 120,
|
|
672
|
-
minHeight: 80,
|
|
673
|
-
maxWidth,
|
|
674
|
-
maxHeight: 720
|
|
675
|
-
};
|
|
742
|
+
const getEditorInnerWidth = () => {
|
|
743
|
+
if (!editorRef.current) return 800;
|
|
744
|
+
return Math.max(editorRef.current.clientWidth - 24, 200);
|
|
676
745
|
};
|
|
677
746
|
const ensureImageMediaFrame = imageContainer => {
|
|
678
747
|
if (!imageContainer) return null;
|
|
@@ -680,160 +749,164 @@ function RichTextEditor({
|
|
|
680
749
|
if (frame) return frame;
|
|
681
750
|
frame = document.createElement("div");
|
|
682
751
|
frame.className = "image-media-frame";
|
|
683
|
-
["width", "height", "marginLeft", "marginTop", "maxWidth"].forEach(prop => {
|
|
684
|
-
if (imageContainer.style[prop]) {
|
|
685
|
-
frame.style[prop] = imageContainer.style[prop];
|
|
686
|
-
imageContainer.style[prop] = "";
|
|
687
|
-
}
|
|
688
|
-
});
|
|
689
|
-
if (imageContainer.dataset.explicitHeight) {
|
|
690
|
-
frame.dataset.explicitHeight = imageContainer.dataset.explicitHeight;
|
|
691
|
-
delete imageContainer.dataset.explicitHeight;
|
|
692
|
-
}
|
|
693
752
|
const children = Array.from(imageContainer.children);
|
|
694
753
|
imageContainer.appendChild(frame);
|
|
695
754
|
children.forEach(child => frame.appendChild(child));
|
|
696
755
|
return frame;
|
|
697
756
|
};
|
|
698
757
|
const getImageMediaTarget = imageContainer => ensureImageMediaFrame(imageContainer) || imageContainer;
|
|
699
|
-
const
|
|
758
|
+
const getMediaWidthPercent = container => {
|
|
759
|
+
if (!container) return 100;
|
|
760
|
+
if (container.dataset.widthPercent) {
|
|
761
|
+
return Number(container.dataset.widthPercent);
|
|
762
|
+
}
|
|
763
|
+
const width = container.style.width || "";
|
|
764
|
+
if (width.endsWith("%")) {
|
|
765
|
+
return parseInt(width, 10) || 100;
|
|
766
|
+
}
|
|
767
|
+
const editorWidth = getEditorInnerWidth();
|
|
768
|
+
const rect = container.getBoundingClientRect();
|
|
769
|
+
if (editorWidth > 0 && rect.width > 0) {
|
|
770
|
+
return Math.round(rect.width / editorWidth * 100);
|
|
771
|
+
}
|
|
772
|
+
return 100;
|
|
773
|
+
};
|
|
774
|
+
const applyMediaWidthPercent = (container, percent) => {
|
|
775
|
+
if (!container) return;
|
|
776
|
+
const clamped = Math.max(25, Math.min(100, Math.round(percent)));
|
|
777
|
+
container.dataset.widthPercent = String(clamped);
|
|
778
|
+
container.style.width = `${clamped}%`;
|
|
779
|
+
container.style.maxWidth = "100%";
|
|
780
|
+
container.style.marginLeft = "";
|
|
781
|
+
container.style.marginTop = "";
|
|
782
|
+
if (container.classList.contains("video-container")) {
|
|
783
|
+
container.style.height = "0";
|
|
784
|
+
container.style.paddingBottom = "56.25%";
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
const frame = getImageMediaTarget(container);
|
|
788
|
+
if (!frame) return;
|
|
789
|
+
frame.style.width = "100%";
|
|
790
|
+
frame.style.height = "";
|
|
700
791
|
const img = frame.querySelector("img");
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
792
|
+
if (img) {
|
|
793
|
+
img.style.height = "auto";
|
|
794
|
+
img.style.objectFit = "";
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
const normalizeMediaWidth = container => {
|
|
798
|
+
if (!container) return;
|
|
799
|
+
if (container.classList.contains("image-small")) {
|
|
800
|
+
container.classList.remove("image-small");
|
|
801
|
+
applyMediaWidthPercent(container, 50);
|
|
712
802
|
return;
|
|
713
803
|
}
|
|
714
|
-
if (
|
|
715
|
-
|
|
804
|
+
if (container.dataset.widthPercent) {
|
|
805
|
+
applyMediaWidthPercent(container, Number(container.dataset.widthPercent));
|
|
806
|
+
return;
|
|
716
807
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
808
|
+
const width = container.style.width || "";
|
|
809
|
+
if (width.endsWith("%")) {
|
|
810
|
+
applyMediaWidthPercent(container, parseInt(width, 10) || 100);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
if (width.endsWith("px")) {
|
|
814
|
+
const editorWidth = getEditorInnerWidth();
|
|
815
|
+
const px = parseFloat(width);
|
|
816
|
+
if (editorWidth > 0 && px > 0) {
|
|
817
|
+
applyMediaWidthPercent(container, Math.round(px / editorWidth * 100));
|
|
725
818
|
}
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
if (container.classList.contains("video-container")) {
|
|
822
|
+
applyMediaWidthPercent(container, 100);
|
|
726
823
|
}
|
|
727
824
|
};
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
825
|
+
const clearMediaSelection = () => {
|
|
826
|
+
var _editorRef$current;
|
|
827
|
+
(_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 || _editorRef$current.querySelectorAll(".rte-media-selected").forEach(element => {
|
|
828
|
+
element.classList.remove("rte-media-selected");
|
|
829
|
+
});
|
|
830
|
+
setSelectedMedia(null);
|
|
831
|
+
};
|
|
832
|
+
const selectMediaContainer = container => {
|
|
833
|
+
var _editorRef$current2;
|
|
834
|
+
if (!container || !((_editorRef$current2 = editorRef.current) !== null && _editorRef$current2 !== void 0 && _editorRef$current2.contains(container))) return;
|
|
835
|
+
editorRef.current.querySelectorAll(".rte-media-selected").forEach(element => {
|
|
836
|
+
element.classList.remove("rte-media-selected");
|
|
837
|
+
});
|
|
838
|
+
container.classList.add("rte-media-selected");
|
|
839
|
+
setSelectedMedia(container);
|
|
733
840
|
};
|
|
734
841
|
const attachMediaResizeHandle = container => {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
const resizeTarget =
|
|
842
|
+
var _resizeTarget$querySe;
|
|
843
|
+
if (!container) return;
|
|
844
|
+
const resizeTarget = container.classList.contains("video-container") ? container : getImageMediaTarget(container);
|
|
738
845
|
if (!resizeTarget) return;
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
handle.addEventListener("mousedown", event => {
|
|
765
|
-
if (!editable) return;
|
|
766
|
-
event.preventDefault();
|
|
767
|
-
event.stopPropagation();
|
|
768
|
-
const limits = getMediaSizeLimits();
|
|
769
|
-
const rect = resizeTarget.getBoundingClientRect();
|
|
770
|
-
const startX = event.clientX;
|
|
771
|
-
const startY = event.clientY;
|
|
772
|
-
const startWidth = rect.width;
|
|
773
|
-
const startHeight = rect.height;
|
|
774
|
-
const startMarginLeft = Number.parseFloat(resizeTarget.style.marginLeft) || 0;
|
|
775
|
-
const startMarginTop = Number.parseFloat(resizeTarget.style.marginTop) || 0;
|
|
776
|
-
if (isVideo) {
|
|
777
|
-
resizeTarget.style.paddingBottom = "0";
|
|
778
|
-
}
|
|
779
|
-
const onMouseMove = moveEvent => {
|
|
780
|
-
const deltaX = moveEvent.clientX - startX;
|
|
781
|
-
const deltaY = moveEvent.clientY - startY;
|
|
782
|
-
let nextWidth = startWidth;
|
|
783
|
-
let nextHeight = startHeight;
|
|
784
|
-
if (edge === "right") {
|
|
785
|
-
nextWidth = startWidth + deltaX;
|
|
786
|
-
} else if (edge === "left") {
|
|
787
|
-
nextWidth = startWidth - deltaX;
|
|
788
|
-
} else if (edge === "bottom") {
|
|
789
|
-
nextHeight = startHeight + deltaY;
|
|
790
|
-
} else if (edge === "top") {
|
|
791
|
-
nextHeight = startHeight - deltaY;
|
|
792
|
-
}
|
|
793
|
-
nextWidth = Math.max(limits.minWidth, Math.min(nextWidth, limits.maxWidth));
|
|
794
|
-
nextHeight = Math.max(limits.minHeight, Math.min(nextHeight, limits.maxHeight));
|
|
795
|
-
if (edge === "left") {
|
|
796
|
-
resizeTarget.style.marginLeft = `${Math.round(startMarginLeft + (startWidth - nextWidth))}px`;
|
|
797
|
-
}
|
|
798
|
-
if (edge === "top") {
|
|
799
|
-
resizeTarget.style.marginTop = `${Math.round(startMarginTop + (startHeight - nextHeight))}px`;
|
|
800
|
-
}
|
|
801
|
-
if (isVideo) {
|
|
802
|
-
applyVideoMediaSize(resizeTarget, nextWidth, nextHeight);
|
|
803
|
-
} else {
|
|
804
|
-
applyImageMediaSize(resizeTarget, nextWidth, nextHeight, edge);
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
const onMouseUp = () => {
|
|
808
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
809
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
810
|
-
triggerChange();
|
|
811
|
-
};
|
|
812
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
813
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
814
|
-
});
|
|
815
|
-
handlesWrapper.appendChild(handle);
|
|
846
|
+
(_resizeTarget$querySe = resizeTarget.querySelector(".media-resize-handle")) === null || _resizeTarget$querySe === void 0 || _resizeTarget$querySe.remove();
|
|
847
|
+
const handle = document.createElement("div");
|
|
848
|
+
handle.className = "media-resize-handle";
|
|
849
|
+
handle.title = "Drag to resize";
|
|
850
|
+
handle.setAttribute("contenteditable", "false");
|
|
851
|
+
handle.addEventListener("mousedown", event => {
|
|
852
|
+
if (!editable) return;
|
|
853
|
+
event.preventDefault();
|
|
854
|
+
event.stopPropagation();
|
|
855
|
+
selectMediaContainer(container);
|
|
856
|
+
const editorWidth = getEditorInnerWidth();
|
|
857
|
+
const startX = event.clientX;
|
|
858
|
+
const startWidth = container.getBoundingClientRect().width;
|
|
859
|
+
const onMouseMove = moveEvent => {
|
|
860
|
+
const nextWidth = Math.max(60, startWidth + (moveEvent.clientX - startX));
|
|
861
|
+
const percent = Math.round(nextWidth / editorWidth * 100);
|
|
862
|
+
applyMediaWidthPercent(container, percent);
|
|
863
|
+
};
|
|
864
|
+
const onMouseUp = () => {
|
|
865
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
866
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
867
|
+
triggerChange();
|
|
868
|
+
};
|
|
869
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
870
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
816
871
|
});
|
|
817
|
-
resizeTarget.appendChild(
|
|
872
|
+
resizeTarget.appendChild(handle);
|
|
818
873
|
};
|
|
819
874
|
const handleEditorFocus = () => {
|
|
820
875
|
setEditorFocused(true);
|
|
821
876
|
};
|
|
822
877
|
const handleEditorBlur = () => {
|
|
823
878
|
requestAnimationFrame(() => {
|
|
824
|
-
var _editorRef$
|
|
825
|
-
if (!((_editorRef$
|
|
879
|
+
var _editorRef$current3;
|
|
880
|
+
if (!((_editorRef$current3 = editorRef.current) !== null && _editorRef$current3 !== void 0 && _editorRef$current3.contains(document.activeElement))) {
|
|
826
881
|
setEditorFocused(false);
|
|
827
882
|
}
|
|
828
883
|
});
|
|
829
884
|
};
|
|
830
885
|
const updateMediaControlVisibility = container => {
|
|
831
|
-
const
|
|
832
|
-
if (
|
|
833
|
-
|
|
834
|
-
|
|
886
|
+
const handle = container.querySelector(".media-resize-handle");
|
|
887
|
+
if (handle instanceof HTMLElement) {
|
|
888
|
+
handle.style.display = editable ? "block" : "none";
|
|
889
|
+
handle.style.pointerEvents = editable ? "auto" : "none";
|
|
835
890
|
}
|
|
836
891
|
};
|
|
892
|
+
const BLOCK_TAGS = ["P", "DIV", "H1", "H2", "H3", "BLOCKQUOTE", "LI"];
|
|
893
|
+
const getActiveBlock = node => {
|
|
894
|
+
if (!editorRef.current || !node) return null;
|
|
895
|
+
let current = node.nodeType === 3 ? node.parentNode : node;
|
|
896
|
+
while (current && current !== editorRef.current) {
|
|
897
|
+
if (current.nodeType === 1 && BLOCK_TAGS.includes(current.tagName)) {
|
|
898
|
+
return current;
|
|
899
|
+
}
|
|
900
|
+
current = current.parentNode;
|
|
901
|
+
}
|
|
902
|
+
return editorRef.current;
|
|
903
|
+
};
|
|
904
|
+
const getBlockFormat = node => {
|
|
905
|
+
const block = getActiveBlock(node);
|
|
906
|
+
if (!block || block === editorRef.current) return "div";
|
|
907
|
+
const tag = block.tagName.toLowerCase();
|
|
908
|
+
return tag === "p" || tag === "li" ? "div" : tag;
|
|
909
|
+
};
|
|
837
910
|
const createMediaDeleteButton = (title, className, onRemove) => {
|
|
838
911
|
const deleteBtn = document.createElement("button");
|
|
839
912
|
deleteBtn.type = "button";
|
|
@@ -852,10 +925,10 @@ function RichTextEditor({
|
|
|
852
925
|
// Listen for selection changes globally to update styles and list type in one pass
|
|
853
926
|
useEffect(() => {
|
|
854
927
|
const handleGlobalSelectionSync = () => {
|
|
855
|
-
var _editorRef$
|
|
928
|
+
var _editorRef$current4;
|
|
856
929
|
// Only sync if the editor has focus
|
|
857
930
|
const sel = window.getSelection();
|
|
858
|
-
if (!sel || !sel.rangeCount || !((_editorRef$
|
|
931
|
+
if (!sel || !sel.rangeCount || !((_editorRef$current4 = editorRef.current) !== null && _editorRef$current4 !== void 0 && _editorRef$current4.contains(sel.anchorNode))) {
|
|
859
932
|
return;
|
|
860
933
|
}
|
|
861
934
|
|
|
@@ -903,6 +976,7 @@ function RichTextEditor({
|
|
|
903
976
|
} else {
|
|
904
977
|
setCurrentFontSize("16");
|
|
905
978
|
}
|
|
979
|
+
setCurrentBlockFormat(getBlockFormat(sel.anchorNode));
|
|
906
980
|
};
|
|
907
981
|
document.addEventListener("selectionchange", handleGlobalSelectionSync);
|
|
908
982
|
return () => {
|
|
@@ -1126,8 +1200,10 @@ function RichTextEditor({
|
|
|
1126
1200
|
}
|
|
1127
1201
|
setVideoModalOpen(false);
|
|
1128
1202
|
setVideoUrl("");
|
|
1129
|
-
|
|
1130
|
-
|
|
1203
|
+
requestAnimationFrame(() => {
|
|
1204
|
+
processExistingMedia(editorRef.current);
|
|
1205
|
+
triggerChange();
|
|
1206
|
+
});
|
|
1131
1207
|
} else {
|
|
1132
1208
|
console.warn("Invalid Video URL or Platform not supported");
|
|
1133
1209
|
}
|
|
@@ -1138,11 +1214,25 @@ function RichTextEditor({
|
|
|
1138
1214
|
if (!videoContainer.querySelector(".video-delete-button")) {
|
|
1139
1215
|
const deleteBtn = createMediaDeleteButton("Remove video", "video-delete-button image-delete-button", () => {
|
|
1140
1216
|
videoContainer.remove();
|
|
1217
|
+
clearMediaSelection();
|
|
1141
1218
|
triggerChange && triggerChange();
|
|
1142
1219
|
});
|
|
1143
1220
|
videoContainer.appendChild(deleteBtn);
|
|
1144
1221
|
}
|
|
1222
|
+
if (!videoContainer.dataset.mediaEnhanced) {
|
|
1223
|
+
videoContainer.dataset.mediaEnhanced = "true";
|
|
1224
|
+
if (!videoContainer.classList.contains("image-align-left") && !videoContainer.classList.contains("image-align-center") && !videoContainer.classList.contains("image-align-right")) {
|
|
1225
|
+
videoContainer.classList.add("image-align-left");
|
|
1226
|
+
}
|
|
1227
|
+
videoContainer.addEventListener("click", event => {
|
|
1228
|
+
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1229
|
+
event.preventDefault();
|
|
1230
|
+
event.stopPropagation();
|
|
1231
|
+
selectMediaContainer(videoContainer);
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1145
1234
|
attachMediaResizeHandle(videoContainer);
|
|
1235
|
+
normalizeMediaWidth(videoContainer);
|
|
1146
1236
|
updateMediaControlVisibility(videoContainer);
|
|
1147
1237
|
});
|
|
1148
1238
|
};
|
|
@@ -1162,6 +1252,7 @@ function RichTextEditor({
|
|
|
1162
1252
|
}));
|
|
1163
1253
|
}
|
|
1164
1254
|
attachMediaResizeHandle(existingWrapper);
|
|
1255
|
+
normalizeMediaWidth(existingWrapper);
|
|
1165
1256
|
updateMediaControlVisibility(existingWrapper);
|
|
1166
1257
|
return;
|
|
1167
1258
|
}
|
|
@@ -1171,24 +1262,21 @@ function RichTextEditor({
|
|
|
1171
1262
|
wrapper.style.cursor = editable ? "pointer" : "default";
|
|
1172
1263
|
const frame = document.createElement("div");
|
|
1173
1264
|
frame.className = "image-media-frame";
|
|
1174
|
-
if (img.getAttribute("width") && !frame.style.width) {
|
|
1175
|
-
frame.style.width = `${img.getAttribute("width")}px`;
|
|
1176
|
-
} else if (img.style.width && img.style.width.endsWith("px")) {
|
|
1177
|
-
frame.style.width = img.style.width;
|
|
1178
|
-
}
|
|
1179
1265
|
img.classList.add("rte-image");
|
|
1180
1266
|
img.setAttribute("data-align", align);
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
img.style.height = frame.dataset.explicitHeight ? "100%" : "auto";
|
|
1184
|
-
} else {
|
|
1185
|
-
img.style.width = "";
|
|
1186
|
-
img.style.height = "auto";
|
|
1187
|
-
}
|
|
1188
|
-
img.addEventListener("click", event => {
|
|
1267
|
+
img.style.height = "auto";
|
|
1268
|
+
img.addEventListener("dblclick", event => {
|
|
1189
1269
|
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1270
|
+
event.preventDefault();
|
|
1271
|
+
event.stopPropagation();
|
|
1190
1272
|
openImageModal(img.src);
|
|
1191
1273
|
});
|
|
1274
|
+
img.addEventListener("click", event => {
|
|
1275
|
+
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1276
|
+
event.preventDefault();
|
|
1277
|
+
event.stopPropagation();
|
|
1278
|
+
selectMediaContainer(wrapper);
|
|
1279
|
+
});
|
|
1192
1280
|
const deleteBtn = createMediaDeleteButton("Remove image", "image-delete-button", () => {
|
|
1193
1281
|
wrapper.remove();
|
|
1194
1282
|
triggerChange && triggerChange();
|
|
@@ -1203,6 +1291,7 @@ function RichTextEditor({
|
|
|
1203
1291
|
frame.appendChild(deleteBtn);
|
|
1204
1292
|
wrapper.appendChild(frame);
|
|
1205
1293
|
attachMediaResizeHandle(wrapper);
|
|
1294
|
+
normalizeMediaWidth(wrapper);
|
|
1206
1295
|
if (nextSibling) {
|
|
1207
1296
|
parentNode.insertBefore(wrapper, nextSibling);
|
|
1208
1297
|
} else {
|
|
@@ -1229,9 +1318,19 @@ function RichTextEditor({
|
|
|
1229
1318
|
const img = document.createElement('img');
|
|
1230
1319
|
img.src = dataUrl;
|
|
1231
1320
|
img.alt = fileName || "image";
|
|
1232
|
-
img.addEventListener("
|
|
1321
|
+
img.addEventListener("dblclick", event => {
|
|
1322
|
+
event.preventDefault();
|
|
1323
|
+
event.stopPropagation();
|
|
1324
|
+
openImageModal(dataUrl);
|
|
1325
|
+
});
|
|
1326
|
+
img.addEventListener("click", event => {
|
|
1327
|
+
event.preventDefault();
|
|
1328
|
+
event.stopPropagation();
|
|
1329
|
+
selectMediaContainer(container);
|
|
1330
|
+
});
|
|
1233
1331
|
frame.appendChild(img);
|
|
1234
1332
|
container.appendChild(frame);
|
|
1333
|
+
applyMediaWidthPercent(container, 25);
|
|
1235
1334
|
|
|
1236
1335
|
// Insert at cursor position
|
|
1237
1336
|
insertNodeAtCursor(container);
|
|
@@ -1374,6 +1473,12 @@ function RichTextEditor({
|
|
|
1374
1473
|
txt.innerHTML = html;
|
|
1375
1474
|
return txt.value;
|
|
1376
1475
|
};
|
|
1476
|
+
const isCursorAtStartOfListItem = (range, listItem) => {
|
|
1477
|
+
const prefixRange = document.createRange();
|
|
1478
|
+
prefixRange.setStart(listItem, 0);
|
|
1479
|
+
prefixRange.setEnd(range.startContainer, range.startOffset);
|
|
1480
|
+
return prefixRange.toString().replace(/[\u200B\u00A0\s]/g, "").length === 0;
|
|
1481
|
+
};
|
|
1377
1482
|
const isCursorAtEndOfListItem = (range, listItem) => {
|
|
1378
1483
|
const suffixRange = document.createRange();
|
|
1379
1484
|
suffixRange.setStart(range.startContainer, range.startOffset);
|
|
@@ -1398,6 +1503,8 @@ function RichTextEditor({
|
|
|
1398
1503
|
selection.addRange(newRange);
|
|
1399
1504
|
};
|
|
1400
1505
|
const handleKeyDown = useCallback(e => {
|
|
1506
|
+
if (applyMarkdownShortcut(e)) return;
|
|
1507
|
+
|
|
1401
1508
|
// Handle Enter key
|
|
1402
1509
|
if (e.key === 'Enter') {
|
|
1403
1510
|
e.preventDefault();
|
|
@@ -1418,7 +1525,7 @@ function RichTextEditor({
|
|
|
1418
1525
|
// If we're at the end of a list item, add a new one
|
|
1419
1526
|
if (range.collapsed && isCursorAtEndOfListItem(range, listItem)) {
|
|
1420
1527
|
// If it's empty, create a regular paragraph instead
|
|
1421
|
-
if (listItem
|
|
1528
|
+
if (isListItemEffectivelyEmpty(listItem)) {
|
|
1422
1529
|
document.execCommand('insertHTML', false, '<div><br></div>');
|
|
1423
1530
|
// Move the cursor to the new line
|
|
1424
1531
|
const newRange = document.createRange();
|
|
@@ -1450,7 +1557,7 @@ function RichTextEditor({
|
|
|
1450
1557
|
} else {
|
|
1451
1558
|
list.appendChild(newItem);
|
|
1452
1559
|
}
|
|
1453
|
-
if (
|
|
1560
|
+
if (isListItemEffectivelyEmpty(newItem)) {
|
|
1454
1561
|
newItem.textContent = "";
|
|
1455
1562
|
prepareListItemForTyping(newItem, selection);
|
|
1456
1563
|
} else {
|
|
@@ -1468,6 +1575,54 @@ function RichTextEditor({
|
|
|
1468
1575
|
triggerChange();
|
|
1469
1576
|
return;
|
|
1470
1577
|
}
|
|
1578
|
+
if (e.key === "Backspace") {
|
|
1579
|
+
var _node$closest, _node, _editorRef$current5;
|
|
1580
|
+
const selection = window.getSelection();
|
|
1581
|
+
if (!(selection !== null && selection !== void 0 && selection.rangeCount)) return;
|
|
1582
|
+
const range = selection.getRangeAt(0);
|
|
1583
|
+
if (!range.collapsed) return;
|
|
1584
|
+
let node = range.startContainer;
|
|
1585
|
+
if (node.nodeType === 3) {
|
|
1586
|
+
node = node.parentNode;
|
|
1587
|
+
}
|
|
1588
|
+
const listItem = (_node$closest = (_node = node).closest) === null || _node$closest === void 0 ? void 0 : _node$closest.call(_node, "li");
|
|
1589
|
+
if (!listItem || !((_editorRef$current5 = editorRef.current) !== null && _editorRef$current5 !== void 0 && _editorRef$current5.contains(listItem))) return;
|
|
1590
|
+
const list = listItem.parentNode;
|
|
1591
|
+
if (isListItemEffectivelyEmpty(listItem)) {
|
|
1592
|
+
e.preventDefault();
|
|
1593
|
+
const prevLi = listItem.previousElementSibling;
|
|
1594
|
+
const blockMedia = Array.from(listItem.querySelectorAll(LIST_BLOCK_MEDIA_SELECTOR));
|
|
1595
|
+
blockMedia.forEach(media => {
|
|
1596
|
+
var _list$parentNode;
|
|
1597
|
+
(_list$parentNode = list.parentNode) === null || _list$parentNode === void 0 || _list$parentNode.insertBefore(media, list.nextSibling);
|
|
1598
|
+
});
|
|
1599
|
+
listItem.remove();
|
|
1600
|
+
if (list.children.length === 0) {
|
|
1601
|
+
list.remove();
|
|
1602
|
+
}
|
|
1603
|
+
if ((prevLi === null || prevLi === void 0 ? void 0 : prevLi.tagName) === "LI") {
|
|
1604
|
+
const newRange = document.createRange();
|
|
1605
|
+
newRange.selectNodeContents(prevLi);
|
|
1606
|
+
newRange.collapse(false);
|
|
1607
|
+
selection.removeAllRanges();
|
|
1608
|
+
selection.addRange(newRange);
|
|
1609
|
+
}
|
|
1610
|
+
triggerChange();
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
if (isCursorAtStartOfListItem(range, listItem)) {
|
|
1614
|
+
const prevLi = listItem.previousElementSibling;
|
|
1615
|
+
if ((prevLi === null || prevLi === void 0 ? void 0 : prevLi.tagName) === "LI" && isListItemEffectivelyEmpty(prevLi)) {
|
|
1616
|
+
e.preventDefault();
|
|
1617
|
+
prevLi.remove();
|
|
1618
|
+
if (list.children.length === 0) {
|
|
1619
|
+
list.remove();
|
|
1620
|
+
}
|
|
1621
|
+
triggerChange();
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1471
1626
|
|
|
1472
1627
|
// Handle Ctrl/Cmd + B/I/U for bold/italic/underline
|
|
1473
1628
|
if ((e.ctrlKey || e.metaKey) && e.key === "b") {
|
|
@@ -1508,6 +1663,65 @@ function RichTextEditor({
|
|
|
1508
1663
|
const handleSelect = type => {
|
|
1509
1664
|
exec(type === "unordered" ? "insertUnorderedList" : "insertOrderedList");
|
|
1510
1665
|
};
|
|
1666
|
+
const applyBlockFormat = format => {
|
|
1667
|
+
document.execCommand("formatBlock", false, format);
|
|
1668
|
+
setCurrentBlockFormat(format);
|
|
1669
|
+
triggerChange();
|
|
1670
|
+
focus();
|
|
1671
|
+
};
|
|
1672
|
+
const clearFormatting = () => {
|
|
1673
|
+
document.execCommand("removeFormat", false, null);
|
|
1674
|
+
document.execCommand("unlink", false, null);
|
|
1675
|
+
document.execCommand("formatBlock", false, "div");
|
|
1676
|
+
setCurrentBlockFormat("div");
|
|
1677
|
+
setCurrentFontSize("16");
|
|
1678
|
+
setCurrentLineHeight("");
|
|
1679
|
+
setFontColor("#000000");
|
|
1680
|
+
triggerChange();
|
|
1681
|
+
focus();
|
|
1682
|
+
};
|
|
1683
|
+
const deleteTextBeforeCursorInBlock = (block, range, selection) => {
|
|
1684
|
+
const prefixRange = document.createRange();
|
|
1685
|
+
prefixRange.setStart(block, 0);
|
|
1686
|
+
prefixRange.setEnd(range.startContainer, range.startOffset);
|
|
1687
|
+
prefixRange.deleteContents();
|
|
1688
|
+
const nextRange = document.createRange();
|
|
1689
|
+
nextRange.setStart(block, 0);
|
|
1690
|
+
nextRange.collapse(true);
|
|
1691
|
+
selection.removeAllRanges();
|
|
1692
|
+
selection.addRange(nextRange);
|
|
1693
|
+
};
|
|
1694
|
+
const applyMarkdownShortcut = event => {
|
|
1695
|
+
if (event.key !== " " || event.ctrlKey || event.metaKey || event.altKey) {
|
|
1696
|
+
return false;
|
|
1697
|
+
}
|
|
1698
|
+
const selection = window.getSelection();
|
|
1699
|
+
if (!(selection !== null && selection !== void 0 && selection.rangeCount) || !selection.isCollapsed || !editorRef.current) {
|
|
1700
|
+
return false;
|
|
1701
|
+
}
|
|
1702
|
+
const range = selection.getRangeAt(0);
|
|
1703
|
+
const block = getActiveBlock(range.startContainer);
|
|
1704
|
+
if (!block || !editorRef.current.contains(block)) return false;
|
|
1705
|
+
const prefixRange = document.createRange();
|
|
1706
|
+
prefixRange.setStart(block, 0);
|
|
1707
|
+
prefixRange.setEnd(range.startContainer, range.startOffset);
|
|
1708
|
+
const textBeforeCursor = prefixRange.toString().replace(/\u00A0/g, " ").trim();
|
|
1709
|
+
const shortcuts = {
|
|
1710
|
+
"#": () => applyBlockFormat("h1"),
|
|
1711
|
+
"##": () => applyBlockFormat("h2"),
|
|
1712
|
+
"###": () => applyBlockFormat("h3"),
|
|
1713
|
+
">": () => applyBlockFormat("blockquote"),
|
|
1714
|
+
"-": () => handleSelect("unordered"),
|
|
1715
|
+
"*": () => handleSelect("unordered"),
|
|
1716
|
+
"1.": () => handleSelect("ordered")
|
|
1717
|
+
};
|
|
1718
|
+
const action = shortcuts[textBeforeCursor];
|
|
1719
|
+
if (!action) return false;
|
|
1720
|
+
event.preventDefault();
|
|
1721
|
+
deleteTextBeforeCursorInBlock(block, range, selection);
|
|
1722
|
+
action();
|
|
1723
|
+
return true;
|
|
1724
|
+
};
|
|
1511
1725
|
const onLineHeightChange = value => {
|
|
1512
1726
|
if (!value) return;
|
|
1513
1727
|
const sel = window.getSelection();
|
|
@@ -1689,6 +1903,7 @@ function RichTextEditor({
|
|
|
1689
1903
|
}
|
|
1690
1904
|
}, [disabled]);
|
|
1691
1905
|
const handleEditorClick = useCallback(e => {
|
|
1906
|
+
var _editorRef$current6;
|
|
1692
1907
|
setSelectionVersion(v => v + 1);
|
|
1693
1908
|
const deleteBtn = e.target.closest('button[title="Remove image"], button[title="Remove video"]');
|
|
1694
1909
|
if (deleteBtn && editable && editorFocused) {
|
|
@@ -1697,10 +1912,15 @@ function RichTextEditor({
|
|
|
1697
1912
|
const wrapper = deleteBtn.closest('.image-container, .video-container');
|
|
1698
1913
|
if (wrapper) {
|
|
1699
1914
|
wrapper.remove();
|
|
1915
|
+
clearMediaSelection();
|
|
1700
1916
|
triggerChange();
|
|
1701
1917
|
}
|
|
1702
1918
|
return;
|
|
1703
1919
|
}
|
|
1920
|
+
const clickedMedia = e.target.closest('.image-container, .video-container');
|
|
1921
|
+
if (clickedMedia && (_editorRef$current6 = editorRef.current) !== null && _editorRef$current6 !== void 0 && _editorRef$current6.contains(clickedMedia) && editable) {
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1704
1924
|
|
|
1705
1925
|
// Check if the click is on a link
|
|
1706
1926
|
const clickedLink = e.target.closest('a');
|
|
@@ -1710,13 +1930,8 @@ function RichTextEditor({
|
|
|
1710
1930
|
window.open(clickedLink.href, '_blank');
|
|
1711
1931
|
return;
|
|
1712
1932
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
const clickedImg = e.target.closest('img');
|
|
1716
|
-
if (clickedImg && !clickedImg.closest('.rte-modal')) {
|
|
1717
|
-
setSelectedImage(clickedImg);
|
|
1718
|
-
} else if (!e.target.closest('.rte-image-toolbar')) {
|
|
1719
|
-
setSelectedImage(null);
|
|
1933
|
+
if (!e.target.closest('.rte-media-toolbar')) {
|
|
1934
|
+
clearMediaSelection();
|
|
1720
1935
|
}
|
|
1721
1936
|
|
|
1722
1937
|
// If disabled is true, prevent editing
|
|
@@ -1735,73 +1950,72 @@ function RichTextEditor({
|
|
|
1735
1950
|
}, 0);
|
|
1736
1951
|
}
|
|
1737
1952
|
}, [editable, disabled, editorFocused, triggerChange]);
|
|
1738
|
-
const
|
|
1739
|
-
if (!
|
|
1953
|
+
const renderMediaToolbar = () => {
|
|
1954
|
+
if (!selectedMedia || !editorRef.current || !editable) return null;
|
|
1740
1955
|
const editorRect = editorRef.current.getBoundingClientRect();
|
|
1741
|
-
const
|
|
1742
|
-
const top =
|
|
1743
|
-
const left =
|
|
1744
|
-
const width =
|
|
1956
|
+
const mediaRect = selectedMedia.getBoundingClientRect();
|
|
1957
|
+
const top = mediaRect.top - editorRect.top + editorRef.current.scrollTop;
|
|
1958
|
+
const left = mediaRect.left - editorRect.left + editorRef.current.scrollLeft;
|
|
1959
|
+
const width = mediaRect.width;
|
|
1960
|
+
const currentPercent = getMediaWidthPercent(selectedMedia);
|
|
1961
|
+
const widthPresets = [25, 50, 75, 100];
|
|
1745
1962
|
const handleAlignment = align => {
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
wrapper.classList.add(`image-align-${align}`);
|
|
1752
|
-
selectedImage.setAttribute('data-align', align);
|
|
1753
|
-
triggerChange();
|
|
1754
|
-
}
|
|
1963
|
+
selectedMedia.classList.remove("image-align-left", "image-align-center", "image-align-right");
|
|
1964
|
+
selectedMedia.classList.add(`image-align-${align}`);
|
|
1965
|
+
const img = selectedMedia.querySelector("img");
|
|
1966
|
+
if (img) img.setAttribute("data-align", align);
|
|
1967
|
+
triggerChange();
|
|
1755
1968
|
};
|
|
1756
|
-
const
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
wrapper.remove();
|
|
1760
|
-
setSelectedImage(null);
|
|
1761
|
-
triggerChange();
|
|
1762
|
-
}
|
|
1969
|
+
const setWidth = percent => {
|
|
1970
|
+
applyMediaWidthPercent(selectedMedia, percent);
|
|
1971
|
+
triggerChange();
|
|
1763
1972
|
};
|
|
1764
|
-
const
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
}
|
|
1773
|
-
triggerChange();
|
|
1973
|
+
const removeMedia = () => {
|
|
1974
|
+
selectedMedia.remove();
|
|
1975
|
+
clearMediaSelection();
|
|
1976
|
+
triggerChange();
|
|
1977
|
+
};
|
|
1978
|
+
const isActivePercent = percent => {
|
|
1979
|
+
if (!selectedMedia.dataset.widthPercent && !(selectedMedia.style.width || "").endsWith("%")) {
|
|
1980
|
+
return false;
|
|
1774
1981
|
}
|
|
1982
|
+
return Math.abs(currentPercent - percent) <= 3;
|
|
1775
1983
|
};
|
|
1776
1984
|
return /*#__PURE__*/React.createElement("div", {
|
|
1777
|
-
className: "rte-
|
|
1985
|
+
className: "rte-media-toolbar",
|
|
1778
1986
|
style: {
|
|
1779
|
-
position:
|
|
1780
|
-
top: Math.max(0, top -
|
|
1781
|
-
left: Math.max(
|
|
1987
|
+
position: "absolute",
|
|
1988
|
+
top: Math.max(0, top - 44),
|
|
1989
|
+
left: Math.max(8, left + width / 2 - 156),
|
|
1782
1990
|
zIndex: 1000
|
|
1783
1991
|
}
|
|
1784
1992
|
}, /*#__PURE__*/React.createElement("button", {
|
|
1785
1993
|
type: "button",
|
|
1786
|
-
onClick: () => handleAlignment(
|
|
1994
|
+
onClick: () => handleAlignment("left"),
|
|
1787
1995
|
title: "Align Left"
|
|
1788
1996
|
}, "L"), /*#__PURE__*/React.createElement("button", {
|
|
1789
1997
|
type: "button",
|
|
1790
|
-
onClick: () => handleAlignment(
|
|
1998
|
+
onClick: () => handleAlignment("center"),
|
|
1791
1999
|
title: "Align Center"
|
|
1792
2000
|
}, "C"), /*#__PURE__*/React.createElement("button", {
|
|
1793
2001
|
type: "button",
|
|
1794
|
-
onClick: () => handleAlignment(
|
|
2002
|
+
onClick: () => handleAlignment("right"),
|
|
1795
2003
|
title: "Align Right"
|
|
1796
|
-
}, "R"), /*#__PURE__*/React.createElement("
|
|
2004
|
+
}, "R"), /*#__PURE__*/React.createElement("span", {
|
|
2005
|
+
className: "rte-media-toolbar-divider"
|
|
2006
|
+
}), widthPresets.map(percent => /*#__PURE__*/React.createElement("button", {
|
|
2007
|
+
key: percent,
|
|
1797
2008
|
type: "button",
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
2009
|
+
className: isActivePercent(percent) ? "active" : "",
|
|
2010
|
+
onClick: () => setWidth(percent),
|
|
2011
|
+
title: `${percent}% width`
|
|
2012
|
+
}, percent, "%")), /*#__PURE__*/React.createElement("span", {
|
|
2013
|
+
className: "rte-media-toolbar-divider"
|
|
2014
|
+
}), /*#__PURE__*/React.createElement("button", {
|
|
1801
2015
|
type: "button",
|
|
1802
|
-
onClick:
|
|
2016
|
+
onClick: removeMedia,
|
|
1803
2017
|
className: "danger",
|
|
1804
|
-
title: "Remove
|
|
2018
|
+
title: "Remove"
|
|
1805
2019
|
}, "\xD7"));
|
|
1806
2020
|
};
|
|
1807
2021
|
if (isLoading) {
|
|
@@ -1837,7 +2051,7 @@ function RichTextEditor({
|
|
|
1837
2051
|
e.preventDefault();
|
|
1838
2052
|
e.stopPropagation();
|
|
1839
2053
|
}
|
|
1840
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
2054
|
+
}, !disabled && /*#__PURE__*/React.createElement("div", {
|
|
1841
2055
|
className: "rte-toolbar"
|
|
1842
2056
|
}, /*#__PURE__*/React.createElement("button", {
|
|
1843
2057
|
type: "button",
|
|
@@ -1885,6 +2099,41 @@ function RichTextEditor({
|
|
|
1885
2099
|
backgroundColor: '#e5e7eb',
|
|
1886
2100
|
margin: '0 4px'
|
|
1887
2101
|
}
|
|
2102
|
+
}), /*#__PURE__*/React.createElement("select", {
|
|
2103
|
+
value: currentBlockFormat,
|
|
2104
|
+
onMouseDown: e => e.stopPropagation(),
|
|
2105
|
+
onChange: e => {
|
|
2106
|
+
e.preventDefault();
|
|
2107
|
+
e.stopPropagation();
|
|
2108
|
+
applyBlockFormat(e.target.value);
|
|
2109
|
+
},
|
|
2110
|
+
className: "rte-toolbar-select rte-heading-select",
|
|
2111
|
+
title: "Text style"
|
|
2112
|
+
}, /*#__PURE__*/React.createElement("option", {
|
|
2113
|
+
value: "div"
|
|
2114
|
+
}, "Paragraph"), /*#__PURE__*/React.createElement("option", {
|
|
2115
|
+
value: "h1"
|
|
2116
|
+
}, "Heading 1"), /*#__PURE__*/React.createElement("option", {
|
|
2117
|
+
value: "h2"
|
|
2118
|
+
}, "Heading 2"), /*#__PURE__*/React.createElement("option", {
|
|
2119
|
+
value: "h3"
|
|
2120
|
+
}, "Heading 3"), /*#__PURE__*/React.createElement("option", {
|
|
2121
|
+
value: "blockquote"
|
|
2122
|
+
}, "Quote")), /*#__PURE__*/React.createElement("button", {
|
|
2123
|
+
type: "button",
|
|
2124
|
+
title: "Clear Formatting",
|
|
2125
|
+
className: "rte-toolbar-button rte-toolbar-button-text",
|
|
2126
|
+
onMouseDown: e => {
|
|
2127
|
+
e.preventDefault();
|
|
2128
|
+
clearFormatting();
|
|
2129
|
+
}
|
|
2130
|
+
}, "Tx"), /*#__PURE__*/React.createElement("div", {
|
|
2131
|
+
style: {
|
|
2132
|
+
width: '1px',
|
|
2133
|
+
height: '20px',
|
|
2134
|
+
backgroundColor: '#e5e7eb',
|
|
2135
|
+
margin: '0 4px'
|
|
2136
|
+
}
|
|
1888
2137
|
}), /*#__PURE__*/React.createElement("select", {
|
|
1889
2138
|
value: currentFontSize,
|
|
1890
2139
|
onMouseDown: e => e.stopPropagation(),
|
|
@@ -2051,11 +2300,9 @@ function RichTextEditor({
|
|
|
2051
2300
|
e.preventDefault();
|
|
2052
2301
|
addLink();
|
|
2053
2302
|
}
|
|
2054
|
-
}, /*#__PURE__*/React.createElement(
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
}
|
|
2058
|
-
}, "\uD83D\uDD17")), /*#__PURE__*/React.createElement("input", {
|
|
2303
|
+
}, /*#__PURE__*/React.createElement(FaLink, {
|
|
2304
|
+
size: 14
|
|
2305
|
+
})), /*#__PURE__*/React.createElement("input", {
|
|
2059
2306
|
ref: fileInputRef,
|
|
2060
2307
|
type: "file",
|
|
2061
2308
|
accept: "image/*",
|
|
@@ -2222,9 +2469,17 @@ function RichTextEditor({
|
|
|
2222
2469
|
}
|
|
2223
2470
|
return null;
|
|
2224
2471
|
})()), /*#__PURE__*/React.createElement("div", {
|
|
2472
|
+
className: "rte-content-wrapper",
|
|
2473
|
+
style: {
|
|
2474
|
+
position: 'relative'
|
|
2475
|
+
}
|
|
2476
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
2225
2477
|
ref: editorRef,
|
|
2226
2478
|
contentEditable: editable && disabled !== true,
|
|
2227
2479
|
suppressContentEditableWarning: true,
|
|
2480
|
+
role: "textbox",
|
|
2481
|
+
"aria-multiline": "true",
|
|
2482
|
+
"aria-label": label || "Rich text editor",
|
|
2228
2483
|
onInput: handleInput,
|
|
2229
2484
|
onPaste: handlePaste,
|
|
2230
2485
|
onDrop: handleDrop,
|
|
@@ -2240,7 +2495,13 @@ function RichTextEditor({
|
|
|
2240
2495
|
paddingLeft: paddingLeft || '12px'
|
|
2241
2496
|
},
|
|
2242
2497
|
className: `rte-content${editable ? " rte-is-editable" : ""}${editorFocused ? " rte-is-focused" : ""}`
|
|
2243
|
-
}),
|
|
2498
|
+
}), isEmpty && editable && disabled !== true && /*#__PURE__*/React.createElement("div", {
|
|
2499
|
+
className: "rte-placeholder",
|
|
2500
|
+
style: {
|
|
2501
|
+
left: paddingLeft || '12px'
|
|
2502
|
+
},
|
|
2503
|
+
"aria-hidden": "true"
|
|
2504
|
+
}, placeholder)), renderMediaToolbar(), /*#__PURE__*/React.createElement("div", {
|
|
2244
2505
|
className: "rte-footer"
|
|
2245
2506
|
}, /*#__PURE__*/React.createElement("div", {
|
|
2246
2507
|
className: "rte-footer-content"
|
|
@@ -2256,13 +2517,11 @@ function RichTextEditor({
|
|
|
2256
2517
|
}, /*#__PURE__*/React.createElement("div", {
|
|
2257
2518
|
className: "rte-modal",
|
|
2258
2519
|
onClick: e => e.stopPropagation()
|
|
2520
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
2521
|
+
className: "rte-modal-header"
|
|
2259
2522
|
}, /*#__PURE__*/React.createElement("h3", {
|
|
2260
2523
|
className: "rte-modal-title"
|
|
2261
|
-
}, "Insert Link"), /*#__PURE__*/React.createElement("div", {
|
|
2262
|
-
className: "rte-modal-divider"
|
|
2263
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
2264
|
-
className: "rte-modal-body"
|
|
2265
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
2524
|
+
}, "Insert Link")), /*#__PURE__*/React.createElement("div", {
|
|
2266
2525
|
className: "rte-form-group"
|
|
2267
2526
|
}, /*#__PURE__*/React.createElement("label", {
|
|
2268
2527
|
className: "rte-label"
|
|
@@ -2284,20 +2543,15 @@ function RichTextEditor({
|
|
|
2284
2543
|
onChange: e => setLinkUrl(e.target.value),
|
|
2285
2544
|
onKeyDown: e => e.key === 'Enter' && confirmLink(),
|
|
2286
2545
|
autoFocus: true
|
|
2287
|
-
}))
|
|
2288
|
-
className: "rte-modal-
|
|
2289
|
-
style: {
|
|
2290
|
-
margin: '8px 0 20px 0'
|
|
2291
|
-
}
|
|
2292
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
2293
|
-
className: "rte-modal-footer"
|
|
2546
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
2547
|
+
className: "rte-modal-actions"
|
|
2294
2548
|
}, /*#__PURE__*/React.createElement("button", {
|
|
2295
2549
|
type: "button",
|
|
2296
|
-
className: "rte-
|
|
2550
|
+
className: "rte-button rte-button-secondary",
|
|
2297
2551
|
onClick: cancelLink
|
|
2298
2552
|
}, "Cancel"), /*#__PURE__*/React.createElement("button", {
|
|
2299
2553
|
type: "button",
|
|
2300
|
-
className: "rte-
|
|
2554
|
+
className: "rte-button rte-button-primary",
|
|
2301
2555
|
onClick: confirmLink,
|
|
2302
2556
|
disabled: !linkUrl
|
|
2303
2557
|
}, "Insert")))), tableModalOpen && /*#__PURE__*/React.createElement("div", {
|