Changes
Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
co-authored by
renee-png
parent
de39281fba
commit
99bb03c9dc
@@ -359,12 +359,14 @@ export function CustomFormBuilder() {
|
||||
editor.chain().focus().updateAttributes("paragraph", { caption: !cur }).run();
|
||||
};
|
||||
|
||||
// Linearized document segments for export. Tables now carry per-cell border data.
|
||||
type TableCellSeg = { text: string; borders: CellBorders; isHeader: boolean };
|
||||
// Linearized document segments for export. Inline runs preserve bold/italic/underline.
|
||||
// Newlines inside `text` (from <br> or block splits inside cells) are honored on render.
|
||||
type InlineRun = { text: string; bold?: boolean; italic?: boolean; underline?: boolean };
|
||||
type TableCellSeg = { runs: InlineRun[]; borders: CellBorders; isHeader: boolean };
|
||||
type Segment =
|
||||
| {
|
||||
kind: "text";
|
||||
text: string;
|
||||
runs: InlineRun[];
|
||||
align: "left" | "center" | "right" | "justify";
|
||||
indent: number;
|
||||
caption?: boolean;
|
||||
@@ -372,6 +374,73 @@ export function CustomFormBuilder() {
|
||||
}
|
||||
| { kind: "table"; rows: TableCellSeg[][] };
|
||||
|
||||
// Walk a DOM subtree, emitting inline runs with bold/italic/underline marks.
|
||||
// <br> becomes a newline character within the current run stream.
|
||||
const collectRuns = (
|
||||
node: Node,
|
||||
marks: { bold?: boolean; italic?: boolean; underline?: boolean } = {},
|
||||
): InlineRun[] => {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
const t = node.textContent ?? "";
|
||||
if (!t) return [];
|
||||
return [{ text: t, ...marks }];
|
||||
}
|
||||
if (node.nodeType !== Node.ELEMENT_NODE) return [];
|
||||
const el = node as HTMLElement;
|
||||
const tag = el.tagName.toLowerCase();
|
||||
if (tag === "br") return [{ text: "\n", ...marks }];
|
||||
const next = { ...marks };
|
||||
if (tag === "strong" || tag === "b") next.bold = true;
|
||||
if (tag === "em" || tag === "i") next.italic = true;
|
||||
if (tag === "u") next.underline = true;
|
||||
// Honor inline style overrides too
|
||||
const fw = el.style.fontWeight;
|
||||
if (fw && (fw === "bold" || parseInt(fw, 10) >= 600)) next.bold = true;
|
||||
if (el.style.fontStyle === "italic") next.italic = true;
|
||||
const td = el.style.textDecoration || el.style.textDecorationLine;
|
||||
if (td && td.includes("underline")) next.underline = true;
|
||||
const out: InlineRun[] = [];
|
||||
el.childNodes.forEach((child) => out.push(...collectRuns(child, next)));
|
||||
return out;
|
||||
};
|
||||
|
||||
// Merge adjacent runs with identical marks for cleaner output.
|
||||
const mergeRuns = (runs: InlineRun[]): InlineRun[] => {
|
||||
const out: InlineRun[] = [];
|
||||
for (const r of runs) {
|
||||
if (!r.text) continue;
|
||||
const last = out[out.length - 1];
|
||||
if (
|
||||
last &&
|
||||
!!last.bold === !!r.bold &&
|
||||
!!last.italic === !!r.italic &&
|
||||
!!last.underline === !!r.underline
|
||||
) {
|
||||
last.text += r.text;
|
||||
} else {
|
||||
out.push({ ...r });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
const collectCellRuns = (cell: HTMLElement): InlineRun[] => {
|
||||
// Cells may contain multiple <p> blocks — join them with a newline.
|
||||
const blocks = Array.from(cell.children).filter((c) =>
|
||||
["p", "h1", "h2", "h3", "div", "li"].includes((c as HTMLElement).tagName.toLowerCase()),
|
||||
) as HTMLElement[];
|
||||
let runs: InlineRun[] = [];
|
||||
if (blocks.length === 0) {
|
||||
runs = collectRuns(cell);
|
||||
} else {
|
||||
blocks.forEach((b, i) => {
|
||||
if (i > 0) runs.push({ text: "\n" });
|
||||
runs.push(...collectRuns(b));
|
||||
});
|
||||
}
|
||||
return mergeRuns(runs);
|
||||
};
|
||||
|
||||
const getRenderedSegments = (): Segment[] => {
|
||||
if (!editor) return [];
|
||||
const html = editor.getHTML();
|
||||
@@ -379,8 +448,6 @@ export function CustomFormBuilder() {
|
||||
tmp.innerHTML = html;
|
||||
const out: Segment[] = [];
|
||||
|
||||
const cellText = (cell: HTMLElement) => (cell.textContent ?? "").replace(/\s+/g, " ").trim();
|
||||
|
||||
const pushBlock = (el: HTMLElement) => {
|
||||
const tag = el.tagName.toLowerCase();
|
||||
if (tag === "table") {
|
||||
@@ -389,7 +456,7 @@ export function CustomFormBuilder() {
|
||||
const cells = Array.from(tr.children) as HTMLElement[];
|
||||
rows.push(
|
||||
cells.map((c) => ({
|
||||
text: cellText(c),
|
||||
runs: collectCellRuns(c),
|
||||
borders: parseBordersFromAttr(c.getAttribute("data-borders") ?? "trbl"),
|
||||
isHeader: c.tagName.toLowerCase() === "th",
|
||||
})),
|
||||
@@ -405,7 +472,7 @@ export function CustomFormBuilder() {
|
||||
const heading = tag === "h1" ? 1 : tag === "h2" ? 2 : tag === "h3" ? 3 : 0;
|
||||
out.push({
|
||||
kind: "text",
|
||||
text: el.textContent || "",
|
||||
runs: mergeRuns(collectRuns(el)),
|
||||
align,
|
||||
indent: indentPx,
|
||||
caption,
|
||||
|
||||
Reference in New Issue
Block a user