Jul 24 - Update Rich-text editor to support uploading images and inserting table
This commit is contained in:
@@ -74,8 +74,17 @@
|
||||
</span>
|
||||
<span class="ql-formats">
|
||||
<button class="ql-link"></button>
|
||||
<button class="ql-image" title="Insert image"></button>
|
||||
<button class="ql-clean"></button>
|
||||
</span>
|
||||
<span class="ql-formats ql-table-tools">
|
||||
<button type="button" class="ql-table-op" data-op="insert" title="Insert table">▦</button>
|
||||
<button type="button" class="ql-table-op" data-op="row" title="Add row below">+Row</button>
|
||||
<button type="button" class="ql-table-op" data-op="col" title="Add column right">+Col</button>
|
||||
<button type="button" class="ql-table-op" data-op="delrow" title="Delete row">−Row</button>
|
||||
<button type="button" class="ql-table-op" data-op="delcol" title="Delete column">−Col</button>
|
||||
<button type="button" class="ql-table-op" data-op="deltable" title="Delete table">✕Table</button>
|
||||
</span>
|
||||
</div>
|
||||
<div id="editor"></div>
|
||||
<!-- Real field. Quill enhances it; if Quill fails to load, this stays a
|
||||
@@ -140,16 +149,75 @@
|
||||
(function () {
|
||||
if (typeof Quill === 'undefined') return; // textarea stays usable
|
||||
var ta = document.getElementById('body_src');
|
||||
var toolbar = document.getElementById('editor-toolbar');
|
||||
var toolbarEl = document.getElementById('editor-toolbar');
|
||||
var editorEl = document.getElementById('editor');
|
||||
var quill = new Quill(editorEl, { theme: 'snow', modules: { toolbar: toolbar } });
|
||||
var quill = new Quill(editorEl, {
|
||||
theme: 'snow',
|
||||
// `table: true` enables Quill 2's built-in table module.
|
||||
modules: { toolbar: toolbarEl, table: true }
|
||||
});
|
||||
if (ta.value.trim()) quill.clipboard.dangerouslyPasteHTML(ta.value);
|
||||
|
||||
var csrf = document.querySelector('meta[name="csrf-token"]').content;
|
||||
var tableModule = quill.getModule('table');
|
||||
|
||||
// --- Image button: upload the file, then embed the returned URL. This
|
||||
// keeps the DB small (no base64) and the image is served as a static file.
|
||||
quill.getModule('toolbar').addHandler('image', function () {
|
||||
var input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/png,image/jpeg,image/gif,image/webp';
|
||||
input.onchange = function () {
|
||||
var file = input.files && input.files[0];
|
||||
if (!file) return;
|
||||
var fd = new FormData();
|
||||
fd.append('file', file);
|
||||
fetch('{{ url_for("admin.upload") }}', {
|
||||
method: 'POST', headers: { 'X-CSRFToken': csrf }, body: fd
|
||||
}).then(function (r) {
|
||||
return r.json().then(function (j) { return { ok: r.ok, body: j }; });
|
||||
}).then(function (res) {
|
||||
if (!res.ok) { alert(res.body.error || 'Upload failed.'); return; }
|
||||
var range = quill.getSelection(true);
|
||||
quill.insertEmbed(range.index, 'image', res.body.url, 'user');
|
||||
quill.setSelection(range.index + 1, 'silent');
|
||||
}).catch(function () { alert('Upload failed.'); });
|
||||
};
|
||||
input.click();
|
||||
});
|
||||
|
||||
// --- Table controls. Row/column ops act on the cell holding the cursor.
|
||||
toolbarEl.querySelectorAll('.ql-table-op').forEach(function (btn) {
|
||||
// Preventing the mousedown default keeps the editor's selection so the
|
||||
// table module knows which cell the cursor is in.
|
||||
btn.addEventListener('mousedown', function (e) { e.preventDefault(); });
|
||||
btn.addEventListener('click', function () {
|
||||
var op = btn.getAttribute('data-op');
|
||||
try {
|
||||
if (op === 'insert') {
|
||||
if (!quill.getSelection()) quill.setSelection(quill.getLength() - 1, 0);
|
||||
tableModule.insertTable(3, 3);
|
||||
}
|
||||
else if (op === 'row') tableModule.insertRowBelow();
|
||||
else if (op === 'col') tableModule.insertColumnRight();
|
||||
else if (op === 'delrow') tableModule.deleteRow();
|
||||
else if (op === 'delcol') tableModule.deleteColumn();
|
||||
else if (op === 'deltable') tableModule.deleteTable();
|
||||
} catch (err) {
|
||||
alert('Place the cursor inside a table cell first.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Switch the UI from textarea to Quill only once it's ready.
|
||||
document.body.classList.add('quill-on');
|
||||
|
||||
ta.form.addEventListener('submit', function () {
|
||||
ta.value = quill.getText().trim().length ? quill.root.innerHTML : '';
|
||||
// getText() is empty for an image/table-only body, so also check for
|
||||
// embeds before treating the editor as blank (which would wipe the save).
|
||||
var hasText = quill.getText().trim().length > 0;
|
||||
var hasEmbed = quill.root.querySelector('img, table');
|
||||
ta.value = (hasText || hasEmbed) ? quill.root.innerHTML : '';
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user