react-lite-rich-text-editor 1.1.7 → 1.1.9
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/dist/index.cjs.js +548 -44
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +548 -44
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -411,7 +411,7 @@ function styleInject(css, ref) {
|
|
|
411
411
|
}
|
|
412
412
|
}
|
|
413
413
|
|
|
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}";
|
|
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-bg-color-picker-label .rte-bg-color-swatch{align-items:center;border:1px solid #d1d5db;border-radius:4px;display:inline-flex;font-size:11px;font-weight:700;height:18px;justify-content:center;line-height:1;pointer-events:none;width:18px}.rte-media-width-custom{align-items:center;display:flex;gap:2px}.rte-media-width-input{border:1px solid #e5e7eb;border-radius:4px;color:#374151;font-size:11px;height:28px;outline:none;padding:0 6px;width:56px}.rte-media-width-input:focus{border-color:#3b82f6}.rte-media-width-unit{background:#fff;border:1px solid #e5e7eb;border-radius:4px;color:#374151;cursor:pointer;font-size:11px;height:28px;outline:none;padding:0 4px;width:44px}.rte-media-width-apply{color:#2563eb!important;font-size:13px!important;font-weight:700;height:28px;min-width:28px!important;padding:0 6px!important}.rte-media-width-apply:hover{background-color:#eff6ff!important}.rte-area-highlight-mode .rte-content{cursor:crosshair}.rte-area-highlight-block{box-decoration-break:clone;-webkit-box-decoration-break:clone;box-sizing:border-box;display:block}.rte-area-highlight-hint{background:rgba(37,99,235,.92);border-radius:999px;color:#fff;font-size:11px;font-weight:600;left:50%;padding:4px 10px;pointer-events:none;position:absolute;top:8px;transform:translateX(-50%);white-space:nowrap;z-index:5}.rte-marquee-preview{border:2px dashed;border-radius:2px;box-sizing:border-box;pointer-events:none;position:absolute;z-index:6}.rte-area-highlight-toggle{min-width:32px;padding:0 6px!important;width:auto!important}.rte-area-highlight-icon{border:2px dashed;border-radius:2px;box-sizing:border-box;display:block;height:14px;width:14px}.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{box-sizing:border-box;display:block;line-height:0;margin:16px 0;max-width:100%;position:relative}.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-container[data-width-px] .image-media-frame,.image-container[data-width-px] .image-media-frame img{height:auto;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;flex-wrap:wrap;gap:2px;max-width:calc(100% - 16px);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}";
|
|
415
415
|
styleInject(css_248z);
|
|
416
416
|
|
|
417
417
|
// Helper functions for HTML escaping
|
|
@@ -423,6 +423,108 @@ const escapeAttr = str => escapeHtml(str).replace(/"/g, """);
|
|
|
423
423
|
|
|
424
424
|
// URL detection regex
|
|
425
425
|
const URL_REGEX = /(https?:\/\/[^\s]+)/g;
|
|
426
|
+
const rectsIntersect = (a, b) => !(a.right < b.left || a.left > b.right || a.bottom < b.top || a.top > b.bottom);
|
|
427
|
+
const caretRangeFromClientPoint = (x, y) => {
|
|
428
|
+
var _document$caretPositi, _document;
|
|
429
|
+
if (document.caretRangeFromPoint) {
|
|
430
|
+
return document.caretRangeFromPoint(x, y);
|
|
431
|
+
}
|
|
432
|
+
const position = (_document$caretPositi = (_document = document).caretPositionFromPoint) === null || _document$caretPositi === void 0 ? void 0 : _document$caretPositi.call(_document, x, y);
|
|
433
|
+
if (!position) return null;
|
|
434
|
+
const range = document.createRange();
|
|
435
|
+
range.setStart(position.offsetNode, position.offset);
|
|
436
|
+
range.collapse(true);
|
|
437
|
+
return range;
|
|
438
|
+
};
|
|
439
|
+
const clipRectToBounds = (rect, bounds) => {
|
|
440
|
+
const left = Math.max(rect.left, bounds.left);
|
|
441
|
+
const top = Math.max(rect.top, bounds.top);
|
|
442
|
+
const right = Math.min(rect.right, bounds.right);
|
|
443
|
+
const bottom = Math.min(rect.bottom, bounds.bottom);
|
|
444
|
+
return {
|
|
445
|
+
left,
|
|
446
|
+
top,
|
|
447
|
+
right,
|
|
448
|
+
bottom,
|
|
449
|
+
width: Math.max(0, right - left),
|
|
450
|
+
height: Math.max(0, bottom - top)
|
|
451
|
+
};
|
|
452
|
+
};
|
|
453
|
+
const BLOCK_SELECTOR = "h1, h2, h3, h4, h5, h6, p, blockquote, li, td, th, div:not(.rte-area-highlight-block):not(.image-container):not(.video-container):not(.image-media-frame)";
|
|
454
|
+
const getIntersectingBlocks = (editor, clientRect) => {
|
|
455
|
+
const candidates = [...editor.querySelectorAll(BLOCK_SELECTOR)].filter(el => {
|
|
456
|
+
if (el === editor || !editor.contains(el)) return false;
|
|
457
|
+
if (el.closest(".rte-area-highlight-block, .image-container, .video-container")) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
return rectsIntersect(el.getBoundingClientRect(), clientRect);
|
|
461
|
+
});
|
|
462
|
+
return candidates.filter(el => !candidates.some(other => other !== el && other.contains(el)));
|
|
463
|
+
};
|
|
464
|
+
const insertAreaHighlightRegion = (editor, clientRect, color) => {
|
|
465
|
+
const editorRect = editor.getBoundingClientRect();
|
|
466
|
+
const clipped = clipRectToBounds(clientRect, editorRect);
|
|
467
|
+
const widthPx = Math.max(40, Math.round(clipped.width));
|
|
468
|
+
const heightPx = Math.max(24, Math.round(clipped.height));
|
|
469
|
+
const region = document.createElement("div");
|
|
470
|
+
region.className = "rte-area-highlight-block";
|
|
471
|
+
region.style.backgroundColor = color;
|
|
472
|
+
region.style.minHeight = `${heightPx}px`;
|
|
473
|
+
region.style.width = `${widthPx}px`;
|
|
474
|
+
region.style.maxWidth = "100%";
|
|
475
|
+
region.style.boxSizing = "border-box";
|
|
476
|
+
region.style.padding = "12px";
|
|
477
|
+
region.style.borderRadius = "4px";
|
|
478
|
+
region.style.margin = "8px 0";
|
|
479
|
+
region.style.display = "block";
|
|
480
|
+
const blocks = getIntersectingBlocks(editor, clipped);
|
|
481
|
+
if (blocks.length > 0) {
|
|
482
|
+
const first = blocks[0];
|
|
483
|
+
if (first.parentNode) {
|
|
484
|
+
first.parentNode.insertBefore(region, first);
|
|
485
|
+
} else {
|
|
486
|
+
editor.appendChild(region);
|
|
487
|
+
}
|
|
488
|
+
blocks.forEach(block => {
|
|
489
|
+
block.style.backgroundColor = "";
|
|
490
|
+
block.style.borderRadius = "";
|
|
491
|
+
region.appendChild(block);
|
|
492
|
+
});
|
|
493
|
+
return true;
|
|
494
|
+
}
|
|
495
|
+
const insertRange = caretRangeFromClientPoint(clipped.left + clipped.width / 2, clipped.top + Math.min(8, clipped.height / 2)) || caretRangeFromClientPoint(clipped.left + 4, clipped.top + 4);
|
|
496
|
+
region.innerHTML = " ";
|
|
497
|
+
if (insertRange && editor.contains(insertRange.startContainer)) {
|
|
498
|
+
insertRange.collapse(true);
|
|
499
|
+
let node = insertRange.startContainer;
|
|
500
|
+
if (node.nodeType === 3) node = node.parentNode;
|
|
501
|
+
while (node && node !== editor && node.parentNode !== editor) {
|
|
502
|
+
node = node.parentNode;
|
|
503
|
+
}
|
|
504
|
+
if (node && node !== editor && editor.contains(node)) {
|
|
505
|
+
node.parentNode.insertBefore(region, node);
|
|
506
|
+
} else {
|
|
507
|
+
editor.appendChild(region);
|
|
508
|
+
}
|
|
509
|
+
} else {
|
|
510
|
+
editor.appendChild(region);
|
|
511
|
+
}
|
|
512
|
+
return true;
|
|
513
|
+
};
|
|
514
|
+
const getClientRectFromDrag = (startX, startY, currentX, currentY) => {
|
|
515
|
+
const left = Math.min(startX, currentX);
|
|
516
|
+
const top = Math.min(startY, currentY);
|
|
517
|
+
const right = Math.max(startX, currentX);
|
|
518
|
+
const bottom = Math.max(startY, currentY);
|
|
519
|
+
return {
|
|
520
|
+
left,
|
|
521
|
+
top,
|
|
522
|
+
right,
|
|
523
|
+
bottom,
|
|
524
|
+
width: right - left,
|
|
525
|
+
height: bottom - top
|
|
526
|
+
};
|
|
527
|
+
};
|
|
426
528
|
function RichTextEditor({
|
|
427
529
|
onChange,
|
|
428
530
|
showEditButton,
|
|
@@ -441,8 +543,11 @@ function RichTextEditor({
|
|
|
441
543
|
onImageUpload
|
|
442
544
|
}) {
|
|
443
545
|
const editorRef = React.useRef(null);
|
|
546
|
+
const contentWrapperRef = React.useRef(null);
|
|
444
547
|
const fileInputRef = React.useRef(null);
|
|
445
548
|
const scrollTopRef = React.useRef(0);
|
|
549
|
+
const areaDragRef = React.useRef(null);
|
|
550
|
+
const bgColorRef = React.useRef("#ffff00");
|
|
446
551
|
const [html, setHtml] = React.useState("");
|
|
447
552
|
const [linkModalOpen, setLinkModalOpen] = React.useState(false);
|
|
448
553
|
const [linkUrl, setLinkUrl] = React.useState("");
|
|
@@ -483,11 +588,15 @@ function RichTextEditor({
|
|
|
483
588
|
const [tableCols, setTableCols] = React.useState(3);
|
|
484
589
|
const [selectionVersion, setSelectionVersion] = React.useState(0);
|
|
485
590
|
const [selectedMedia, setSelectedMedia] = React.useState(null);
|
|
591
|
+
const [mediaWidthInput, setMediaWidthInput] = React.useState("100");
|
|
592
|
+
const [mediaWidthUnit, setMediaWidthUnit] = React.useState("%");
|
|
486
593
|
const [metrics, setMetrics] = React.useState({
|
|
487
594
|
words: 0,
|
|
488
595
|
chars: 0
|
|
489
596
|
});
|
|
490
597
|
const [isEmpty, setIsEmpty] = React.useState(!value);
|
|
598
|
+
const [areaHighlightMode, setAreaHighlightMode] = React.useState(false);
|
|
599
|
+
const [marqueePreview, setMarqueePreview] = React.useState(null);
|
|
491
600
|
const updateMetrics = React.useCallback(() => {
|
|
492
601
|
if (!editorRef.current) return;
|
|
493
602
|
// Calculate metrics immediately but outside of render path
|
|
@@ -524,6 +633,10 @@ function RichTextEditor({
|
|
|
524
633
|
selectionRangeRef.current = sel.getRangeAt(0).cloneRange();
|
|
525
634
|
}
|
|
526
635
|
};
|
|
636
|
+
const handleKeyUp = () => {
|
|
637
|
+
saveSelection();
|
|
638
|
+
setSelectionVersion(v => v + 1);
|
|
639
|
+
};
|
|
527
640
|
const handleZoomIn = () => {
|
|
528
641
|
setZoomLevel(prevZoom => prevZoom + 0.1);
|
|
529
642
|
};
|
|
@@ -773,9 +886,31 @@ function RichTextEditor({
|
|
|
773
886
|
}
|
|
774
887
|
return 100;
|
|
775
888
|
};
|
|
889
|
+
const getMediaWidthPx = container => {
|
|
890
|
+
if (!container) return 0;
|
|
891
|
+
if (container.dataset.widthPx) {
|
|
892
|
+
return Number(container.dataset.widthPx);
|
|
893
|
+
}
|
|
894
|
+
const width = container.style.width || "";
|
|
895
|
+
if (width.endsWith("px")) {
|
|
896
|
+
return parseInt(width, 10) || 0;
|
|
897
|
+
}
|
|
898
|
+
return Math.round(container.getBoundingClientRect().width);
|
|
899
|
+
};
|
|
900
|
+
const syncMediaWidthControls = container => {
|
|
901
|
+
if (!container) return;
|
|
902
|
+
if (container.dataset.widthPx) {
|
|
903
|
+
setMediaWidthInput(String(getMediaWidthPx(container)));
|
|
904
|
+
setMediaWidthUnit("px");
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
setMediaWidthInput(String(getMediaWidthPercent(container)));
|
|
908
|
+
setMediaWidthUnit("%");
|
|
909
|
+
};
|
|
776
910
|
const applyMediaWidthPercent = (container, percent) => {
|
|
777
911
|
if (!container) return;
|
|
778
|
-
const clamped = Math.max(
|
|
912
|
+
const clamped = Math.max(10, Math.min(100, Math.round(percent)));
|
|
913
|
+
delete container.dataset.widthPx;
|
|
779
914
|
container.dataset.widthPercent = String(clamped);
|
|
780
915
|
container.style.width = `${clamped}%`;
|
|
781
916
|
container.style.maxWidth = "100%";
|
|
@@ -796,8 +931,36 @@ function RichTextEditor({
|
|
|
796
931
|
img.style.objectFit = "";
|
|
797
932
|
}
|
|
798
933
|
};
|
|
934
|
+
const applyMediaWidthPx = (container, px) => {
|
|
935
|
+
if (!container) return;
|
|
936
|
+
const clamped = Math.max(20, Math.min(2000, Math.round(px)));
|
|
937
|
+
delete container.dataset.widthPercent;
|
|
938
|
+
container.dataset.widthPx = String(clamped);
|
|
939
|
+
container.style.width = `${clamped}px`;
|
|
940
|
+
container.style.maxWidth = "100%";
|
|
941
|
+
container.style.marginLeft = "";
|
|
942
|
+
container.style.marginTop = "";
|
|
943
|
+
if (container.classList.contains("video-container")) {
|
|
944
|
+
container.style.height = "0";
|
|
945
|
+
container.style.paddingBottom = "56.25%";
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
const frame = getImageMediaTarget(container);
|
|
949
|
+
if (!frame) return;
|
|
950
|
+
frame.style.width = "100%";
|
|
951
|
+
frame.style.height = "";
|
|
952
|
+
const img = frame.querySelector("img");
|
|
953
|
+
if (img) {
|
|
954
|
+
img.style.height = "auto";
|
|
955
|
+
img.style.objectFit = "";
|
|
956
|
+
}
|
|
957
|
+
};
|
|
799
958
|
const normalizeMediaWidth = container => {
|
|
800
959
|
if (!container) return;
|
|
960
|
+
if (container.dataset.widthPx) {
|
|
961
|
+
applyMediaWidthPx(container, Number(container.dataset.widthPx));
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
801
964
|
if (container.classList.contains("image-small")) {
|
|
802
965
|
container.classList.remove("image-small");
|
|
803
966
|
applyMediaWidthPercent(container, 50);
|
|
@@ -813,11 +976,7 @@ function RichTextEditor({
|
|
|
813
976
|
return;
|
|
814
977
|
}
|
|
815
978
|
if (width.endsWith("px")) {
|
|
816
|
-
|
|
817
|
-
const px = parseFloat(width);
|
|
818
|
-
if (editorWidth > 0 && px > 0) {
|
|
819
|
-
applyMediaWidthPercent(container, Math.round(px / editorWidth * 100));
|
|
820
|
-
}
|
|
979
|
+
applyMediaWidthPx(container, parseFloat(width));
|
|
821
980
|
return;
|
|
822
981
|
}
|
|
823
982
|
if (container.classList.contains("video-container")) {
|
|
@@ -860,12 +1019,18 @@ function RichTextEditor({
|
|
|
860
1019
|
const startWidth = container.getBoundingClientRect().width;
|
|
861
1020
|
const onMouseMove = moveEvent => {
|
|
862
1021
|
const nextWidth = Math.max(60, startWidth + (moveEvent.clientX - startX));
|
|
863
|
-
|
|
864
|
-
|
|
1022
|
+
if (container.dataset.widthPx) {
|
|
1023
|
+
applyMediaWidthPx(container, nextWidth);
|
|
1024
|
+
} else {
|
|
1025
|
+
const percent = Math.round(nextWidth / editorWidth * 100);
|
|
1026
|
+
applyMediaWidthPercent(container, percent);
|
|
1027
|
+
}
|
|
865
1028
|
};
|
|
866
1029
|
const onMouseUp = () => {
|
|
867
1030
|
document.removeEventListener("mousemove", onMouseMove);
|
|
868
1031
|
document.removeEventListener("mouseup", onMouseUp);
|
|
1032
|
+
syncMediaWidthControls(container);
|
|
1033
|
+
setSelectionVersion(v => v + 1);
|
|
869
1034
|
triggerChange();
|
|
870
1035
|
};
|
|
871
1036
|
document.addEventListener("mousemove", onMouseMove);
|
|
@@ -992,11 +1157,118 @@ function RichTextEditor({
|
|
|
992
1157
|
focus();
|
|
993
1158
|
};
|
|
994
1159
|
const [fontColor, setFontColor] = React.useState("#000000");
|
|
1160
|
+
const [bgColor, setBgColor] = React.useState("#ffff00");
|
|
1161
|
+
React.useEffect(() => {
|
|
1162
|
+
bgColorRef.current = bgColor;
|
|
1163
|
+
}, [bgColor]);
|
|
995
1164
|
const getActiveTextColor = () => getColorAtCursor() || fontColor;
|
|
996
|
-
const
|
|
1165
|
+
const restoreSavedSelection = () => {
|
|
1166
|
+
if (!selectionRangeRef.current) return;
|
|
1167
|
+
const sel = window.getSelection();
|
|
1168
|
+
if (!sel) return;
|
|
1169
|
+
sel.removeAllRanges();
|
|
1170
|
+
sel.addRange(selectionRangeRef.current);
|
|
1171
|
+
};
|
|
1172
|
+
const applyTextColor = color => {
|
|
997
1173
|
setFontColor(color);
|
|
998
|
-
|
|
1174
|
+
focus();
|
|
1175
|
+
restoreSavedSelection();
|
|
1176
|
+
document.execCommand("styleWithCSS", false, true);
|
|
1177
|
+
document.execCommand("foreColor", false, color);
|
|
1178
|
+
document.execCommand("styleWithCSS", false, false);
|
|
1179
|
+
triggerChange();
|
|
1180
|
+
};
|
|
1181
|
+
const applyBackgroundColor = color => {
|
|
1182
|
+
setBgColor(color);
|
|
1183
|
+
focus();
|
|
1184
|
+
restoreSavedSelection();
|
|
1185
|
+
document.execCommand("styleWithCSS", false, true);
|
|
1186
|
+
if (!document.execCommand("hiliteColor", false, color)) {
|
|
1187
|
+
document.execCommand("backColor", false, color);
|
|
1188
|
+
}
|
|
1189
|
+
document.execCommand("styleWithCSS", false, false);
|
|
1190
|
+
triggerChange();
|
|
999
1191
|
};
|
|
1192
|
+
const applyMarqueeHighlight = React.useCallback((clientRect, color) => {
|
|
1193
|
+
const editor = editorRef.current;
|
|
1194
|
+
if (!editor || clientRect.width < 10 || clientRect.height < 10) {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
editor.focus();
|
|
1198
|
+
const editorRect = editor.getBoundingClientRect();
|
|
1199
|
+
const clippedRect = clipRectToBounds(clientRect, editorRect);
|
|
1200
|
+
if (clippedRect.width < 10 || clippedRect.height < 10) {
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
insertAreaHighlightRegion(editor, clippedRect, color);
|
|
1204
|
+
editor.querySelectorAll("td, th").forEach(cell => {
|
|
1205
|
+
if (!editor.contains(cell)) return;
|
|
1206
|
+
if (cell.closest(".rte-area-highlight-block")) return;
|
|
1207
|
+
if (rectsIntersect(cell.getBoundingClientRect(), clippedRect)) {
|
|
1208
|
+
cell.style.backgroundColor = color;
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
editor.querySelectorAll(".image-container, .video-container").forEach(media => {
|
|
1212
|
+
if (!editor.contains(media)) return;
|
|
1213
|
+
if (rectsIntersect(media.getBoundingClientRect(), clippedRect)) {
|
|
1214
|
+
media.style.backgroundColor = color;
|
|
1215
|
+
media.style.padding = "4px";
|
|
1216
|
+
media.style.borderRadius = "4px";
|
|
1217
|
+
}
|
|
1218
|
+
});
|
|
1219
|
+
updateMetrics();
|
|
1220
|
+
triggerChange();
|
|
1221
|
+
focus();
|
|
1222
|
+
}, [triggerChange, updateMetrics]);
|
|
1223
|
+
const handleAreaSelectMouseDown = React.useCallback(e => {
|
|
1224
|
+
if (!areaHighlightMode || !editable || disabled) return;
|
|
1225
|
+
if (e.button !== 0) return;
|
|
1226
|
+
if (e.target.closest(".rte-toolbar, .rte-media-toolbar, .rte-modal-overlay")) return;
|
|
1227
|
+
e.preventDefault();
|
|
1228
|
+
e.stopPropagation();
|
|
1229
|
+
const wrapper = contentWrapperRef.current;
|
|
1230
|
+
if (!wrapper) return;
|
|
1231
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
1232
|
+
areaDragRef.current = {
|
|
1233
|
+
startX: e.clientX,
|
|
1234
|
+
startY: e.clientY,
|
|
1235
|
+
currentX: e.clientX,
|
|
1236
|
+
currentY: e.clientY,
|
|
1237
|
+
wrapperRect
|
|
1238
|
+
};
|
|
1239
|
+
const updatePreview = (clientX, clientY) => {
|
|
1240
|
+
const drag = areaDragRef.current;
|
|
1241
|
+
if (!drag) return;
|
|
1242
|
+
drag.currentX = clientX;
|
|
1243
|
+
drag.currentY = clientY;
|
|
1244
|
+
const rect = getClientRectFromDrag(drag.startX, drag.startY, drag.currentX, drag.currentY);
|
|
1245
|
+
setMarqueePreview({
|
|
1246
|
+
left: rect.left - drag.wrapperRect.left,
|
|
1247
|
+
top: rect.top - drag.wrapperRect.top,
|
|
1248
|
+
width: rect.width,
|
|
1249
|
+
height: rect.height
|
|
1250
|
+
});
|
|
1251
|
+
};
|
|
1252
|
+
const onMove = ev => {
|
|
1253
|
+
ev.preventDefault();
|
|
1254
|
+
updatePreview(ev.clientX, ev.clientY);
|
|
1255
|
+
};
|
|
1256
|
+
const onUp = ev => {
|
|
1257
|
+
document.removeEventListener("mousemove", onMove);
|
|
1258
|
+
document.removeEventListener("mouseup", onUp);
|
|
1259
|
+
const drag = areaDragRef.current;
|
|
1260
|
+
areaDragRef.current = null;
|
|
1261
|
+
setMarqueePreview(null);
|
|
1262
|
+
if (!drag) return;
|
|
1263
|
+
const clientRect = getClientRectFromDrag(drag.startX, drag.startY, ev.clientX, ev.clientY);
|
|
1264
|
+
requestAnimationFrame(() => {
|
|
1265
|
+
applyMarqueeHighlight(clientRect, bgColorRef.current);
|
|
1266
|
+
});
|
|
1267
|
+
};
|
|
1268
|
+
document.addEventListener("mousemove", onMove);
|
|
1269
|
+
document.addEventListener("mouseup", onUp);
|
|
1270
|
+
updatePreview(e.clientX, e.clientY);
|
|
1271
|
+
}, [areaHighlightMode, editable, disabled, applyMarqueeHighlight]);
|
|
1000
1272
|
const addLink = () => {
|
|
1001
1273
|
const sel = window.getSelection();
|
|
1002
1274
|
if (!sel || sel.rangeCount === 0) return;
|
|
@@ -1250,9 +1522,25 @@ function RichTextEditor({
|
|
|
1250
1522
|
if (frame && !frame.querySelector(".image-delete-button")) {
|
|
1251
1523
|
frame.appendChild(createMediaDeleteButton("Remove image", "image-delete-button", () => {
|
|
1252
1524
|
existingWrapper.remove();
|
|
1525
|
+
clearMediaSelection();
|
|
1253
1526
|
triggerChange && triggerChange();
|
|
1254
1527
|
}));
|
|
1255
1528
|
}
|
|
1529
|
+
if (!existingWrapper.dataset.mediaEnhanced) {
|
|
1530
|
+
existingWrapper.dataset.mediaEnhanced = "true";
|
|
1531
|
+
existingWrapper.addEventListener("click", event => {
|
|
1532
|
+
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1533
|
+
event.preventDefault();
|
|
1534
|
+
event.stopPropagation();
|
|
1535
|
+
selectMediaContainer(existingWrapper);
|
|
1536
|
+
});
|
|
1537
|
+
img.addEventListener("dblclick", event => {
|
|
1538
|
+
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1539
|
+
event.preventDefault();
|
|
1540
|
+
event.stopPropagation();
|
|
1541
|
+
openImageModal(img.src);
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1256
1544
|
attachMediaResizeHandle(existingWrapper);
|
|
1257
1545
|
normalizeMediaWidth(existingWrapper);
|
|
1258
1546
|
updateMediaControlVisibility(existingWrapper);
|
|
@@ -1330,14 +1618,33 @@ function RichTextEditor({
|
|
|
1330
1618
|
event.stopPropagation();
|
|
1331
1619
|
selectMediaContainer(container);
|
|
1332
1620
|
});
|
|
1621
|
+
container.style.cursor = editable ? "pointer" : "default";
|
|
1622
|
+
container.dataset.mediaEnhanced = "true";
|
|
1623
|
+
container.addEventListener("click", event => {
|
|
1624
|
+
if (event.target.closest(".image-delete-button, .media-resize-handle")) return;
|
|
1625
|
+
event.preventDefault();
|
|
1626
|
+
event.stopPropagation();
|
|
1627
|
+
selectMediaContainer(container);
|
|
1628
|
+
});
|
|
1629
|
+
const deleteBtn = createMediaDeleteButton("Remove image", "image-delete-button", () => {
|
|
1630
|
+
container.remove();
|
|
1631
|
+
clearMediaSelection();
|
|
1632
|
+
triggerChange();
|
|
1633
|
+
});
|
|
1333
1634
|
frame.appendChild(img);
|
|
1635
|
+
frame.appendChild(deleteBtn);
|
|
1334
1636
|
container.appendChild(frame);
|
|
1335
|
-
|
|
1637
|
+
attachMediaResizeHandle(container);
|
|
1638
|
+
applyMediaWidthPercent(container, 50);
|
|
1336
1639
|
|
|
1337
1640
|
// Insert at cursor position
|
|
1338
1641
|
insertNodeAtCursor(container);
|
|
1339
1642
|
requestAnimationFrame(() => {
|
|
1643
|
+
var _editorRef$current5;
|
|
1340
1644
|
processExistingMedia(editorRef.current);
|
|
1645
|
+
selectMediaContainer(container);
|
|
1646
|
+
syncMediaWidthControls(container);
|
|
1647
|
+
(_editorRef$current5 = editorRef.current) === null || _editorRef$current5 === void 0 || _editorRef$current5.focus();
|
|
1341
1648
|
triggerChange();
|
|
1342
1649
|
});
|
|
1343
1650
|
}
|
|
@@ -1505,6 +1812,28 @@ function RichTextEditor({
|
|
|
1505
1812
|
selection.addRange(newRange);
|
|
1506
1813
|
};
|
|
1507
1814
|
const handleKeyDown = React.useCallback(e => {
|
|
1815
|
+
var _editorRef$current6;
|
|
1816
|
+
if (e.key === "Escape") {
|
|
1817
|
+
if (areaHighlightMode) {
|
|
1818
|
+
e.preventDefault();
|
|
1819
|
+
areaDragRef.current = null;
|
|
1820
|
+
setMarqueePreview(null);
|
|
1821
|
+
setAreaHighlightMode(false);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
if (selectedMedia) {
|
|
1825
|
+
e.preventDefault();
|
|
1826
|
+
clearMediaSelection();
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
if ((e.key === "Delete" || e.key === "Backspace") && selectedMedia !== null && selectedMedia !== void 0 && selectedMedia.classList.contains("rte-media-selected") && (_editorRef$current6 = editorRef.current) !== null && _editorRef$current6 !== void 0 && _editorRef$current6.contains(selectedMedia)) {
|
|
1831
|
+
e.preventDefault();
|
|
1832
|
+
selectedMedia.remove();
|
|
1833
|
+
clearMediaSelection();
|
|
1834
|
+
triggerChange();
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1508
1837
|
if (applyMarkdownShortcut(e)) return;
|
|
1509
1838
|
|
|
1510
1839
|
// Handle Enter key
|
|
@@ -1578,7 +1907,7 @@ function RichTextEditor({
|
|
|
1578
1907
|
return;
|
|
1579
1908
|
}
|
|
1580
1909
|
if (e.key === "Backspace") {
|
|
1581
|
-
var _node$closest, _node, _editorRef$
|
|
1910
|
+
var _node$closest, _node, _editorRef$current7;
|
|
1582
1911
|
const selection = window.getSelection();
|
|
1583
1912
|
if (!(selection !== null && selection !== void 0 && selection.rangeCount)) return;
|
|
1584
1913
|
const range = selection.getRangeAt(0);
|
|
@@ -1588,7 +1917,7 @@ function RichTextEditor({
|
|
|
1588
1917
|
node = node.parentNode;
|
|
1589
1918
|
}
|
|
1590
1919
|
const listItem = (_node$closest = (_node = node).closest) === null || _node$closest === void 0 ? void 0 : _node$closest.call(_node, "li");
|
|
1591
|
-
if (!listItem || !((_editorRef$
|
|
1920
|
+
if (!listItem || !((_editorRef$current7 = editorRef.current) !== null && _editorRef$current7 !== void 0 && _editorRef$current7.contains(listItem))) return;
|
|
1592
1921
|
const list = listItem.parentNode;
|
|
1593
1922
|
if (isListItemEffectivelyEmpty(listItem)) {
|
|
1594
1923
|
e.preventDefault();
|
|
@@ -1637,7 +1966,7 @@ function RichTextEditor({
|
|
|
1637
1966
|
e.preventDefault();
|
|
1638
1967
|
exec("underline");
|
|
1639
1968
|
}
|
|
1640
|
-
}, [exec, triggerChange, fontColor]);
|
|
1969
|
+
}, [exec, triggerChange, fontColor, areaHighlightMode, selectedMedia]);
|
|
1641
1970
|
const confirmLink = () => {
|
|
1642
1971
|
// Add protocol if missing
|
|
1643
1972
|
let url = linkUrl.trim();
|
|
@@ -1679,6 +2008,11 @@ function RichTextEditor({
|
|
|
1679
2008
|
setCurrentFontSize("16");
|
|
1680
2009
|
setCurrentLineHeight("");
|
|
1681
2010
|
setFontColor("#000000");
|
|
2011
|
+
setBgColor("#ffff00");
|
|
2012
|
+
document.execCommand("styleWithCSS", false, true);
|
|
2013
|
+
document.execCommand("hiliteColor", false, "transparent");
|
|
2014
|
+
document.execCommand("backColor", false, "transparent");
|
|
2015
|
+
document.execCommand("styleWithCSS", false, false);
|
|
1682
2016
|
triggerChange();
|
|
1683
2017
|
focus();
|
|
1684
2018
|
};
|
|
@@ -1905,7 +2239,11 @@ function RichTextEditor({
|
|
|
1905
2239
|
}
|
|
1906
2240
|
}, [disabled]);
|
|
1907
2241
|
const handleEditorClick = React.useCallback(e => {
|
|
1908
|
-
var _editorRef$
|
|
2242
|
+
var _editorRef$current8;
|
|
2243
|
+
if (areaHighlightMode) {
|
|
2244
|
+
e.preventDefault();
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
1909
2247
|
setSelectionVersion(v => v + 1);
|
|
1910
2248
|
const deleteBtn = e.target.closest('button[title="Remove image"], button[title="Remove video"]');
|
|
1911
2249
|
if (deleteBtn && editable && editorFocused) {
|
|
@@ -1920,7 +2258,10 @@ function RichTextEditor({
|
|
|
1920
2258
|
return;
|
|
1921
2259
|
}
|
|
1922
2260
|
const clickedMedia = e.target.closest('.image-container, .video-container');
|
|
1923
|
-
if (clickedMedia && (_editorRef$
|
|
2261
|
+
if (clickedMedia && (_editorRef$current8 = editorRef.current) !== null && _editorRef$current8 !== void 0 && _editorRef$current8.contains(clickedMedia) && editable) {
|
|
2262
|
+
e.preventDefault();
|
|
2263
|
+
e.stopPropagation();
|
|
2264
|
+
selectMediaContainer(clickedMedia);
|
|
1924
2265
|
return;
|
|
1925
2266
|
}
|
|
1926
2267
|
|
|
@@ -1951,16 +2292,78 @@ function RichTextEditor({
|
|
|
1951
2292
|
}
|
|
1952
2293
|
}, 0);
|
|
1953
2294
|
}
|
|
1954
|
-
}, [editable, disabled, editorFocused, triggerChange]);
|
|
2295
|
+
}, [areaHighlightMode, editable, disabled, editorFocused, triggerChange]);
|
|
2296
|
+
React.useEffect(() => {
|
|
2297
|
+
if (!selectedMedia) return;
|
|
2298
|
+
syncMediaWidthControls(selectedMedia);
|
|
2299
|
+
}, [selectedMedia, selectionVersion]);
|
|
2300
|
+
React.useEffect(() => {
|
|
2301
|
+
const editor = editorRef.current;
|
|
2302
|
+
if (!editor || !selectedMedia) return;
|
|
2303
|
+
const bumpToolbarPosition = () => setSelectionVersion(v => v + 1);
|
|
2304
|
+
editor.addEventListener("scroll", bumpToolbarPosition, {
|
|
2305
|
+
passive: true
|
|
2306
|
+
});
|
|
2307
|
+
window.addEventListener("resize", bumpToolbarPosition, {
|
|
2308
|
+
passive: true
|
|
2309
|
+
});
|
|
2310
|
+
return () => {
|
|
2311
|
+
editor.removeEventListener("scroll", bumpToolbarPosition);
|
|
2312
|
+
window.removeEventListener("resize", bumpToolbarPosition);
|
|
2313
|
+
};
|
|
2314
|
+
}, [selectedMedia]);
|
|
1955
2315
|
const renderMediaToolbar = () => {
|
|
1956
2316
|
if (!selectedMedia || !editorRef.current || !editable) return null;
|
|
1957
|
-
const
|
|
2317
|
+
const wrapperEl = contentWrapperRef.current;
|
|
2318
|
+
const anchorRect = (wrapperEl === null || wrapperEl === void 0 ? void 0 : wrapperEl.getBoundingClientRect()) ?? editorRef.current.getBoundingClientRect();
|
|
1958
2319
|
const mediaRect = selectedMedia.getBoundingClientRect();
|
|
1959
|
-
const
|
|
1960
|
-
const
|
|
1961
|
-
const
|
|
2320
|
+
const relTop = mediaRect.top - anchorRect.top;
|
|
2321
|
+
const relLeft = mediaRect.left - anchorRect.left;
|
|
2322
|
+
const mediaWidth = mediaRect.width;
|
|
2323
|
+
const mediaHeight = mediaRect.height;
|
|
1962
2324
|
const currentPercent = getMediaWidthPercent(selectedMedia);
|
|
1963
2325
|
const widthPresets = [25, 50, 75, 100];
|
|
2326
|
+
const toolbarWidth = 340;
|
|
2327
|
+
const toolbarHeight = 40;
|
|
2328
|
+
const gap = 8;
|
|
2329
|
+
const preferredTop = relTop - toolbarHeight - gap;
|
|
2330
|
+
const toolbarTop = preferredTop >= 4 ? preferredTop : relTop + mediaHeight + gap;
|
|
2331
|
+
const applyCustomMediaWidth = () => {
|
|
2332
|
+
const num = parseFloat(mediaWidthInput);
|
|
2333
|
+
if (isNaN(num) || num <= 0) {
|
|
2334
|
+
syncMediaWidthControls(selectedMedia);
|
|
2335
|
+
return;
|
|
2336
|
+
}
|
|
2337
|
+
if (mediaWidthUnit === "px") {
|
|
2338
|
+
applyMediaWidthPx(selectedMedia, num);
|
|
2339
|
+
} else {
|
|
2340
|
+
applyMediaWidthPercent(selectedMedia, num);
|
|
2341
|
+
}
|
|
2342
|
+
syncMediaWidthControls(selectedMedia);
|
|
2343
|
+
setSelectionVersion(v => v + 1);
|
|
2344
|
+
triggerChange();
|
|
2345
|
+
};
|
|
2346
|
+
const handleUnitChange = newUnit => {
|
|
2347
|
+
if (newUnit === mediaWidthUnit) return;
|
|
2348
|
+
const editorInnerWidth = getEditorInnerWidth();
|
|
2349
|
+
const currentVal = parseFloat(mediaWidthInput);
|
|
2350
|
+
if (isNaN(currentVal) || currentVal <= 0) {
|
|
2351
|
+
setMediaWidthUnit(newUnit);
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
let converted = currentVal;
|
|
2355
|
+
if (newUnit === "px" && mediaWidthUnit === "%") {
|
|
2356
|
+
converted = Math.round(editorInnerWidth * currentVal / 100);
|
|
2357
|
+
applyMediaWidthPx(selectedMedia, converted);
|
|
2358
|
+
} else if (newUnit === "%" && mediaWidthUnit === "px") {
|
|
2359
|
+
converted = Math.min(100, Math.max(10, Math.round(currentVal / editorInnerWidth * 100)));
|
|
2360
|
+
applyMediaWidthPercent(selectedMedia, converted);
|
|
2361
|
+
}
|
|
2362
|
+
setMediaWidthInput(String(converted));
|
|
2363
|
+
setMediaWidthUnit(newUnit);
|
|
2364
|
+
setSelectionVersion(v => v + 1);
|
|
2365
|
+
triggerChange();
|
|
2366
|
+
};
|
|
1964
2367
|
const handleAlignment = align => {
|
|
1965
2368
|
selectedMedia.classList.remove("image-align-left", "image-align-center", "image-align-right");
|
|
1966
2369
|
selectedMedia.classList.add(`image-align-${align}`);
|
|
@@ -1970,6 +2373,8 @@ function RichTextEditor({
|
|
|
1970
2373
|
};
|
|
1971
2374
|
const setWidth = percent => {
|
|
1972
2375
|
applyMediaWidthPercent(selectedMedia, percent);
|
|
2376
|
+
syncMediaWidthControls(selectedMedia);
|
|
2377
|
+
setSelectionVersion(v => v + 1);
|
|
1973
2378
|
triggerChange();
|
|
1974
2379
|
};
|
|
1975
2380
|
const removeMedia = () => {
|
|
@@ -1978,17 +2383,26 @@ function RichTextEditor({
|
|
|
1978
2383
|
triggerChange();
|
|
1979
2384
|
};
|
|
1980
2385
|
const isActivePercent = percent => {
|
|
2386
|
+
if (selectedMedia.dataset.widthPx) return false;
|
|
1981
2387
|
if (!selectedMedia.dataset.widthPercent && !(selectedMedia.style.width || "").endsWith("%")) {
|
|
1982
2388
|
return false;
|
|
1983
2389
|
}
|
|
1984
2390
|
return Math.abs(currentPercent - percent) <= 3;
|
|
1985
2391
|
};
|
|
2392
|
+
const isFormControl = target => target instanceof Element && !!target.closest("input, select, textarea");
|
|
1986
2393
|
return /*#__PURE__*/React.createElement("div", {
|
|
1987
2394
|
className: "rte-media-toolbar",
|
|
2395
|
+
onMouseDown: e => {
|
|
2396
|
+
e.stopPropagation();
|
|
2397
|
+
if (!isFormControl(e.target)) {
|
|
2398
|
+
e.preventDefault();
|
|
2399
|
+
}
|
|
2400
|
+
},
|
|
2401
|
+
onClick: e => e.stopPropagation(),
|
|
1988
2402
|
style: {
|
|
1989
2403
|
position: "absolute",
|
|
1990
|
-
top: Math.max(
|
|
1991
|
-
left: Math.max(8,
|
|
2404
|
+
top: Math.max(4, toolbarTop),
|
|
2405
|
+
left: Math.max(8, Math.min(relLeft + mediaWidth / 2 - toolbarWidth / 2, anchorRect.width - toolbarWidth - 8)),
|
|
1992
2406
|
zIndex: 1000
|
|
1993
2407
|
}
|
|
1994
2408
|
}, /*#__PURE__*/React.createElement("button", {
|
|
@@ -2013,6 +2427,44 @@ function RichTextEditor({
|
|
|
2013
2427
|
title: `${percent}% width`
|
|
2014
2428
|
}, percent, "%")), /*#__PURE__*/React.createElement("span", {
|
|
2015
2429
|
className: "rte-media-toolbar-divider"
|
|
2430
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
2431
|
+
className: "rte-media-width-custom"
|
|
2432
|
+
}, /*#__PURE__*/React.createElement("input", {
|
|
2433
|
+
type: "number",
|
|
2434
|
+
className: "rte-media-width-input",
|
|
2435
|
+
value: mediaWidthInput,
|
|
2436
|
+
min: mediaWidthUnit === "%" ? 10 : 20,
|
|
2437
|
+
max: mediaWidthUnit === "%" ? 100 : 2000,
|
|
2438
|
+
title: "Custom width",
|
|
2439
|
+
onChange: e => setMediaWidthInput(e.target.value),
|
|
2440
|
+
onBlur: e => {
|
|
2441
|
+
var _e$relatedTarget, _e$relatedTarget$clos;
|
|
2442
|
+
if ((_e$relatedTarget = e.relatedTarget) !== null && _e$relatedTarget !== void 0 && (_e$relatedTarget$clos = _e$relatedTarget.closest) !== null && _e$relatedTarget$clos !== void 0 && _e$relatedTarget$clos.call(_e$relatedTarget, ".rte-media-width-custom")) return;
|
|
2443
|
+
applyCustomMediaWidth();
|
|
2444
|
+
},
|
|
2445
|
+
onMouseDown: e => e.stopPropagation(),
|
|
2446
|
+
onKeyDown: e => {
|
|
2447
|
+
if (e.key === "Enter") {
|
|
2448
|
+
e.preventDefault();
|
|
2449
|
+
applyCustomMediaWidth();
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
}), /*#__PURE__*/React.createElement("select", {
|
|
2453
|
+
className: "rte-media-width-unit",
|
|
2454
|
+
value: mediaWidthUnit,
|
|
2455
|
+
onChange: e => handleUnitChange(e.target.value),
|
|
2456
|
+
onMouseDown: e => e.stopPropagation()
|
|
2457
|
+
}, /*#__PURE__*/React.createElement("option", {
|
|
2458
|
+
value: "%"
|
|
2459
|
+
}, "%"), /*#__PURE__*/React.createElement("option", {
|
|
2460
|
+
value: "px"
|
|
2461
|
+
}, "px")), /*#__PURE__*/React.createElement("button", {
|
|
2462
|
+
type: "button",
|
|
2463
|
+
className: "rte-media-width-apply",
|
|
2464
|
+
title: "Apply size",
|
|
2465
|
+
onClick: applyCustomMediaWidth
|
|
2466
|
+
}, "\u2713")), /*#__PURE__*/React.createElement("span", {
|
|
2467
|
+
className: "rte-media-toolbar-divider"
|
|
2016
2468
|
}), /*#__PURE__*/React.createElement("button", {
|
|
2017
2469
|
type: "button",
|
|
2018
2470
|
onClick: removeMedia,
|
|
@@ -2058,12 +2510,9 @@ function RichTextEditor({
|
|
|
2058
2510
|
}, /*#__PURE__*/React.createElement("button", {
|
|
2059
2511
|
type: "button",
|
|
2060
2512
|
title: "Bold",
|
|
2061
|
-
|
|
2513
|
+
onMouseDown: e => {
|
|
2062
2514
|
e.preventDefault();
|
|
2063
|
-
|
|
2064
|
-
document.execCommand("bold");
|
|
2065
|
-
handleInput();
|
|
2066
|
-
focus();
|
|
2515
|
+
exec("bold");
|
|
2067
2516
|
},
|
|
2068
2517
|
className: `rte-toolbar-button ${isBold ? "active" : ""}`
|
|
2069
2518
|
}, /*#__PURE__*/React.createElement(FaBold, {
|
|
@@ -2071,12 +2520,9 @@ function RichTextEditor({
|
|
|
2071
2520
|
})), /*#__PURE__*/React.createElement("button", {
|
|
2072
2521
|
type: "button",
|
|
2073
2522
|
title: "Italic",
|
|
2074
|
-
|
|
2523
|
+
onMouseDown: e => {
|
|
2075
2524
|
e.preventDefault();
|
|
2076
|
-
|
|
2077
|
-
document.execCommand("italic");
|
|
2078
|
-
handleInput();
|
|
2079
|
-
focus();
|
|
2525
|
+
exec("italic");
|
|
2080
2526
|
},
|
|
2081
2527
|
className: `rte-toolbar-button ${isItalic ? "active" : ""}`
|
|
2082
2528
|
}, /*#__PURE__*/React.createElement(FaItalic, {
|
|
@@ -2084,12 +2530,9 @@ function RichTextEditor({
|
|
|
2084
2530
|
})), /*#__PURE__*/React.createElement("button", {
|
|
2085
2531
|
type: "button",
|
|
2086
2532
|
title: "Underline",
|
|
2087
|
-
|
|
2533
|
+
onMouseDown: e => {
|
|
2088
2534
|
e.preventDefault();
|
|
2089
|
-
|
|
2090
|
-
document.execCommand("underline");
|
|
2091
|
-
handleInput();
|
|
2092
|
-
focus();
|
|
2535
|
+
exec("underline");
|
|
2093
2536
|
},
|
|
2094
2537
|
className: `rte-toolbar-button ${isUnderline ? "active" : ""}`
|
|
2095
2538
|
}, /*#__PURE__*/React.createElement(FaUnderline, {
|
|
@@ -2156,7 +2599,7 @@ function RichTextEditor({
|
|
|
2156
2599
|
key: s,
|
|
2157
2600
|
value: s
|
|
2158
2601
|
}, s, "px"))), /*#__PURE__*/React.createElement("label", {
|
|
2159
|
-
title: "
|
|
2602
|
+
title: "Text Color",
|
|
2160
2603
|
className: "rte-color-picker-label"
|
|
2161
2604
|
}, /*#__PURE__*/React.createElement(FaFont, {
|
|
2162
2605
|
size: 14,
|
|
@@ -2169,12 +2612,53 @@ function RichTextEditor({
|
|
|
2169
2612
|
onMouseDown: e => {
|
|
2170
2613
|
e.preventDefault();
|
|
2171
2614
|
e.stopPropagation();
|
|
2615
|
+
saveSelection();
|
|
2172
2616
|
},
|
|
2173
2617
|
onChange: e => {
|
|
2174
2618
|
e.stopPropagation();
|
|
2175
|
-
|
|
2619
|
+
applyTextColor(e.target.value);
|
|
2176
2620
|
},
|
|
2177
2621
|
className: "rte-color-input"
|
|
2622
|
+
})), /*#__PURE__*/React.createElement("label", {
|
|
2623
|
+
title: areaHighlightMode ? "Highlight color for area selection" : "Text background color",
|
|
2624
|
+
className: "rte-color-picker-label rte-bg-color-picker-label"
|
|
2625
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
2626
|
+
className: "rte-bg-color-swatch",
|
|
2627
|
+
style: {
|
|
2628
|
+
backgroundColor: bgColor,
|
|
2629
|
+
color: fontColor
|
|
2630
|
+
}
|
|
2631
|
+
}, "A"), /*#__PURE__*/React.createElement("input", {
|
|
2632
|
+
type: "color",
|
|
2633
|
+
value: bgColor,
|
|
2634
|
+
onMouseDown: e => {
|
|
2635
|
+
e.preventDefault();
|
|
2636
|
+
e.stopPropagation();
|
|
2637
|
+
if (!areaHighlightMode) saveSelection();
|
|
2638
|
+
},
|
|
2639
|
+
onChange: e => {
|
|
2640
|
+
e.stopPropagation();
|
|
2641
|
+
const color = e.target.value;
|
|
2642
|
+
setBgColor(color);
|
|
2643
|
+
if (!areaHighlightMode) {
|
|
2644
|
+
applyBackgroundColor(color);
|
|
2645
|
+
}
|
|
2646
|
+
},
|
|
2647
|
+
className: "rte-color-input"
|
|
2648
|
+
})), /*#__PURE__*/React.createElement("button", {
|
|
2649
|
+
type: "button",
|
|
2650
|
+
title: "Area highlight \u2014 drag to select a box (like screenshot)",
|
|
2651
|
+
className: `rte-toolbar-button rte-area-highlight-toggle${areaHighlightMode ? " active" : ""}`,
|
|
2652
|
+
onMouseDown: e => e.preventDefault(),
|
|
2653
|
+
onClick: () => {
|
|
2654
|
+
setAreaHighlightMode(prev => !prev);
|
|
2655
|
+
setMarqueePreview(null);
|
|
2656
|
+
areaDragRef.current = null;
|
|
2657
|
+
focus();
|
|
2658
|
+
}
|
|
2659
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
2660
|
+
className: "rte-area-highlight-icon",
|
|
2661
|
+
"aria-hidden": "true"
|
|
2178
2662
|
})), /*#__PURE__*/React.createElement("div", {
|
|
2179
2663
|
style: {
|
|
2180
2664
|
width: '1px',
|
|
@@ -2471,11 +2955,26 @@ function RichTextEditor({
|
|
|
2471
2955
|
}
|
|
2472
2956
|
return null;
|
|
2473
2957
|
})()), /*#__PURE__*/React.createElement("div", {
|
|
2474
|
-
|
|
2958
|
+
ref: contentWrapperRef,
|
|
2959
|
+
className: `rte-content-wrapper${areaHighlightMode ? " rte-area-highlight-mode" : ""}`,
|
|
2475
2960
|
style: {
|
|
2476
2961
|
position: 'relative'
|
|
2962
|
+
},
|
|
2963
|
+
onMouseDown: handleAreaSelectMouseDown
|
|
2964
|
+
}, areaHighlightMode && /*#__PURE__*/React.createElement("div", {
|
|
2965
|
+
className: "rte-area-highlight-hint",
|
|
2966
|
+
"aria-hidden": "true"
|
|
2967
|
+
}, "Drag to select an area, then release to apply highlight"), marqueePreview && /*#__PURE__*/React.createElement("div", {
|
|
2968
|
+
className: "rte-marquee-preview",
|
|
2969
|
+
style: {
|
|
2970
|
+
left: marqueePreview.left,
|
|
2971
|
+
top: marqueePreview.top,
|
|
2972
|
+
width: marqueePreview.width,
|
|
2973
|
+
height: marqueePreview.height,
|
|
2974
|
+
backgroundColor: `${bgColor}55`,
|
|
2975
|
+
borderColor: bgColor
|
|
2477
2976
|
}
|
|
2478
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
2977
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
2479
2978
|
ref: editorRef,
|
|
2480
2979
|
contentEditable: editable && disabled !== true,
|
|
2481
2980
|
suppressContentEditableWarning: true,
|
|
@@ -2488,6 +2987,11 @@ function RichTextEditor({
|
|
|
2488
2987
|
onDragStart: e => e.preventDefault(),
|
|
2489
2988
|
onDragOver: e => e.preventDefault(),
|
|
2490
2989
|
onKeyDown: handleKeyDown,
|
|
2990
|
+
onKeyUp: handleKeyUp,
|
|
2991
|
+
onMouseUp: () => {
|
|
2992
|
+
saveSelection();
|
|
2993
|
+
setSelectionVersion(v => v + 1);
|
|
2994
|
+
},
|
|
2491
2995
|
onClick: handleEditorClick,
|
|
2492
2996
|
onFocus: handleEditorFocus,
|
|
2493
2997
|
onBlur: handleEditorBlur,
|
|
@@ -2503,7 +3007,7 @@ function RichTextEditor({
|
|
|
2503
3007
|
left: paddingLeft || '12px'
|
|
2504
3008
|
},
|
|
2505
3009
|
"aria-hidden": "true"
|
|
2506
|
-
}, placeholder)
|
|
3010
|
+
}, placeholder), renderMediaToolbar()), /*#__PURE__*/React.createElement("div", {
|
|
2507
3011
|
className: "rte-footer"
|
|
2508
3012
|
}, /*#__PURE__*/React.createElement("div", {
|
|
2509
3013
|
className: "rte-footer-content"
|