Jul 24 - Update Rich-text editor to enhance table editor 6

This commit is contained in:
2026-07-24 15:35:12 -04:00
parent 22754f6ae8
commit a321a881d8
5 changed files with 33 additions and 19 deletions
+3 -1
View File
@@ -33,7 +33,9 @@
"Bash(curl -sSL https://raw.githubusercontent.com/attoae/quill-table-better/main/src/utils/clipboard-matchers.ts -o cm.ts -w \"cm %{http_code}\\\\n\")",
"Bash(curl -sSL https://raw.githubusercontent.com/attoae/quill-table-better/main/src/ui/table-menus.ts -o tm.ts -w \"tm %{http_code}\\\\n\")",
"Bash(curl -sSL https://raw.githubusercontent.com/attoae/quill-table-better/main/src/ui/table-properties-form.ts -o tpf.ts -w \"tpf %{http_code}\\\\n\")",
"Bash(python -m py_compile app.py)"
"Bash(python -m py_compile app.py)",
"Bash(curl -sSL https://raw.githubusercontent.com/attoae/quill-table-better/main/src/utils/index.ts -o utils.ts -w \"utils %{http_code}\\\\n\")",
"Bash(grep -nA15 \"function getAlign|getAlign =|export const getAlign\" utils.ts)"
]
}
}
+10 -2
View File
@@ -128,10 +128,18 @@ shows only `is_published` topics (sections with no published topics are hidden).
load still leaves a working editor + textarea. Registration and keyboard bindings
(`QuillTableBetter.keyboardBindings`) live in `topic_form.html`.
- **Save/load round-trip (critical):** save with `deleteTableTemporary()` then
`quill.getSemanticHTML()`. **Load must NOT use `dangerouslyPasteHTML`/`setContents`**
— quill-table-better renders tables *blank* that way. Instead convert then apply:
`quill.root.innerHTML` — **NOT `getSemanticHTML()`**, which rewrites Quill's
`ql-align-*` class as an inline `text-align` style; quill-table-better's
`getAlign()` reads only the class, so semantic HTML makes the cell-properties
dialog always show "left". **Load must NOT use `dangerouslyPasteHTML`/`setContents`**
either — that renders tables *blank*. Instead convert then apply:
`quill.updateContents(quill.clipboard.convert({html}), 'user')`. The non-table
fallback path still uses `dangerouslyPasteHTML`.
- **Alignment is a CSS class, not inline style.** Cell alignment = Quill's default
class-based align (`ql-align-center` on the cell `<p>`). The sanitizer therefore
allows `class` on p/table tags (also needed for `ql-table-block`), and the public
page styles `.prose .ql-align-*`. Do NOT register `attributors/style/align` — it
breaks the properties-dialog readback described above.
- **Sanitize on save:** `sanitize_html()` (bleach) runs on every `body_html`
write — `ALLOWED_TAGS`/`ALLOWED_ATTRS` cover `img` + full table tags with
`colspan/rowspan/data-*/style`; a `CSSSanitizer` (bleach[css] + `tinycss2`)
+10 -8
View File
@@ -97,21 +97,23 @@ ALLOWED_TAGS = [
# tables (quill-table-better: resizable cols, aligned/styled cells)
"table", "thead", "tbody", "tr", "td", "th", "col", "colgroup",
]
# quill-table-better carries column widths, alignment, borders and background
# as inline `style` on the table/cell tags, plus data-* bookkeeping attributes.
# quill-table-better carries column widths, borders and background as inline
# `style`, plus data-* bookkeeping attributes. Cell alignment is Quill's
# `ql-align-*` CSS class on the cell's <p> block, and quill-table-better also
# tags blots with `ql-table-block`/`table-th-block` classes — all of which must
# survive so the table (and its alignment) round-trips back into the editor.
_TABLE_CELL_ATTRS = [
"data-row", "data-cell", "data-class", "colspan", "rowspan",
"width", "height", "style",
"width", "height", "style", "class",
]
ALLOWED_ATTRS = {
"a": ["href", "title", "target", "rel"],
"img": ["src", "alt", "width", "height"],
# cell alignment lives as inline `text-align` on the cell's <p> block
"p": ["style"],
"p": ["style", "class", "data-cell", "data-row"],
"table": ["class", "style", "align", "width", "height", "data-class"],
"colgroup": ["style"],
"col": ["width", "span", "style"],
"tr": ["data-row", "style"],
"colgroup": ["style", "class"],
"col": ["width", "span", "style", "class"],
"tr": ["data-row", "style", "class"],
"td": _TABLE_CELL_ATTRS,
"th": _TABLE_CELL_ATTRS,
}
+4
View File
@@ -163,6 +163,10 @@ body{
.prose table{border-collapse:collapse;table-layout:fixed;max-width:100%;margin:1em 0;font-size:.95rem}
.prose td,.prose th{border:1px solid var(--hair);padding:.5rem .65rem;text-align:left;vertical-align:top;word-wrap:break-word}
.prose th{background:rgba(0,0,0,.03);color:var(--ink);font-weight:600}
/* Cell alignment is Quill's align class on the cell's <p> (see admin editor). */
.prose .ql-align-center{text-align:center}
.prose .ql-align-right{text-align:right}
.prose .ql-align-justify{text-align:justify}
.topic__link{
align-self:flex-start;display:inline-flex;align-items:center;gap:8px;
+6 -8
View File
@@ -149,11 +149,6 @@
var toolbarEl = document.getElementById('editor-toolbar');
var editorEl = document.getElementById('editor');
// Persist alignment as inline `text-align` (style) rather than a Quill CSS
// class, so cell alignment survives the HTML sanitizer and renders on the
// public page with no extra CSS. Must run before the editor is created.
Quill.register(Quill.import('attributors/style/align'), 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';
@@ -226,12 +221,15 @@
var hasText = quill.getText().trim().length > 0;
var hasEmbed = quill.root.querySelector('img, table');
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).
// Drop quill-table-better's transient selection nodes, then save the raw
// editor HTML. We deliberately avoid getSemanticHTML() here: it rewrites
// Quill's `ql-align-*` class as an inline text-align style, which breaks
// quill-table-better's cell-alignment readback (its getAlign() only reads
// the class). innerHTML keeps the class so alignment round-trips.
if (hasTables) {
try { quill.getModule('table-better').deleteTableTemporary(); } catch (e) {}
}
ta.value = quill.getSemanticHTML();
ta.value = quill.root.innerHTML;
});
})();
</script>