fix: a list number wider than the hanging indent pushes the text
The marker pseudo is floated into the indent instead of absolutely positioned, so a label the indent cannot hold — a "1.1.1." outline number, a Roman numeral — moves the first line right the way both word processors do, rather than printing over the words it labels. Out of flow either way, so no list line grows. The PDF clone's injected marker follows suit, and its chain now reads the effective numbering and walks to the outermost multilevel list: a list numbered by a named style had none of its own and printed a bare separator.
This commit is contained in:
1 parent
7a17fc7995
commit
583bc0ddd8
3 files changed
+32
-24
No files matched your search
+12
-12
@@ -142,16 +142,18 @@ function liOrdinal(li: HTMLElement): number {
|
||||
return parseInt(parent.getAttribute('start') ?? '1', 10) + items.indexOf(li);
|
||||
}
|
||||
|
||||
// The <ol> whose 'multilevel' style governs this list: itself, or the nearest
|
||||
// ordered ancestor with the attr reached without crossing another explicit style.
|
||||
// The outermost <ol> of the unbroken 'multilevel' run this list sits in — where the
|
||||
// chain `counters(list-item, '.')` prints starts, so the walk cannot stop at the nearest
|
||||
// one. Keyed on the *effective* numbering, the attr editor.css draws from: a list
|
||||
// numbered by a named style carries no `data-list-style` of its own.
|
||||
function multilevelRoot(ol: HTMLElement, root: HTMLElement): HTMLElement | null {
|
||||
let found: HTMLElement | null = null;
|
||||
for (let e: HTMLElement | null = ol; e && e !== root; e = e.parentElement) {
|
||||
if (e.tagName !== 'OL') continue;
|
||||
const t = e.getAttribute('data-list-style');
|
||||
if (t === 'multilevel') return e;
|
||||
if (t) return null; // a nearer explicit style cuts the chain
|
||||
if (e.getAttribute('data-eff-list-style') !== 'multilevel') break;
|
||||
found = e;
|
||||
}
|
||||
return null;
|
||||
return found;
|
||||
}
|
||||
|
||||
// The marker text the browser renders for a list item: the depth bullet/bulletChar
|
||||
@@ -203,15 +205,13 @@ function materializeListMarkers(root: HTMLElement): void {
|
||||
const glyph = listMarkerGlyph(li, root);
|
||||
if (!glyph) continue;
|
||||
const target = li.querySelector(':scope > p, :scope > h1, :scope > h2, :scope > h3, :scope > h4, :scope > h5') ?? li;
|
||||
const slot = document.createElement('span');
|
||||
slot.style.cssText = 'display:inline-block;width:0;overflow:visible;vertical-align:baseline';
|
||||
const label = document.createElement('span');
|
||||
label.style.cssText = `display:inline-block;white-space:pre;transform:translateX(-0.635cm);color:${cs.color}`;
|
||||
label.style.cssText = `float:left;min-width:0.635cm;margin-left:-0.635cm;white-space:pre;color:${cs.color}`;
|
||||
// Same symbol shim the editor's ::marker uses (glyphs Liberation Serif lacks).
|
||||
label.style.fontFamily = `'EdenText Symbols', ${cs.fontFamily}`;
|
||||
label.textContent = glyph;
|
||||
slot.appendChild(label);
|
||||
target.insertBefore(slot, target.firstChild);
|
||||
label.textContent = `${glyph} `; // the trailing space editor.css puts in every marker
|
||||
label.dataset.pdfMarker = '';
|
||||
target.insertBefore(label, target.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-11
@@ -672,17 +672,18 @@
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
/* Out of flow, so the marker cannot touch the line it labels — in flow it grew every
|
||||
list line by a pixel, which cost the thesis fixture a page. `top: 0` seats it on the
|
||||
first line, since the pseudo takes the block's own line-height. */
|
||||
/* The containing block for the right-aligned marker below, which stays absolute. */
|
||||
.paper .tiptap li > :is(p, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Floated, so the marker never grows the line box (in flow it grew every list line by a
|
||||
pixel, which cost a fixture a page) yet a label wider than the hanging indent pushes
|
||||
the first line right instead of printing under it, as both word processors do. */
|
||||
.paper .tiptap li > :is(p, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10)::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -0.635cm;
|
||||
float: left;
|
||||
min-width: 0.635cm;
|
||||
margin-left: -0.635cm;
|
||||
white-space: pre;
|
||||
line-height: inherit;
|
||||
/* The marker follows the item's first text portion (listMarker.ts sets the custom
|
||||
@@ -697,7 +698,11 @@
|
||||
/* A label set against the far end of the hanging indent grows into the margin instead
|
||||
of into the text — what keeps Word's Roman numerals off the words they label. */
|
||||
.paper .tiptap :is(ol, ul)[data-marker-align="right"] > li > :is(p, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10)::before {
|
||||
left: auto;
|
||||
float: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
min-width: 0;
|
||||
margin-left: 0;
|
||||
right: calc(100% + 0.635cm);
|
||||
}
|
||||
|
||||
@@ -752,9 +757,11 @@
|
||||
font-family: 'EdenText Symbols', var(--marker-family, var(--font-serif)), serif;
|
||||
}
|
||||
|
||||
/* The PDF clone (the .paper itself) replaces every marker with a real span (pdf.ts). */
|
||||
/* The PDF clone (the .paper itself) replaces every marker with a real span (pdf.ts).
|
||||
`!important` because the per-numbering rules above are one attribute more specific,
|
||||
and html2canvas prints what it cannot count — `counters()` as its bare separator. */
|
||||
.paper.pdf-list-markers li > :is(p, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10)::before {
|
||||
content: none;
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
.paper .tiptap li {
|
||||
@@ -957,8 +964,9 @@
|
||||
|
||||
/* After an inline atom ProseMirror's trailing-break BR adds a line the block has not
|
||||
got, so a paragraph holding an image renders one too tall. Kept where the block really
|
||||
does end in a break — bare, or wrapped in the mark spans of the run that carried it. */
|
||||
.paper .tiptap :not(br):not(:has(br:last-child)) + br.ProseMirror-trailingBreak {
|
||||
does end in a break — bare, wrapped in the mark spans of the run that carried it, or
|
||||
behind the floated marker the PDF clone injects (which leaves the block empty). */
|
||||
.paper .tiptap :not(br):not([data-pdf-marker]):not(:has(br:last-child)) + br.ProseMirror-trailingBreak {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user