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.cjs.js
CHANGED
|
@@ -233,6 +233,27 @@ const FaFont = ({
|
|
|
233
233
|
}
|
|
234
234
|
});
|
|
235
235
|
};
|
|
236
|
+
const FaLink = ({
|
|
237
|
+
className,
|
|
238
|
+
size,
|
|
239
|
+
color,
|
|
240
|
+
style
|
|
241
|
+
}) => {
|
|
242
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
243
|
+
className: className,
|
|
244
|
+
style: {
|
|
245
|
+
display: 'inline-flex',
|
|
246
|
+
alignItems: 'center',
|
|
247
|
+
justifyContent: 'center',
|
|
248
|
+
fontSize: size || '1em',
|
|
249
|
+
color: color || 'inherit',
|
|
250
|
+
...style
|
|
251
|
+
},
|
|
252
|
+
dangerouslySetInnerHTML: {
|
|
253
|
+
__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>`
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
};
|
|
236
257
|
const FaTable = ({
|
|
237
258
|
className,
|
|
238
259
|
size,
|
|
@@ -390,7 +411,7 @@ function styleInject(css, ref) {
|
|
|
390
411
|
}
|
|
391
412
|
}
|
|
392
413
|
|
|
393
|
-
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:
|
|
414
|
+
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}";
|
|
394
415
|
styleInject(css_248z);
|
|
395
416
|
|
|
396
417
|
// Helper functions for HTML escaping
|
|
@@ -407,11 +428,12 @@ function RichTextEditor({
|
|
|
407
428
|
showEditButton,
|
|
408
429
|
onBlur,
|
|
409
430
|
disabled = false,
|
|
410
|
-
editable: initialEditable =
|
|
431
|
+
editable: initialEditable = true,
|
|
411
432
|
value,
|
|
412
433
|
isLoading,
|
|
413
434
|
isList = false,
|
|
414
435
|
label,
|
|
436
|
+
placeholder = "Type here...",
|
|
415
437
|
showBorder = true,
|
|
416
438
|
paddingLeft,
|
|
417
439
|
minHeight,
|
|
@@ -429,6 +451,7 @@ function RichTextEditor({
|
|
|
429
451
|
const [editable, setEditable] = React.useState(initialEditable);
|
|
430
452
|
const [editorFocused, setEditorFocused] = React.useState(false);
|
|
431
453
|
const lastSynchronizedHtmlRef = React.useRef("");
|
|
454
|
+
const syncProcessedMediaRef = React.useRef(() => {});
|
|
432
455
|
React.useEffect(() => {
|
|
433
456
|
setEditable(initialEditable);
|
|
434
457
|
}, [initialEditable]);
|
|
@@ -447,6 +470,7 @@ function RichTextEditor({
|
|
|
447
470
|
// NEW: Track current line height
|
|
448
471
|
const [currentLineHeight, setCurrentLineHeight] = React.useState("");
|
|
449
472
|
const [activeAlign, setActiveAlign] = React.useState(null);
|
|
473
|
+
const [currentBlockFormat, setCurrentBlockFormat] = React.useState("div");
|
|
450
474
|
const [imageModalOpen, setImageModalOpen] = React.useState(false);
|
|
451
475
|
const [selectedImageUrl, setSelectedImageUrl] = React.useState("");
|
|
452
476
|
const [zoomLevel, setZoomLevel] = React.useState(1);
|
|
@@ -458,11 +482,12 @@ function RichTextEditor({
|
|
|
458
482
|
const [tableRows, setTableRows] = React.useState(3);
|
|
459
483
|
const [tableCols, setTableCols] = React.useState(3);
|
|
460
484
|
const [selectionVersion, setSelectionVersion] = React.useState(0);
|
|
461
|
-
const [
|
|
485
|
+
const [selectedMedia, setSelectedMedia] = React.useState(null);
|
|
462
486
|
const [metrics, setMetrics] = React.useState({
|
|
463
487
|
words: 0,
|
|
464
488
|
chars: 0
|
|
465
489
|
});
|
|
490
|
+
const [isEmpty, setIsEmpty] = React.useState(!value);
|
|
466
491
|
const updateMetrics = React.useCallback(() => {
|
|
467
492
|
if (!editorRef.current) return;
|
|
468
493
|
// Calculate metrics immediately but outside of render path
|
|
@@ -474,6 +499,11 @@ function RichTextEditor({
|
|
|
474
499
|
words,
|
|
475
500
|
chars
|
|
476
501
|
});
|
|
502
|
+
|
|
503
|
+
// Track emptiness for the placeholder. Account for media-only content.
|
|
504
|
+
const stripped = text.replace(/[\u200B\u00A0\s]/g, "");
|
|
505
|
+
const hasMedia = !!editorRef.current.querySelector("img, table, iframe");
|
|
506
|
+
setIsEmpty(stripped.length === 0 && !hasMedia);
|
|
477
507
|
}, []);
|
|
478
508
|
const openImageModal = url => {
|
|
479
509
|
if (editorRef.current) {
|
|
@@ -535,16 +565,15 @@ function RichTextEditor({
|
|
|
535
565
|
}, [imageModalOpen]);
|
|
536
566
|
React.useEffect(() => {
|
|
537
567
|
if (editorRef.current && value && value !== lastSynchronizedHtmlRef.current) {
|
|
538
|
-
requestAnimationFrame(() =>
|
|
568
|
+
requestAnimationFrame(() => syncProcessedMediaRef.current(editorRef.current));
|
|
539
569
|
}
|
|
540
570
|
}, [value]);
|
|
541
|
-
|
|
542
|
-
// Runs whenever editable changes (toggles delete icon visibility)
|
|
543
571
|
React.useEffect(() => {
|
|
544
572
|
if (!editable) {
|
|
545
573
|
setEditorFocused(false);
|
|
574
|
+
clearMediaSelection();
|
|
546
575
|
}
|
|
547
|
-
|
|
576
|
+
syncProcessedMediaRef.current(editorRef.current);
|
|
548
577
|
}, [editable]);
|
|
549
578
|
React.useEffect(() => {
|
|
550
579
|
if (!editorRef.current) return;
|
|
@@ -575,7 +604,7 @@ function RichTextEditor({
|
|
|
575
604
|
if (editorRef.current && editorRef.current.innerHTML !== newContent) {
|
|
576
605
|
editorRef.current.innerHTML = newContent;
|
|
577
606
|
}
|
|
578
|
-
requestAnimationFrame(() =>
|
|
607
|
+
requestAnimationFrame(() => syncProcessedMediaRef.current(editorRef.current));
|
|
579
608
|
updateMetrics();
|
|
580
609
|
}
|
|
581
610
|
} catch (e) {
|
|
@@ -590,10 +619,47 @@ function RichTextEditor({
|
|
|
590
619
|
}
|
|
591
620
|
}
|
|
592
621
|
}, [value, initialEditable, updateMetrics]);
|
|
622
|
+
const LIST_BLOCK_MEDIA_SELECTOR = ".video-container, .image-container, table";
|
|
623
|
+
const isListItemEffectivelyEmpty = listItem => {
|
|
624
|
+
if (!listItem) return true;
|
|
625
|
+
const clone = listItem.cloneNode(true);
|
|
626
|
+
clone.querySelectorAll(LIST_BLOCK_MEDIA_SELECTOR).forEach(el => el.remove());
|
|
627
|
+
clone.querySelectorAll("br").forEach(el => el.remove());
|
|
628
|
+
return clone.textContent.replace(/[\u200B\u00A0\s]/g, "").length === 0;
|
|
629
|
+
};
|
|
630
|
+
const hoistBlockMediaOutOfListItems = container => {
|
|
631
|
+
if (!container) return false;
|
|
632
|
+
let changed = false;
|
|
633
|
+
container.querySelectorAll("ol, ul").forEach(list => {
|
|
634
|
+
const items = Array.from(list.children).filter(child => child.tagName === "LI");
|
|
635
|
+
items.forEach(listItem => {
|
|
636
|
+
const blockMedia = Array.from(listItem.querySelectorAll(LIST_BLOCK_MEDIA_SELECTOR));
|
|
637
|
+
if (blockMedia.length === 0) return;
|
|
638
|
+
const hadText = !isListItemEffectivelyEmpty(listItem);
|
|
639
|
+
blockMedia.forEach(media => {
|
|
640
|
+
listItem.removeChild(media);
|
|
641
|
+
if (list.parentNode) {
|
|
642
|
+
list.parentNode.insertBefore(media, list.nextSibling);
|
|
643
|
+
}
|
|
644
|
+
changed = true;
|
|
645
|
+
});
|
|
646
|
+
if (!hadText || isListItemEffectivelyEmpty(listItem)) {
|
|
647
|
+
listItem.remove();
|
|
648
|
+
changed = true;
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
if (list.children.length === 0 && list.parentNode) {
|
|
652
|
+
list.remove();
|
|
653
|
+
changed = true;
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
return changed;
|
|
657
|
+
};
|
|
593
658
|
const processExistingMedia = container => {
|
|
594
|
-
if (!container) return;
|
|
659
|
+
if (!container) return false;
|
|
595
660
|
processExistingImages(container);
|
|
596
661
|
processExistingVideos(container);
|
|
662
|
+
return hoistBlockMediaOutOfListItems(container);
|
|
597
663
|
};
|
|
598
664
|
const getCleanHtml = () => {
|
|
599
665
|
if (!editorRef.current) return "";
|
|
@@ -609,6 +675,11 @@ function RichTextEditor({
|
|
|
609
675
|
lastSynchronizedHtmlRef.current = next;
|
|
610
676
|
onChange && onChange(next);
|
|
611
677
|
}, [onChange]);
|
|
678
|
+
syncProcessedMediaRef.current = container => {
|
|
679
|
+
if (processExistingMedia(container)) {
|
|
680
|
+
triggerChange();
|
|
681
|
+
}
|
|
682
|
+
};
|
|
612
683
|
|
|
613
684
|
// Helper to walk up DOM to find style tags or CSS style:
|
|
614
685
|
const isParentStyle = (node, ...tagNames) => {
|
|
@@ -664,17 +735,15 @@ function RichTextEditor({
|
|
|
664
735
|
return rgbToHex(computedColor);
|
|
665
736
|
};
|
|
666
737
|
const stripEditorChrome = root => {
|
|
667
|
-
root.querySelectorAll(".image-delete-button, .video-delete-button, .video-edit-overlay, .media-resize-
|
|
738
|
+
root.querySelectorAll(".image-delete-button, .video-delete-button, .video-edit-overlay, .media-resize-handle").forEach(element => element.remove());
|
|
739
|
+
root.querySelectorAll(".rte-media-selected").forEach(element => {
|
|
740
|
+
element.classList.remove("rte-media-selected");
|
|
741
|
+
});
|
|
668
742
|
return root;
|
|
669
743
|
};
|
|
670
|
-
const
|
|
671
|
-
|
|
672
|
-
return
|
|
673
|
-
minWidth: 120,
|
|
674
|
-
minHeight: 80,
|
|
675
|
-
maxWidth,
|
|
676
|
-
maxHeight: 720
|
|
677
|
-
};
|
|
744
|
+
const getEditorInnerWidth = () => {
|
|
745
|
+
if (!editorRef.current) return 800;
|
|
746
|
+
return Math.max(editorRef.current.clientWidth - 24, 200);
|
|
678
747
|
};
|
|
679
748
|
const ensureImageMediaFrame = imageContainer => {
|
|
680
749
|
if (!imageContainer) return null;
|
|
@@ -682,160 +751,164 @@ function RichTextEditor({
|
|
|
682
751
|
if (frame) return frame;
|
|
683
752
|
frame = document.createElement("div");
|
|
684
753
|
frame.className = "image-media-frame";
|
|
685
|
-
["width", "height", "marginLeft", "marginTop", "maxWidth"].forEach(prop => {
|
|
686
|
-
if (imageContainer.style[prop]) {
|
|
687
|
-
frame.style[prop] = imageContainer.style[prop];
|
|
688
|
-
imageContainer.style[prop] = "";
|
|
689
|
-
}
|
|
690
|
-
});
|
|
691
|
-
if (imageContainer.dataset.explicitHeight) {
|
|
692
|
-
frame.dataset.explicitHeight = imageContainer.dataset.explicitHeight;
|
|
693
|
-
delete imageContainer.dataset.explicitHeight;
|
|
694
|
-
}
|
|
695
754
|
const children = Array.from(imageContainer.children);
|
|
696
755
|
imageContainer.appendChild(frame);
|
|
697
756
|
children.forEach(child => frame.appendChild(child));
|
|
698
757
|
return frame;
|
|
699
758
|
};
|
|
700
759
|
const getImageMediaTarget = imageContainer => ensureImageMediaFrame(imageContainer) || imageContainer;
|
|
701
|
-
const
|
|
760
|
+
const getMediaWidthPercent = container => {
|
|
761
|
+
if (!container) return 100;
|
|
762
|
+
if (container.dataset.widthPercent) {
|
|
763
|
+
return Number(container.dataset.widthPercent);
|
|
764
|
+
}
|
|
765
|
+
const width = container.style.width || "";
|
|
766
|
+
if (width.endsWith("%")) {
|
|
767
|
+
return parseInt(width, 10) || 100;
|
|
768
|
+
}
|
|
769
|
+
const editorWidth = getEditorInnerWidth();
|
|
770
|
+
const rect = container.getBoundingClientRect();
|
|
771
|
+
if (editorWidth > 0 && rect.width > 0) {
|
|
772
|
+
return Math.round(rect.width / editorWidth * 100);
|
|
773
|
+
}
|
|
774
|
+
return 100;
|
|
775
|
+
};
|
|
776
|
+
const applyMediaWidthPercent = (container, percent) => {
|
|
777
|
+
if (!container) return;
|
|
778
|
+
const clamped = Math.max(25, Math.min(100, Math.round(percent)));
|
|
779
|
+
container.dataset.widthPercent = String(clamped);
|
|
780
|
+
container.style.width = `${clamped}%`;
|
|
781
|
+
container.style.maxWidth = "100%";
|
|
782
|
+
container.style.marginLeft = "";
|
|
783
|
+
container.style.marginTop = "";
|
|
784
|
+
if (container.classList.contains("video-container")) {
|
|
785
|
+
container.style.height = "0";
|
|
786
|
+
container.style.paddingBottom = "56.25%";
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const frame = getImageMediaTarget(container);
|
|
790
|
+
if (!frame) return;
|
|
791
|
+
frame.style.width = "100%";
|
|
792
|
+
frame.style.height = "";
|
|
702
793
|
const img = frame.querySelector("img");
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
794
|
+
if (img) {
|
|
795
|
+
img.style.height = "auto";
|
|
796
|
+
img.style.objectFit = "";
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
const normalizeMediaWidth = container => {
|
|
800
|
+
if (!container) return;
|
|
801
|
+
if (container.classList.contains("image-small")) {
|
|
802
|
+
container.classList.remove("image-small");
|
|
803
|
+
applyMediaWidthPercent(container, 50);
|
|
714
804
|
return;
|
|
715
805
|
}
|
|
716
|
-
if (
|
|
717
|
-
|
|
806
|
+
if (container.dataset.widthPercent) {
|
|
807
|
+
applyMediaWidthPercent(container, Number(container.dataset.widthPercent));
|
|
808
|
+
return;
|
|
718
809
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
810
|
+
const width = container.style.width || "";
|
|
811
|
+
if (width.endsWith("%")) {
|
|
812
|
+
applyMediaWidthPercent(container, parseInt(width, 10) || 100);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
if (width.endsWith("px")) {
|
|
816
|
+
const editorWidth = getEditorInnerWidth();
|
|
817
|
+
const px = parseFloat(width);
|
|
818
|
+
if (editorWidth > 0 && px > 0) {
|
|
819
|
+
applyMediaWidthPercent(container, Math.round(px / editorWidth * 100));
|
|
727
820
|
}
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (container.classList.contains("video-container")) {
|
|
824
|
+
applyMediaWidthPercent(container, 100);
|
|
728
825
|
}
|
|
729
826
|
};
|
|
730
|
-
const
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
827
|
+
const clearMediaSelection = () => {
|
|
828
|
+
var _editorRef$current;
|
|
829
|
+
(_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 || _editorRef$current.querySelectorAll(".rte-media-selected").forEach(element => {
|
|
830
|
+
element.classList.remove("rte-media-selected");
|
|
831
|
+
});
|
|
832
|
+
setSelectedMedia(null);
|
|
833
|
+
};
|
|
834
|
+
const selectMediaContainer = container => {
|
|
835
|
+
var _editorRef$current2;
|
|
836
|
+
if (!container || !((_editorRef$current2 = editorRef.current) !== null && _editorRef$current2 !== void 0 && _editorRef$current2.contains(container))) return;
|
|
837
|
+
editorRef.current.querySelectorAll(".rte-media-selected").forEach(element => {
|
|
838
|
+
element.classList.remove("rte-media-selected");
|
|
839
|
+
});
|
|
840
|
+
container.classList.add("rte-media-selected");
|
|
841
|
+
setSelectedMedia(container);
|
|
735
842
|
};
|
|
736
843
|
const attachMediaResizeHandle = container => {
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const resizeTarget =
|
|
844
|
+
var _resizeTarget$querySe;
|
|
845
|
+
if (!container) return;
|
|
846
|
+
const resizeTarget = container.classList.contains("video-container") ? container : getImageMediaTarget(container);
|
|
740
847
|
if (!resizeTarget) return;
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
handle.addEventListener("mousedown", event => {
|
|
767
|
-
if (!editable) return;
|
|
768
|
-
event.preventDefault();
|
|
769
|
-
event.stopPropagation();
|
|
770
|
-
const limits = getMediaSizeLimits();
|
|
771
|
-
const rect = resizeTarget.getBoundingClientRect();
|
|
772
|
-
const startX = event.clientX;
|
|
773
|
-
const startY = event.clientY;
|
|
774
|
-
const startWidth = rect.width;
|
|
775
|
-
const startHeight = rect.height;
|
|
776
|
-
const startMarginLeft = Number.parseFloat(resizeTarget.style.marginLeft) || 0;
|
|
777
|
-
const startMarginTop = Number.parseFloat(resizeTarget.style.marginTop) || 0;
|
|
778
|
-
if (isVideo) {
|
|
779
|
-
resizeTarget.style.paddingBottom = "0";
|
|
780
|
-
}
|
|
781
|
-
const onMouseMove = moveEvent => {
|
|
782
|
-
const deltaX = moveEvent.clientX - startX;
|
|
783
|
-
const deltaY = moveEvent.clientY - startY;
|
|
784
|
-
let nextWidth = startWidth;
|
|
785
|
-
let nextHeight = startHeight;
|
|
786
|
-
if (edge === "right") {
|
|
787
|
-
nextWidth = startWidth + deltaX;
|
|
788
|
-
} else if (edge === "left") {
|
|
789
|
-
nextWidth = startWidth - deltaX;
|
|
790
|
-
} else if (edge === "bottom") {
|
|
791
|
-
nextHeight = startHeight + deltaY;
|
|
792
|
-
} else if (edge === "top") {
|
|
793
|
-
nextHeight = startHeight - deltaY;
|
|
794
|
-
}
|
|
795
|
-
nextWidth = Math.max(limits.minWidth, Math.min(nextWidth, limits.maxWidth));
|
|
796
|
-
nextHeight = Math.max(limits.minHeight, Math.min(nextHeight, limits.maxHeight));
|
|
797
|
-
if (edge === "left") {
|
|
798
|
-
resizeTarget.style.marginLeft = `${Math.round(startMarginLeft + (startWidth - nextWidth))}px`;
|
|
799
|
-
}
|
|
800
|
-
if (edge === "top") {
|
|
801
|
-
resizeTarget.style.marginTop = `${Math.round(startMarginTop + (startHeight - nextHeight))}px`;
|
|
802
|
-
}
|
|
803
|
-
if (isVideo) {
|
|
804
|
-
applyVideoMediaSize(resizeTarget, nextWidth, nextHeight);
|
|
805
|
-
} else {
|
|
806
|
-
applyImageMediaSize(resizeTarget, nextWidth, nextHeight, edge);
|
|
807
|
-
}
|
|
808
|
-
};
|
|
809
|
-
const onMouseUp = () => {
|
|
810
|
-
document.removeEventListener("mousemove", onMouseMove);
|
|
811
|
-
document.removeEventListener("mouseup", onMouseUp);
|
|
812
|
-
triggerChange();
|
|
813
|
-
};
|
|
814
|
-
document.addEventListener("mousemove", onMouseMove);
|
|
815
|
-
document.addEventListener("mouseup", onMouseUp);
|
|
816
|
-
});
|
|
817
|
-
handlesWrapper.appendChild(handle);
|
|
848
|
+
(_resizeTarget$querySe = resizeTarget.querySelector(".media-resize-handle")) === null || _resizeTarget$querySe === void 0 || _resizeTarget$querySe.remove();
|
|
849
|
+
const handle = document.createElement("div");
|
|
850
|
+
handle.className = "media-resize-handle";
|
|
851
|
+
handle.title = "Drag to resize";
|
|
852
|
+
handle.setAttribute("contenteditable", "false");
|
|
853
|
+
handle.addEventListener("mousedown", event => {
|
|
854
|
+
if (!editable) return;
|
|
855
|
+
event.preventDefault();
|
|
856
|
+
event.stopPropagation();
|
|
857
|
+
selectMediaContainer(container);
|
|
858
|
+
const editorWidth = getEditorInnerWidth();
|
|
859
|
+
const startX = event.clientX;
|
|
860
|
+
const startWidth = container.getBoundingClientRect().width;
|
|
861
|
+
const onMouseMove = moveEvent => {
|
|
862
|
+
const nextWidth = Math.max(60, startWidth + (moveEvent.clientX - startX));
|
|
863
|
+
const percent = Math.round(nextWidth / editorWidth * 100);
|
|
864
|
+
applyMediaWidthPercent(container, percent);
|
|
865
|
+
};
|
|
866
|
+
const onMouseUp = () => {
|
|
867
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
868
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
869
|
+
triggerChange();
|
|
870
|
+
};
|
|
871
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
872
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
818
873
|
});
|
|
819
|
-
resizeTarget.appendChild(
|
|
874
|
+
resizeTarget.appendChild(handle);
|
|
820
875
|
};
|
|
821
876
|
const handleEditorFocus = () => {
|
|
822
877
|
setEditorFocused(true);
|
|
823
878
|
};
|
|
824
879
|
const handleEditorBlur = () => {
|
|
825
880
|
requestAnimationFrame(() => {
|
|
826
|
-
var _editorRef$
|
|
827
|
-
if (!((_editorRef$
|
|
881
|
+
var _editorRef$current3;
|
|
882
|
+
if (!((_editorRef$current3 = editorRef.current) !== null && _editorRef$current3 !== void 0 && _editorRef$current3.contains(document.activeElement))) {
|
|
828
883
|
setEditorFocused(false);
|
|
829
884
|
}
|
|
830
885
|
});
|
|
831
886
|
};
|
|
832
887
|
const updateMediaControlVisibility = container => {
|
|
833
|
-
const
|
|
834
|
-
if (
|
|
835
|
-
|
|
836
|
-
|
|
888
|
+
const handle = container.querySelector(".media-resize-handle");
|
|
889
|
+
if (handle instanceof HTMLElement) {
|
|
890
|
+
handle.style.display = editable ? "block" : "none";
|
|
891
|
+
handle.style.pointerEvents = editable ? "auto" : "none";
|
|
837
892
|
}
|
|
838
893
|
};
|
|
894
|
+
const BLOCK_TAGS = ["P", "DIV", "H1", "H2", "H3", "BLOCKQUOTE", "LI"];
|
|
895
|
+
const getActiveBlock = node => {
|
|
896
|
+
if (!editorRef.current || !node) return null;
|
|
897
|
+
let current = node.nodeType === 3 ? node.parentNode : node;
|
|
898
|
+
while (current && current !== editorRef.current) {
|
|
899
|
+
if (current.nodeType === 1 && BLOCK_TAGS.includes(current.tagName)) {
|
|
900
|
+
return current;
|
|
901
|
+
}
|
|
902
|
+
current = current.parentNode;
|
|
903
|
+
}
|
|
904
|
+
return editorRef.current;
|
|
905
|
+
};
|
|
906
|
+
const getBlockFormat = node => {
|
|
907
|
+
const block = getActiveBlock(node);
|
|
908
|
+
if (!block || block === editorRef.current) return "div";
|
|
909
|
+
const tag = block.tagName.toLowerCase();
|
|
910
|
+
return tag === "p" || tag === "li" ? "div" : tag;
|
|
911
|
+
};
|
|
839
912
|
const createMediaDeleteButton = (title, className, onRemove) => {
|
|
840
913
|
const deleteBtn = document.createElement("button");
|
|
841
914
|
deleteBtn.type = "button";
|
|
@@ -854,10 +927,10 @@ function RichTextEditor({
|
|
|
854
927
|
// Listen for selection changes globally to update styles and list type in one pass
|
|
855
928
|
React.useEffect(() => {
|
|
856
929
|
const handleGlobalSelectionSync = () => {
|
|
857
|
-
var _editorRef$
|
|
930
|
+
var _editorRef$current4;
|
|
858
931
|
// Only sync if the editor has focus
|
|
859
932
|
const sel = window.getSelection();
|
|
860
|
-
if (!sel || !sel.rangeCount || !((_editorRef$
|
|
933
|
+
if (!sel || !sel.rangeCount || !((_editorRef$current4 = editorRef.current) !== null && _editorRef$current4 !== void 0 && _editorRef$current4.contains(sel.anchorNode))) {
|
|
861
934
|
return;
|
|
862
935
|
}
|
|
863
936
|
|
|
@@ -905,6 +978,7 @@ function RichTextEditor({
|
|
|
905
978
|
} else {
|
|
906
979
|
setCurrentFontSize("16");
|
|
907
980
|
}
|
|
981
|
+
setCurrentBlockFormat(getBlockFormat(sel.anchorNode));
|
|
908
982
|
};
|
|
909
983
|
document.addEventListener("selectionchange", handleGlobalSelectionSync);
|
|
910
984
|
return () => {
|
|
@@ -1128,8 +1202,10 @@ function RichTextEditor({
|
|
|
1128
1202
|
}
|
|
1129
1203
|
setVideoModalOpen(false);
|
|
1130
1204
|
setVideoUrl("");
|
|
1131
|
-
|
|
1132
|
-
|
|
1205
|
+
requestAnimationFrame(() => {
|
|
1206
|
+
processExistingMedia(editorRef.current);
|
|
1207
|
+
triggerChange();
|
|
1208
|
+
});
|
|
1133
1209
|
} else {
|
|
1134
1210
|
console.warn("Invalid Video URL or Platform not supported");
|
|
1135
1211
|
}
|
|
@@ -1140,11 +1216,25 @@ function RichTextEditor({
|
|
|
1140
1216
|
if (!videoContainer.querySelector(".video-delete-button")) {
|
|
1141
1217
|
const deleteBtn = createMediaDeleteButton("Remove video", "video-delete-button image-delete-button", () => {
|
|
1142
1218
|
videoContainer.remove();
|
|
1219
|
+
clearMediaSelection();
|
|
1143
1220
|
triggerChange && triggerChange();
|
|
1144
1221
|
});
|
|
1145
1222
|
videoContainer.appendChild(deleteBtn);
|
|
1146
1223
|
}
|
|
1224
|
+
if (!videoContainer.dataset.mediaEnhanced) {
|
|
1225
|
+
videoContainer.dataset.mediaEnhanced = "true";
|
|
1226
|
+
if (!videoContainer.classList.contains("image-align-left") && !videoContainer.classList.contains("image-align-center") && !videoContainer.classList.contains("image-align-right")) {
|
|
1227
|
+
videoContainer.classList.add("image-align-left");
|
|
1228
|
+
}
|
|
1229
|
+
videoContainer.addEventListener("click", event => {
|
|
1230
|
+
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1231
|
+
event.preventDefault();
|
|
1232
|
+
event.stopPropagation();
|
|
1233
|
+
selectMediaContainer(videoContainer);
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1147
1236
|
attachMediaResizeHandle(videoContainer);
|
|
1237
|
+
normalizeMediaWidth(videoContainer);
|
|
1148
1238
|
updateMediaControlVisibility(videoContainer);
|
|
1149
1239
|
});
|
|
1150
1240
|
};
|
|
@@ -1164,6 +1254,7 @@ function RichTextEditor({
|
|
|
1164
1254
|
}));
|
|
1165
1255
|
}
|
|
1166
1256
|
attachMediaResizeHandle(existingWrapper);
|
|
1257
|
+
normalizeMediaWidth(existingWrapper);
|
|
1167
1258
|
updateMediaControlVisibility(existingWrapper);
|
|
1168
1259
|
return;
|
|
1169
1260
|
}
|
|
@@ -1173,24 +1264,21 @@ function RichTextEditor({
|
|
|
1173
1264
|
wrapper.style.cursor = editable ? "pointer" : "default";
|
|
1174
1265
|
const frame = document.createElement("div");
|
|
1175
1266
|
frame.className = "image-media-frame";
|
|
1176
|
-
if (img.getAttribute("width") && !frame.style.width) {
|
|
1177
|
-
frame.style.width = `${img.getAttribute("width")}px`;
|
|
1178
|
-
} else if (img.style.width && img.style.width.endsWith("px")) {
|
|
1179
|
-
frame.style.width = img.style.width;
|
|
1180
|
-
}
|
|
1181
1267
|
img.classList.add("rte-image");
|
|
1182
1268
|
img.setAttribute("data-align", align);
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
img.style.height = frame.dataset.explicitHeight ? "100%" : "auto";
|
|
1186
|
-
} else {
|
|
1187
|
-
img.style.width = "";
|
|
1188
|
-
img.style.height = "auto";
|
|
1189
|
-
}
|
|
1190
|
-
img.addEventListener("click", event => {
|
|
1269
|
+
img.style.height = "auto";
|
|
1270
|
+
img.addEventListener("dblclick", event => {
|
|
1191
1271
|
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1272
|
+
event.preventDefault();
|
|
1273
|
+
event.stopPropagation();
|
|
1192
1274
|
openImageModal(img.src);
|
|
1193
1275
|
});
|
|
1276
|
+
img.addEventListener("click", event => {
|
|
1277
|
+
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1278
|
+
event.preventDefault();
|
|
1279
|
+
event.stopPropagation();
|
|
1280
|
+
selectMediaContainer(wrapper);
|
|
1281
|
+
});
|
|
1194
1282
|
const deleteBtn = createMediaDeleteButton("Remove image", "image-delete-button", () => {
|
|
1195
1283
|
wrapper.remove();
|
|
1196
1284
|
triggerChange && triggerChange();
|
|
@@ -1205,6 +1293,7 @@ function RichTextEditor({
|
|
|
1205
1293
|
frame.appendChild(deleteBtn);
|
|
1206
1294
|
wrapper.appendChild(frame);
|
|
1207
1295
|
attachMediaResizeHandle(wrapper);
|
|
1296
|
+
normalizeMediaWidth(wrapper);
|
|
1208
1297
|
if (nextSibling) {
|
|
1209
1298
|
parentNode.insertBefore(wrapper, nextSibling);
|
|
1210
1299
|
} else {
|
|
@@ -1231,9 +1320,19 @@ function RichTextEditor({
|
|
|
1231
1320
|
const img = document.createElement('img');
|
|
1232
1321
|
img.src = dataUrl;
|
|
1233
1322
|
img.alt = fileName || "image";
|
|
1234
|
-
img.addEventListener("
|
|
1323
|
+
img.addEventListener("dblclick", event => {
|
|
1324
|
+
event.preventDefault();
|
|
1325
|
+
event.stopPropagation();
|
|
1326
|
+
openImageModal(dataUrl);
|
|
1327
|
+
});
|
|
1328
|
+
img.addEventListener("click", event => {
|
|
1329
|
+
event.preventDefault();
|
|
1330
|
+
event.stopPropagation();
|
|
1331
|
+
selectMediaContainer(container);
|
|
1332
|
+
});
|
|
1235
1333
|
frame.appendChild(img);
|
|
1236
1334
|
container.appendChild(frame);
|
|
1335
|
+
applyMediaWidthPercent(container, 25);
|
|
1237
1336
|
|
|
1238
1337
|
// Insert at cursor position
|
|
1239
1338
|
insertNodeAtCursor(container);
|
|
@@ -1376,6 +1475,12 @@ function RichTextEditor({
|
|
|
1376
1475
|
txt.innerHTML = html;
|
|
1377
1476
|
return txt.value;
|
|
1378
1477
|
};
|
|
1478
|
+
const isCursorAtStartOfListItem = (range, listItem) => {
|
|
1479
|
+
const prefixRange = document.createRange();
|
|
1480
|
+
prefixRange.setStart(listItem, 0);
|
|
1481
|
+
prefixRange.setEnd(range.startContainer, range.startOffset);
|
|
1482
|
+
return prefixRange.toString().replace(/[\u200B\u00A0\s]/g, "").length === 0;
|
|
1483
|
+
};
|
|
1379
1484
|
const isCursorAtEndOfListItem = (range, listItem) => {
|
|
1380
1485
|
const suffixRange = document.createRange();
|
|
1381
1486
|
suffixRange.setStart(range.startContainer, range.startOffset);
|
|
@@ -1400,6 +1505,8 @@ function RichTextEditor({
|
|
|
1400
1505
|
selection.addRange(newRange);
|
|
1401
1506
|
};
|
|
1402
1507
|
const handleKeyDown = React.useCallback(e => {
|
|
1508
|
+
if (applyMarkdownShortcut(e)) return;
|
|
1509
|
+
|
|
1403
1510
|
// Handle Enter key
|
|
1404
1511
|
if (e.key === 'Enter') {
|
|
1405
1512
|
e.preventDefault();
|
|
@@ -1420,7 +1527,7 @@ function RichTextEditor({
|
|
|
1420
1527
|
// If we're at the end of a list item, add a new one
|
|
1421
1528
|
if (range.collapsed && isCursorAtEndOfListItem(range, listItem)) {
|
|
1422
1529
|
// If it's empty, create a regular paragraph instead
|
|
1423
|
-
if (listItem
|
|
1530
|
+
if (isListItemEffectivelyEmpty(listItem)) {
|
|
1424
1531
|
document.execCommand('insertHTML', false, '<div><br></div>');
|
|
1425
1532
|
// Move the cursor to the new line
|
|
1426
1533
|
const newRange = document.createRange();
|
|
@@ -1452,7 +1559,7 @@ function RichTextEditor({
|
|
|
1452
1559
|
} else {
|
|
1453
1560
|
list.appendChild(newItem);
|
|
1454
1561
|
}
|
|
1455
|
-
if (
|
|
1562
|
+
if (isListItemEffectivelyEmpty(newItem)) {
|
|
1456
1563
|
newItem.textContent = "";
|
|
1457
1564
|
prepareListItemForTyping(newItem, selection);
|
|
1458
1565
|
} else {
|
|
@@ -1470,6 +1577,54 @@ function RichTextEditor({
|
|
|
1470
1577
|
triggerChange();
|
|
1471
1578
|
return;
|
|
1472
1579
|
}
|
|
1580
|
+
if (e.key === "Backspace") {
|
|
1581
|
+
var _node$closest, _node, _editorRef$current5;
|
|
1582
|
+
const selection = window.getSelection();
|
|
1583
|
+
if (!(selection !== null && selection !== void 0 && selection.rangeCount)) return;
|
|
1584
|
+
const range = selection.getRangeAt(0);
|
|
1585
|
+
if (!range.collapsed) return;
|
|
1586
|
+
let node = range.startContainer;
|
|
1587
|
+
if (node.nodeType === 3) {
|
|
1588
|
+
node = node.parentNode;
|
|
1589
|
+
}
|
|
1590
|
+
const listItem = (_node$closest = (_node = node).closest) === null || _node$closest === void 0 ? void 0 : _node$closest.call(_node, "li");
|
|
1591
|
+
if (!listItem || !((_editorRef$current5 = editorRef.current) !== null && _editorRef$current5 !== void 0 && _editorRef$current5.contains(listItem))) return;
|
|
1592
|
+
const list = listItem.parentNode;
|
|
1593
|
+
if (isListItemEffectivelyEmpty(listItem)) {
|
|
1594
|
+
e.preventDefault();
|
|
1595
|
+
const prevLi = listItem.previousElementSibling;
|
|
1596
|
+
const blockMedia = Array.from(listItem.querySelectorAll(LIST_BLOCK_MEDIA_SELECTOR));
|
|
1597
|
+
blockMedia.forEach(media => {
|
|
1598
|
+
var _list$parentNode;
|
|
1599
|
+
(_list$parentNode = list.parentNode) === null || _list$parentNode === void 0 || _list$parentNode.insertBefore(media, list.nextSibling);
|
|
1600
|
+
});
|
|
1601
|
+
listItem.remove();
|
|
1602
|
+
if (list.children.length === 0) {
|
|
1603
|
+
list.remove();
|
|
1604
|
+
}
|
|
1605
|
+
if ((prevLi === null || prevLi === void 0 ? void 0 : prevLi.tagName) === "LI") {
|
|
1606
|
+
const newRange = document.createRange();
|
|
1607
|
+
newRange.selectNodeContents(prevLi);
|
|
1608
|
+
newRange.collapse(false);
|
|
1609
|
+
selection.removeAllRanges();
|
|
1610
|
+
selection.addRange(newRange);
|
|
1611
|
+
}
|
|
1612
|
+
triggerChange();
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
if (isCursorAtStartOfListItem(range, listItem)) {
|
|
1616
|
+
const prevLi = listItem.previousElementSibling;
|
|
1617
|
+
if ((prevLi === null || prevLi === void 0 ? void 0 : prevLi.tagName) === "LI" && isListItemEffectivelyEmpty(prevLi)) {
|
|
1618
|
+
e.preventDefault();
|
|
1619
|
+
prevLi.remove();
|
|
1620
|
+
if (list.children.length === 0) {
|
|
1621
|
+
list.remove();
|
|
1622
|
+
}
|
|
1623
|
+
triggerChange();
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1473
1628
|
|
|
1474
1629
|
// Handle Ctrl/Cmd + B/I/U for bold/italic/underline
|
|
1475
1630
|
if ((e.ctrlKey || e.metaKey) && e.key === "b") {
|
|
@@ -1510,6 +1665,65 @@ function RichTextEditor({
|
|
|
1510
1665
|
const handleSelect = type => {
|
|
1511
1666
|
exec(type === "unordered" ? "insertUnorderedList" : "insertOrderedList");
|
|
1512
1667
|
};
|
|
1668
|
+
const applyBlockFormat = format => {
|
|
1669
|
+
document.execCommand("formatBlock", false, format);
|
|
1670
|
+
setCurrentBlockFormat(format);
|
|
1671
|
+
triggerChange();
|
|
1672
|
+
focus();
|
|
1673
|
+
};
|
|
1674
|
+
const clearFormatting = () => {
|
|
1675
|
+
document.execCommand("removeFormat", false, null);
|
|
1676
|
+
document.execCommand("unlink", false, null);
|
|
1677
|
+
document.execCommand("formatBlock", false, "div");
|
|
1678
|
+
setCurrentBlockFormat("div");
|
|
1679
|
+
setCurrentFontSize("16");
|
|
1680
|
+
setCurrentLineHeight("");
|
|
1681
|
+
setFontColor("#000000");
|
|
1682
|
+
triggerChange();
|
|
1683
|
+
focus();
|
|
1684
|
+
};
|
|
1685
|
+
const deleteTextBeforeCursorInBlock = (block, range, selection) => {
|
|
1686
|
+
const prefixRange = document.createRange();
|
|
1687
|
+
prefixRange.setStart(block, 0);
|
|
1688
|
+
prefixRange.setEnd(range.startContainer, range.startOffset);
|
|
1689
|
+
prefixRange.deleteContents();
|
|
1690
|
+
const nextRange = document.createRange();
|
|
1691
|
+
nextRange.setStart(block, 0);
|
|
1692
|
+
nextRange.collapse(true);
|
|
1693
|
+
selection.removeAllRanges();
|
|
1694
|
+
selection.addRange(nextRange);
|
|
1695
|
+
};
|
|
1696
|
+
const applyMarkdownShortcut = event => {
|
|
1697
|
+
if (event.key !== " " || event.ctrlKey || event.metaKey || event.altKey) {
|
|
1698
|
+
return false;
|
|
1699
|
+
}
|
|
1700
|
+
const selection = window.getSelection();
|
|
1701
|
+
if (!(selection !== null && selection !== void 0 && selection.rangeCount) || !selection.isCollapsed || !editorRef.current) {
|
|
1702
|
+
return false;
|
|
1703
|
+
}
|
|
1704
|
+
const range = selection.getRangeAt(0);
|
|
1705
|
+
const block = getActiveBlock(range.startContainer);
|
|
1706
|
+
if (!block || !editorRef.current.contains(block)) return false;
|
|
1707
|
+
const prefixRange = document.createRange();
|
|
1708
|
+
prefixRange.setStart(block, 0);
|
|
1709
|
+
prefixRange.setEnd(range.startContainer, range.startOffset);
|
|
1710
|
+
const textBeforeCursor = prefixRange.toString().replace(/\u00A0/g, " ").trim();
|
|
1711
|
+
const shortcuts = {
|
|
1712
|
+
"#": () => applyBlockFormat("h1"),
|
|
1713
|
+
"##": () => applyBlockFormat("h2"),
|
|
1714
|
+
"###": () => applyBlockFormat("h3"),
|
|
1715
|
+
">": () => applyBlockFormat("blockquote"),
|
|
1716
|
+
"-": () => handleSelect("unordered"),
|
|
1717
|
+
"*": () => handleSelect("unordered"),
|
|
1718
|
+
"1.": () => handleSelect("ordered")
|
|
1719
|
+
};
|
|
1720
|
+
const action = shortcuts[textBeforeCursor];
|
|
1721
|
+
if (!action) return false;
|
|
1722
|
+
event.preventDefault();
|
|
1723
|
+
deleteTextBeforeCursorInBlock(block, range, selection);
|
|
1724
|
+
action();
|
|
1725
|
+
return true;
|
|
1726
|
+
};
|
|
1513
1727
|
const onLineHeightChange = value => {
|
|
1514
1728
|
if (!value) return;
|
|
1515
1729
|
const sel = window.getSelection();
|
|
@@ -1691,6 +1905,7 @@ function RichTextEditor({
|
|
|
1691
1905
|
}
|
|
1692
1906
|
}, [disabled]);
|
|
1693
1907
|
const handleEditorClick = React.useCallback(e => {
|
|
1908
|
+
var _editorRef$current6;
|
|
1694
1909
|
setSelectionVersion(v => v + 1);
|
|
1695
1910
|
const deleteBtn = e.target.closest('button[title="Remove image"], button[title="Remove video"]');
|
|
1696
1911
|
if (deleteBtn && editable && editorFocused) {
|
|
@@ -1699,10 +1914,15 @@ function RichTextEditor({
|
|
|
1699
1914
|
const wrapper = deleteBtn.closest('.image-container, .video-container');
|
|
1700
1915
|
if (wrapper) {
|
|
1701
1916
|
wrapper.remove();
|
|
1917
|
+
clearMediaSelection();
|
|
1702
1918
|
triggerChange();
|
|
1703
1919
|
}
|
|
1704
1920
|
return;
|
|
1705
1921
|
}
|
|
1922
|
+
const clickedMedia = e.target.closest('.image-container, .video-container');
|
|
1923
|
+
if (clickedMedia && (_editorRef$current6 = editorRef.current) !== null && _editorRef$current6 !== void 0 && _editorRef$current6.contains(clickedMedia) && editable) {
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1706
1926
|
|
|
1707
1927
|
// Check if the click is on a link
|
|
1708
1928
|
const clickedLink = e.target.closest('a');
|
|
@@ -1712,13 +1932,8 @@ function RichTextEditor({
|
|
|
1712
1932
|
window.open(clickedLink.href, '_blank');
|
|
1713
1933
|
return;
|
|
1714
1934
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
const clickedImg = e.target.closest('img');
|
|
1718
|
-
if (clickedImg && !clickedImg.closest('.rte-modal')) {
|
|
1719
|
-
setSelectedImage(clickedImg);
|
|
1720
|
-
} else if (!e.target.closest('.rte-image-toolbar')) {
|
|
1721
|
-
setSelectedImage(null);
|
|
1935
|
+
if (!e.target.closest('.rte-media-toolbar')) {
|
|
1936
|
+
clearMediaSelection();
|
|
1722
1937
|
}
|
|
1723
1938
|
|
|
1724
1939
|
// If disabled is true, prevent editing
|
|
@@ -1737,73 +1952,72 @@ function RichTextEditor({
|
|
|
1737
1952
|
}, 0);
|
|
1738
1953
|
}
|
|
1739
1954
|
}, [editable, disabled, editorFocused, triggerChange]);
|
|
1740
|
-
const
|
|
1741
|
-
if (!
|
|
1955
|
+
const renderMediaToolbar = () => {
|
|
1956
|
+
if (!selectedMedia || !editorRef.current || !editable) return null;
|
|
1742
1957
|
const editorRect = editorRef.current.getBoundingClientRect();
|
|
1743
|
-
const
|
|
1744
|
-
const top =
|
|
1745
|
-
const left =
|
|
1746
|
-
const width =
|
|
1958
|
+
const mediaRect = selectedMedia.getBoundingClientRect();
|
|
1959
|
+
const top = mediaRect.top - editorRect.top + editorRef.current.scrollTop;
|
|
1960
|
+
const left = mediaRect.left - editorRect.left + editorRef.current.scrollLeft;
|
|
1961
|
+
const width = mediaRect.width;
|
|
1962
|
+
const currentPercent = getMediaWidthPercent(selectedMedia);
|
|
1963
|
+
const widthPresets = [25, 50, 75, 100];
|
|
1747
1964
|
const handleAlignment = align => {
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
wrapper.classList.add(`image-align-${align}`);
|
|
1754
|
-
selectedImage.setAttribute('data-align', align);
|
|
1755
|
-
triggerChange();
|
|
1756
|
-
}
|
|
1965
|
+
selectedMedia.classList.remove("image-align-left", "image-align-center", "image-align-right");
|
|
1966
|
+
selectedMedia.classList.add(`image-align-${align}`);
|
|
1967
|
+
const img = selectedMedia.querySelector("img");
|
|
1968
|
+
if (img) img.setAttribute("data-align", align);
|
|
1969
|
+
triggerChange();
|
|
1757
1970
|
};
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
wrapper.remove();
|
|
1762
|
-
setSelectedImage(null);
|
|
1763
|
-
triggerChange();
|
|
1764
|
-
}
|
|
1971
|
+
const setWidth = percent => {
|
|
1972
|
+
applyMediaWidthPercent(selectedMedia, percent);
|
|
1973
|
+
triggerChange();
|
|
1765
1974
|
};
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
}
|
|
1775
|
-
triggerChange();
|
|
1975
|
+
const removeMedia = () => {
|
|
1976
|
+
selectedMedia.remove();
|
|
1977
|
+
clearMediaSelection();
|
|
1978
|
+
triggerChange();
|
|
1979
|
+
};
|
|
1980
|
+
const isActivePercent = percent => {
|
|
1981
|
+
if (!selectedMedia.dataset.widthPercent && !(selectedMedia.style.width || "").endsWith("%")) {
|
|
1982
|
+
return false;
|
|
1776
1983
|
}
|
|
1984
|
+
return Math.abs(currentPercent - percent) <= 3;
|
|
1777
1985
|
};
|
|
1778
1986
|
return /*#__PURE__*/React.createElement("div", {
|
|
1779
|
-
className: "rte-
|
|
1987
|
+
className: "rte-media-toolbar",
|
|
1780
1988
|
style: {
|
|
1781
|
-
position:
|
|
1782
|
-
top: Math.max(0, top -
|
|
1783
|
-
left: Math.max(
|
|
1989
|
+
position: "absolute",
|
|
1990
|
+
top: Math.max(0, top - 44),
|
|
1991
|
+
left: Math.max(8, left + width / 2 - 156),
|
|
1784
1992
|
zIndex: 1000
|
|
1785
1993
|
}
|
|
1786
1994
|
}, /*#__PURE__*/React.createElement("button", {
|
|
1787
1995
|
type: "button",
|
|
1788
|
-
onClick: () => handleAlignment(
|
|
1996
|
+
onClick: () => handleAlignment("left"),
|
|
1789
1997
|
title: "Align Left"
|
|
1790
1998
|
}, "L"), /*#__PURE__*/React.createElement("button", {
|
|
1791
1999
|
type: "button",
|
|
1792
|
-
onClick: () => handleAlignment(
|
|
2000
|
+
onClick: () => handleAlignment("center"),
|
|
1793
2001
|
title: "Align Center"
|
|
1794
2002
|
}, "C"), /*#__PURE__*/React.createElement("button", {
|
|
1795
2003
|
type: "button",
|
|
1796
|
-
onClick: () => handleAlignment(
|
|
2004
|
+
onClick: () => handleAlignment("right"),
|
|
1797
2005
|
title: "Align Right"
|
|
1798
|
-
}, "R"), /*#__PURE__*/React.createElement("
|
|
2006
|
+
}, "R"), /*#__PURE__*/React.createElement("span", {
|
|
2007
|
+
className: "rte-media-toolbar-divider"
|
|
2008
|
+
}), widthPresets.map(percent => /*#__PURE__*/React.createElement("button", {
|
|
2009
|
+
key: percent,
|
|
1799
2010
|
type: "button",
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
2011
|
+
className: isActivePercent(percent) ? "active" : "",
|
|
2012
|
+
onClick: () => setWidth(percent),
|
|
2013
|
+
title: `${percent}% width`
|
|
2014
|
+
}, percent, "%")), /*#__PURE__*/React.createElement("span", {
|
|
2015
|
+
className: "rte-media-toolbar-divider"
|
|
2016
|
+
}), /*#__PURE__*/React.createElement("button", {
|
|
1803
2017
|
type: "button",
|
|
1804
|
-
onClick:
|
|
2018
|
+
onClick: removeMedia,
|
|
1805
2019
|
className: "danger",
|
|
1806
|
-
title: "Remove
|
|
2020
|
+
title: "Remove"
|
|
1807
2021
|
}, "\xD7"));
|
|
1808
2022
|
};
|
|
1809
2023
|
if (isLoading) {
|
|
@@ -1839,7 +2053,7 @@ function RichTextEditor({
|
|
|
1839
2053
|
e.preventDefault();
|
|
1840
2054
|
e.stopPropagation();
|
|
1841
2055
|
}
|
|
1842
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
2056
|
+
}, !disabled && /*#__PURE__*/React.createElement("div", {
|
|
1843
2057
|
className: "rte-toolbar"
|
|
1844
2058
|
}, /*#__PURE__*/React.createElement("button", {
|
|
1845
2059
|
type: "button",
|
|
@@ -1887,6 +2101,41 @@ function RichTextEditor({
|
|
|
1887
2101
|
backgroundColor: '#e5e7eb',
|
|
1888
2102
|
margin: '0 4px'
|
|
1889
2103
|
}
|
|
2104
|
+
}), /*#__PURE__*/React.createElement("select", {
|
|
2105
|
+
value: currentBlockFormat,
|
|
2106
|
+
onMouseDown: e => e.stopPropagation(),
|
|
2107
|
+
onChange: e => {
|
|
2108
|
+
e.preventDefault();
|
|
2109
|
+
e.stopPropagation();
|
|
2110
|
+
applyBlockFormat(e.target.value);
|
|
2111
|
+
},
|
|
2112
|
+
className: "rte-toolbar-select rte-heading-select",
|
|
2113
|
+
title: "Text style"
|
|
2114
|
+
}, /*#__PURE__*/React.createElement("option", {
|
|
2115
|
+
value: "div"
|
|
2116
|
+
}, "Paragraph"), /*#__PURE__*/React.createElement("option", {
|
|
2117
|
+
value: "h1"
|
|
2118
|
+
}, "Heading 1"), /*#__PURE__*/React.createElement("option", {
|
|
2119
|
+
value: "h2"
|
|
2120
|
+
}, "Heading 2"), /*#__PURE__*/React.createElement("option", {
|
|
2121
|
+
value: "h3"
|
|
2122
|
+
}, "Heading 3"), /*#__PURE__*/React.createElement("option", {
|
|
2123
|
+
value: "blockquote"
|
|
2124
|
+
}, "Quote")), /*#__PURE__*/React.createElement("button", {
|
|
2125
|
+
type: "button",
|
|
2126
|
+
title: "Clear Formatting",
|
|
2127
|
+
className: "rte-toolbar-button rte-toolbar-button-text",
|
|
2128
|
+
onMouseDown: e => {
|
|
2129
|
+
e.preventDefault();
|
|
2130
|
+
clearFormatting();
|
|
2131
|
+
}
|
|
2132
|
+
}, "Tx"), /*#__PURE__*/React.createElement("div", {
|
|
2133
|
+
style: {
|
|
2134
|
+
width: '1px',
|
|
2135
|
+
height: '20px',
|
|
2136
|
+
backgroundColor: '#e5e7eb',
|
|
2137
|
+
margin: '0 4px'
|
|
2138
|
+
}
|
|
1890
2139
|
}), /*#__PURE__*/React.createElement("select", {
|
|
1891
2140
|
value: currentFontSize,
|
|
1892
2141
|
onMouseDown: e => e.stopPropagation(),
|
|
@@ -2053,11 +2302,9 @@ function RichTextEditor({
|
|
|
2053
2302
|
e.preventDefault();
|
|
2054
2303
|
addLink();
|
|
2055
2304
|
}
|
|
2056
|
-
}, /*#__PURE__*/React.createElement(
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
}
|
|
2060
|
-
}, "\uD83D\uDD17")), /*#__PURE__*/React.createElement("input", {
|
|
2305
|
+
}, /*#__PURE__*/React.createElement(FaLink, {
|
|
2306
|
+
size: 14
|
|
2307
|
+
})), /*#__PURE__*/React.createElement("input", {
|
|
2061
2308
|
ref: fileInputRef,
|
|
2062
2309
|
type: "file",
|
|
2063
2310
|
accept: "image/*",
|
|
@@ -2224,9 +2471,17 @@ function RichTextEditor({
|
|
|
2224
2471
|
}
|
|
2225
2472
|
return null;
|
|
2226
2473
|
})()), /*#__PURE__*/React.createElement("div", {
|
|
2474
|
+
className: "rte-content-wrapper",
|
|
2475
|
+
style: {
|
|
2476
|
+
position: 'relative'
|
|
2477
|
+
}
|
|
2478
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
2227
2479
|
ref: editorRef,
|
|
2228
2480
|
contentEditable: editable && disabled !== true,
|
|
2229
2481
|
suppressContentEditableWarning: true,
|
|
2482
|
+
role: "textbox",
|
|
2483
|
+
"aria-multiline": "true",
|
|
2484
|
+
"aria-label": label || "Rich text editor",
|
|
2230
2485
|
onInput: handleInput,
|
|
2231
2486
|
onPaste: handlePaste,
|
|
2232
2487
|
onDrop: handleDrop,
|
|
@@ -2242,7 +2497,13 @@ function RichTextEditor({
|
|
|
2242
2497
|
paddingLeft: paddingLeft || '12px'
|
|
2243
2498
|
},
|
|
2244
2499
|
className: `rte-content${editable ? " rte-is-editable" : ""}${editorFocused ? " rte-is-focused" : ""}`
|
|
2245
|
-
}),
|
|
2500
|
+
}), isEmpty && editable && disabled !== true && /*#__PURE__*/React.createElement("div", {
|
|
2501
|
+
className: "rte-placeholder",
|
|
2502
|
+
style: {
|
|
2503
|
+
left: paddingLeft || '12px'
|
|
2504
|
+
},
|
|
2505
|
+
"aria-hidden": "true"
|
|
2506
|
+
}, placeholder)), renderMediaToolbar(), /*#__PURE__*/React.createElement("div", {
|
|
2246
2507
|
className: "rte-footer"
|
|
2247
2508
|
}, /*#__PURE__*/React.createElement("div", {
|
|
2248
2509
|
className: "rte-footer-content"
|
|
@@ -2258,13 +2519,11 @@ function RichTextEditor({
|
|
|
2258
2519
|
}, /*#__PURE__*/React.createElement("div", {
|
|
2259
2520
|
className: "rte-modal",
|
|
2260
2521
|
onClick: e => e.stopPropagation()
|
|
2522
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
2523
|
+
className: "rte-modal-header"
|
|
2261
2524
|
}, /*#__PURE__*/React.createElement("h3", {
|
|
2262
2525
|
className: "rte-modal-title"
|
|
2263
|
-
}, "Insert Link"), /*#__PURE__*/React.createElement("div", {
|
|
2264
|
-
className: "rte-modal-divider"
|
|
2265
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
2266
|
-
className: "rte-modal-body"
|
|
2267
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
2526
|
+
}, "Insert Link")), /*#__PURE__*/React.createElement("div", {
|
|
2268
2527
|
className: "rte-form-group"
|
|
2269
2528
|
}, /*#__PURE__*/React.createElement("label", {
|
|
2270
2529
|
className: "rte-label"
|
|
@@ -2286,20 +2545,15 @@ function RichTextEditor({
|
|
|
2286
2545
|
onChange: e => setLinkUrl(e.target.value),
|
|
2287
2546
|
onKeyDown: e => e.key === 'Enter' && confirmLink(),
|
|
2288
2547
|
autoFocus: true
|
|
2289
|
-
}))
|
|
2290
|
-
className: "rte-modal-
|
|
2291
|
-
style: {
|
|
2292
|
-
margin: '8px 0 20px 0'
|
|
2293
|
-
}
|
|
2294
|
-
}), /*#__PURE__*/React.createElement("div", {
|
|
2295
|
-
className: "rte-modal-footer"
|
|
2548
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
2549
|
+
className: "rte-modal-actions"
|
|
2296
2550
|
}, /*#__PURE__*/React.createElement("button", {
|
|
2297
2551
|
type: "button",
|
|
2298
|
-
className: "rte-
|
|
2552
|
+
className: "rte-button rte-button-secondary",
|
|
2299
2553
|
onClick: cancelLink
|
|
2300
2554
|
}, "Cancel"), /*#__PURE__*/React.createElement("button", {
|
|
2301
2555
|
type: "button",
|
|
2302
|
-
className: "rte-
|
|
2556
|
+
className: "rte-button rte-button-primary",
|
|
2303
2557
|
onClick: confirmLink,
|
|
2304
2558
|
disabled: !linkUrl
|
|
2305
2559
|
}, "Insert")))), tableModalOpen && /*#__PURE__*/React.createElement("div", {
|