Jul 24 - Update Rich-text editor to enhance table editor
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
{% block head_extra %}
|
||||
<link href="{{ url_for('static', filename='vendor/quill.snow.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='vendor/quill-table-better.css') }}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% macro val(field, default='') -%}
|
||||
@@ -77,13 +78,8 @@
|
||||
<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 class="ql-formats">
|
||||
<button class="ql-table-better" title="Insert table"></button>
|
||||
</span>
|
||||
</div>
|
||||
<div id="editor"></div>
|
||||
@@ -145,21 +141,36 @@
|
||||
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='vendor/quill.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='vendor/quill-table-better.js') }}"></script>
|
||||
<script>
|
||||
(function () {
|
||||
if (typeof Quill === 'undefined') return; // textarea stays usable
|
||||
var ta = document.getElementById('body_src');
|
||||
var toolbarEl = document.getElementById('editor-toolbar');
|
||||
var editorEl = document.getElementById('editor');
|
||||
var quill = new Quill(editorEl, {
|
||||
theme: 'snow',
|
||||
// `table: true` enables Quill 2's built-in table module.
|
||||
modules: { toolbar: toolbarEl, table: true }
|
||||
});
|
||||
|
||||
// quill-table-better: resizable columns/rows + a cell menu for alignment,
|
||||
// borders and background. Registered only if the vendor script loaded.
|
||||
var hasTables = typeof QuillTableBetter !== 'undefined';
|
||||
if (hasTables) {
|
||||
Quill.register({ 'modules/table-better': QuillTableBetter }, true);
|
||||
}
|
||||
|
||||
var modules = { toolbar: toolbarEl };
|
||||
if (hasTables) {
|
||||
modules.table = false; // disable Quill's basic table
|
||||
modules['table-better'] = {
|
||||
language: 'en_US',
|
||||
menus: ['column', 'row', 'merge', 'table', 'cell', 'wrap', 'delete'],
|
||||
toolbarTable: true
|
||||
};
|
||||
modules.keyboard = { bindings: QuillTableBetter.keyboardBindings };
|
||||
}
|
||||
|
||||
var quill = new Quill(editorEl, { theme: 'snow', modules: modules });
|
||||
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.
|
||||
@@ -186,28 +197,8 @@
|
||||
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.');
|
||||
}
|
||||
});
|
||||
});
|
||||
// Table resize + cell alignment are handled by quill-table-better's own
|
||||
// drag handles and floating cell menu — no extra toolbar wiring needed.
|
||||
|
||||
// Switch the UI from textarea to Quill only once it's ready.
|
||||
document.body.classList.add('quill-on');
|
||||
@@ -217,7 +208,13 @@
|
||||
// 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 : '';
|
||||
if (!hasText && !hasEmbed) { ta.value = ''; return; }
|
||||
// Drop quill-table-better's transient selection nodes, then serialize
|
||||
// clean semantic HTML (not raw innerHTML, which can leak UI markup).
|
||||
if (hasTables) {
|
||||
try { quill.getModule('table-better').deleteTableTemporary(); } catch (e) {}
|
||||
}
|
||||
ta.value = quill.getSemanticHTML();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user