feat: support paragraph bar borders#2736
Conversation
caio-pizzol
left a comment
There was a problem hiding this comment.
@iguit0 nice work — the bar rendering is the best of the three apps we tested. Word ignores bar borders entirely, Google Docs renders them but loses the color, SuperDoc gets it right.
one thing to look at: both Word and Google Docs ignore bar when deciding whether to group paragraphs together. right now SuperDoc breaks the group when bars differ, but the other two don't. left an inline comment with more detail.
couple of small cleanup suggestions in the other comments. tests look good.
| if (borders.right) parts.push(`r:[${hashParagraphBorder(borders.right)}]`); | ||
| if (borders.bottom) parts.push(`b:[${hashParagraphBorder(borders.bottom)}]`); | ||
| if (borders.left) parts.push(`l:[${hashParagraphBorder(borders.left)}]`); | ||
| if (borders.bar) parts.push(`bar:[${hashParagraphBorder(borders.bar)}]`); |
There was a problem hiding this comment.
we tested this file in Word and Google Docs — both still group paragraphs together even when their bar borders are different. including bar here breaks that grouping in SuperDoc. the bar should still trigger a re-render when it changes, but it shouldn't affect whether paragraphs are grouped for between borders. worth splitting those two?
| @@ -689,6 +722,25 @@ describe('computeBetweenBorderFlags', () => { | |||
| expect(computeBetweenBorderFlags(fragments, lookup).size).toBe(0); | |||
| }); | |||
There was a problem hiding this comment.
heads up — this test expects bar-only differences to break grouping, but Word and Google Docs both keep them grouped. if the grouping change above goes in, this test would need to flip.
| barElement.style.borderLeftStyle = resolvedBar.style; | ||
| barElement.style.borderLeftWidth = `${resolvedBar.width}px`; | ||
| barElement.style.borderLeftColor = resolvedBar.color; |
There was a problem hiding this comment.
these three lines do the same thing as setBorderSideStyle(barElement, 'left', barBorder) — worth reusing it?
| barElement.style.borderLeftStyle = resolvedBar.style; | |
| barElement.style.borderLeftWidth = `${resolvedBar.width}px`; | |
| barElement.style.borderLeftColor = resolvedBar.color; | |
| setBorderSideStyle(barElement, 'left', barBorder); |
| const getParagraphBarElement = (element: HTMLElement): HTMLElement | undefined => { | ||
| return Array.from(element.children).find( | ||
| (child): child is HTMLElement => child instanceof HTMLElement && child.classList.contains(PARAGRAPH_BAR_CLASS), | ||
| ); |
There was a problem hiding this comment.
element.querySelector('.superdoc-paragraph-bar') does the same thing in one line — worth switching?
Summary
Adds support for Word paragraph bar borders (
w:pBdr/w:bar) in the layout/rendering pipeline.w:baris the vertical decorative line rendered on the left edge of a paragraph. It was already parsed upstream, but it never made it through the layout-engine path, so it could not render in DomPainter.Root cause
w:barwas being dropped innormalizeParagraphBorders()inside the pm-adapter layer.Because of that:
ParagraphBordersdid not exposebarbarbarbardata to renderWhat changed
Contracts
bar?: ParagraphBordertopackages/layout-engine/contracts/src/index.tsPM adapter
normalizeParagraphBorders()to preservebarbetweenspecial-case behavior scoped tobetweenonlybarnow normalizes like the other paragraph border sides:spacesupportHashing / invalidation / grouping
Updated the paragraph-border identity paths so bar-only changes are treated as real visual changes:
packages/layout-engine/layout-bridge/src/paragraph-hash-utils.tspackages/layout-engine/painters/dom/src/paragraph-hash-utils.tspackages/layout-engine/layout-bridge/src/diff.tspackages/layout-engine/layout-engine/src/layout-paragraph.tsThis ensures:
barchangesbardo not incorrectly group forbetweenrenderingDomPainter rendering
barrendering inpackages/layout-engine/painters/dom/src/features/paragraph-borders/border-layer.tsbaras a separate absolutely-positioned vertical rule on the left side of the paragraph border boxbarindependent from the normalleftborder so both can coexistbetweenbehaviorImplementation detail:
baris rendered as a dedicated child decoration element attached to the existing paragraph border layerbar.spacestyle,width, andcolorvaluesTest plan
.docxwith a paragraph using onlyw:bar; verify a left-side vertical bar renders..docxwith bothw:leftandw:bar; verify both render independently.w:bar+ paragraph shading renders correctly and the bar stays visible.between.barno longer group.baris a separate decoration element with pointer events disabled.Closes #2282