Jul 24 - Update Rich-text editor to enhance table editor 6
This commit is contained in:
@@ -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/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-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(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)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
load still leaves a working editor + textarea. Registration and keyboard bindings
|
||||||
(`QuillTableBetter.keyboardBindings`) live in `topic_form.html`.
|
(`QuillTableBetter.keyboardBindings`) live in `topic_form.html`.
|
||||||
- **Save/load round-trip (critical):** save with `deleteTableTemporary()` then
|
- **Save/load round-trip (critical):** save with `deleteTableTemporary()` then
|
||||||
`quill.getSemanticHTML()`. **Load must NOT use `dangerouslyPasteHTML`/`setContents`**
|
`quill.root.innerHTML` — **NOT `getSemanticHTML()`**, which rewrites Quill's
|
||||||
— quill-table-better renders tables *blank* that way. Instead convert then apply:
|
`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
|
`quill.updateContents(quill.clipboard.convert({html}), 'user')`. The non-table
|
||||||
fallback path still uses `dangerouslyPasteHTML`.
|
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`
|
- **Sanitize on save:** `sanitize_html()` (bleach) runs on every `body_html`
|
||||||
write — `ALLOWED_TAGS`/`ALLOWED_ATTRS` cover `img` + full table tags with
|
write — `ALLOWED_TAGS`/`ALLOWED_ATTRS` cover `img` + full table tags with
|
||||||
`colspan/rowspan/data-*/style`; a `CSSSanitizer` (bleach[css] + `tinycss2`)
|
`colspan/rowspan/data-*/style`; a `CSSSanitizer` (bleach[css] + `tinycss2`)
|
||||||
|
|||||||
@@ -97,21 +97,23 @@ ALLOWED_TAGS = [
|
|||||||
# tables (quill-table-better: resizable cols, aligned/styled cells)
|
# tables (quill-table-better: resizable cols, aligned/styled cells)
|
||||||
"table", "thead", "tbody", "tr", "td", "th", "col", "colgroup",
|
"table", "thead", "tbody", "tr", "td", "th", "col", "colgroup",
|
||||||
]
|
]
|
||||||
# quill-table-better carries column widths, alignment, borders and background
|
# quill-table-better carries column widths, borders and background as inline
|
||||||
# as inline `style` on the table/cell tags, plus data-* bookkeeping attributes.
|
# `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 = [
|
_TABLE_CELL_ATTRS = [
|
||||||
"data-row", "data-cell", "data-class", "colspan", "rowspan",
|
"data-row", "data-cell", "data-class", "colspan", "rowspan",
|
||||||
"width", "height", "style",
|
"width", "height", "style", "class",
|
||||||
]
|
]
|
||||||
ALLOWED_ATTRS = {
|
ALLOWED_ATTRS = {
|
||||||
"a": ["href", "title", "target", "rel"],
|
"a": ["href", "title", "target", "rel"],
|
||||||
"img": ["src", "alt", "width", "height"],
|
"img": ["src", "alt", "width", "height"],
|
||||||
# cell alignment lives as inline `text-align` on the cell's <p> block
|
"p": ["style", "class", "data-cell", "data-row"],
|
||||||
"p": ["style"],
|
|
||||||
"table": ["class", "style", "align", "width", "height", "data-class"],
|
"table": ["class", "style", "align", "width", "height", "data-class"],
|
||||||
"colgroup": ["style"],
|
"colgroup": ["style", "class"],
|
||||||
"col": ["width", "span", "style"],
|
"col": ["width", "span", "style", "class"],
|
||||||
"tr": ["data-row", "style"],
|
"tr": ["data-row", "style", "class"],
|
||||||
"td": _TABLE_CELL_ATTRS,
|
"td": _TABLE_CELL_ATTRS,
|
||||||
"th": _TABLE_CELL_ATTRS,
|
"th": _TABLE_CELL_ATTRS,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,6 +163,10 @@ body{
|
|||||||
.prose table{border-collapse:collapse;table-layout:fixed;max-width:100%;margin:1em 0;font-size:.95rem}
|
.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 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}
|
.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{
|
.topic__link{
|
||||||
align-self:flex-start;display:inline-flex;align-items:center;gap:8px;
|
align-self:flex-start;display:inline-flex;align-items:center;gap:8px;
|
||||||
|
|||||||
@@ -149,11 +149,6 @@
|
|||||||
var toolbarEl = document.getElementById('editor-toolbar');
|
var toolbarEl = document.getElementById('editor-toolbar');
|
||||||
var editorEl = document.getElementById('editor');
|
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,
|
// quill-table-better: resizable columns/rows + a cell menu for alignment,
|
||||||
// borders and background. Registered only if the vendor script loaded.
|
// borders and background. Registered only if the vendor script loaded.
|
||||||
var hasTables = typeof QuillTableBetter !== 'undefined';
|
var hasTables = typeof QuillTableBetter !== 'undefined';
|
||||||
@@ -226,12 +221,15 @@
|
|||||||
var hasText = quill.getText().trim().length > 0;
|
var hasText = quill.getText().trim().length > 0;
|
||||||
var hasEmbed = quill.root.querySelector('img, table');
|
var hasEmbed = quill.root.querySelector('img, table');
|
||||||
if (!hasText && !hasEmbed) { ta.value = ''; return; }
|
if (!hasText && !hasEmbed) { ta.value = ''; return; }
|
||||||
// Drop quill-table-better's transient selection nodes, then serialize
|
// Drop quill-table-better's transient selection nodes, then save the raw
|
||||||
// clean semantic HTML (not raw innerHTML, which can leak UI markup).
|
// 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) {
|
if (hasTables) {
|
||||||
try { quill.getModule('table-better').deleteTableTemporary(); } catch (e) {}
|
try { quill.getModule('table-better').deleteTableTemporary(); } catch (e) {}
|
||||||
}
|
}
|
||||||
ta.value = quill.getSemanticHTML();
|
ta.value = quill.root.innerHTML;
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user