fix: split wide Claw PDF tables
This commit is contained in:
parent
934bc56202
commit
b91c40f6df
@ -167,6 +167,36 @@ function createPrintOverrides(targetWindow: Window) {
|
||||
border-radius: 10px !important;
|
||||
}
|
||||
|
||||
.claw-pdf-split-table-group {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
break-inside: auto;
|
||||
page-break-inside: auto;
|
||||
}
|
||||
|
||||
.claw-pdf-split-card {
|
||||
width: 100%;
|
||||
margin: 0 0 10px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
break-inside: auto;
|
||||
page-break-inside: auto;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.claw-pdf-split-caption {
|
||||
padding: 5px 8px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
background: #f8fafc;
|
||||
color: #6b7280;
|
||||
font-size: 10px;
|
||||
line-height: 1.4;
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.chat-card-x-scroll {
|
||||
display: block !important;
|
||||
overflow: visible !important;
|
||||
@ -188,16 +218,23 @@ function createPrintOverrides(targetWindow: Window) {
|
||||
border-collapse: collapse !important;
|
||||
page-break-inside: auto;
|
||||
break-inside: auto;
|
||||
font-size: 11px !important;
|
||||
line-height: 1.45 !important;
|
||||
font-size: 12px !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
|
||||
.claw-pdf-split-table {
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
font-size: 11.5px !important;
|
||||
}
|
||||
|
||||
.claw-pdf-wide-table {
|
||||
font-size: 10px !important;
|
||||
font-size: 11px !important;
|
||||
}
|
||||
|
||||
.claw-pdf-extra-wide-table {
|
||||
font-size: 9px !important;
|
||||
font-size: 10px !important;
|
||||
}
|
||||
|
||||
thead {
|
||||
@ -224,7 +261,17 @@ function createPrintOverrides(targetWindow: Window) {
|
||||
text-overflow: clip !important;
|
||||
vertical-align: top !important;
|
||||
padding: 5px 7px !important;
|
||||
line-height: 1.45 !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
|
||||
th *,
|
||||
td * {
|
||||
min-width: 0 !important;
|
||||
max-width: 100% !important;
|
||||
white-space: normal !important;
|
||||
overflow-wrap: anywhere !important;
|
||||
word-break: break-word !important;
|
||||
text-overflow: clip !important;
|
||||
}
|
||||
|
||||
th {
|
||||
@ -274,15 +321,157 @@ function getTableColumnCount(table: HTMLTableElement) {
|
||||
);
|
||||
}
|
||||
|
||||
const PRINTABLE_TABLE_SPLIT_THRESHOLD = 5;
|
||||
const PRINTABLE_TABLE_PINNED_COLUMNS = 1;
|
||||
const PRINTABLE_TABLE_MAX_COLUMNS_PER_SEGMENT = 4;
|
||||
|
||||
function getPrintableTableColumnGroups(columnCount: number) {
|
||||
if (columnCount < PRINTABLE_TABLE_SPLIT_THRESHOLD) return null;
|
||||
|
||||
const pinnedColumnCount = Math.min(
|
||||
PRINTABLE_TABLE_PINNED_COLUMNS,
|
||||
columnCount
|
||||
);
|
||||
const pinnedColumns = Array.from(
|
||||
{ length: pinnedColumnCount },
|
||||
(_, index) => index
|
||||
);
|
||||
const remainingColumns = Array.from(
|
||||
{ length: columnCount - pinnedColumnCount },
|
||||
(_, index) => index + pinnedColumnCount
|
||||
);
|
||||
const chunkSize = Math.max(
|
||||
1,
|
||||
PRINTABLE_TABLE_MAX_COLUMNS_PER_SEGMENT - pinnedColumnCount
|
||||
);
|
||||
|
||||
const columnGroups: number[][] = [];
|
||||
for (let index = 0; index < remainingColumns.length; index += chunkSize) {
|
||||
columnGroups.push([
|
||||
...pinnedColumns,
|
||||
...remainingColumns.slice(index, index + chunkSize),
|
||||
]);
|
||||
}
|
||||
|
||||
return columnGroups.length > 1 ? columnGroups : null;
|
||||
}
|
||||
|
||||
function getPrintableTableRows(table: HTMLTableElement) {
|
||||
let headRows = table.tHead ? Array.from(table.tHead.rows) : [];
|
||||
const allRows = Array.from(table.rows);
|
||||
|
||||
if (!headRows.length && allRows[0]) {
|
||||
const firstRow = allRows[0];
|
||||
if (Array.from(firstRow.cells).some((cell) => cell.tagName === "TH")) {
|
||||
headRows = [firstRow];
|
||||
}
|
||||
}
|
||||
|
||||
const headRowSet = new Set(headRows);
|
||||
const bodyRows = allRows.filter((row) => !headRowSet.has(row));
|
||||
|
||||
return { headRows, bodyRows };
|
||||
}
|
||||
|
||||
function appendSplitTableRows(
|
||||
targetDocument: Document,
|
||||
targetSection: HTMLTableSectionElement,
|
||||
sourceRows: HTMLTableRowElement[],
|
||||
columnIndexes: number[],
|
||||
fallbackCellTag: "td" | "th"
|
||||
) {
|
||||
sourceRows.forEach((sourceRow) => {
|
||||
const targetRow = targetDocument.createElement("tr");
|
||||
columnIndexes.forEach((columnIndex) => {
|
||||
const sourceCell = sourceRow.cells[columnIndex];
|
||||
const cellTag =
|
||||
sourceCell?.tagName.toLowerCase() === "th" || fallbackCellTag === "th"
|
||||
? "th"
|
||||
: "td";
|
||||
const targetCell = targetDocument.createElement(cellTag);
|
||||
if (sourceCell) {
|
||||
targetCell.innerHTML = sourceCell.innerHTML;
|
||||
}
|
||||
targetRow.appendChild(targetCell);
|
||||
});
|
||||
targetSection.appendChild(targetRow);
|
||||
});
|
||||
}
|
||||
|
||||
function createSplitPrintableTableGroup(
|
||||
targetDocument: Document,
|
||||
table: HTMLTableElement,
|
||||
columnGroups: number[][]
|
||||
) {
|
||||
const { headRows, bodyRows } = getPrintableTableRows(table);
|
||||
const group = targetDocument.createElement("div");
|
||||
group.className = "claw-pdf-split-table-group";
|
||||
|
||||
columnGroups.forEach((columnIndexes, index) => {
|
||||
const card = targetDocument.createElement("section");
|
||||
card.className = "claw-pdf-split-card";
|
||||
|
||||
const caption = targetDocument.createElement("div");
|
||||
caption.className = "claw-pdf-split-caption";
|
||||
caption.textContent = `续表 ${index + 1}/${columnGroups.length}`;
|
||||
card.appendChild(caption);
|
||||
|
||||
const splitTable = targetDocument.createElement("table");
|
||||
splitTable.className = "claw-pdf-split-table";
|
||||
splitTable.dataset.pdfColumnCount = String(columnIndexes.length);
|
||||
|
||||
if (headRows.length) {
|
||||
const thead = targetDocument.createElement("thead");
|
||||
appendSplitTableRows(
|
||||
targetDocument,
|
||||
thead,
|
||||
headRows,
|
||||
columnIndexes,
|
||||
"th"
|
||||
);
|
||||
splitTable.appendChild(thead);
|
||||
}
|
||||
|
||||
const tbody = targetDocument.createElement("tbody");
|
||||
appendSplitTableRows(
|
||||
targetDocument,
|
||||
tbody,
|
||||
bodyRows,
|
||||
columnIndexes,
|
||||
"td"
|
||||
);
|
||||
splitTable.appendChild(tbody);
|
||||
|
||||
card.appendChild(splitTable);
|
||||
group.appendChild(card);
|
||||
});
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
function preparePrintableTables(root: HTMLElement) {
|
||||
root.querySelectorAll<HTMLTableElement>("table").forEach((table) => {
|
||||
if (!table.isConnected) return;
|
||||
|
||||
const columnCount = getTableColumnCount(table);
|
||||
const columnGroups = getPrintableTableColumnGroups(columnCount);
|
||||
const scrollWrapper = table.closest(".chat-card-x-scroll");
|
||||
const tableCard = scrollWrapper?.parentElement;
|
||||
|
||||
if (columnGroups) {
|
||||
const splitGroup = createSplitPrintableTableGroup(
|
||||
root.ownerDocument,
|
||||
table,
|
||||
columnGroups
|
||||
);
|
||||
(tableCard || table).replaceWith(splitGroup);
|
||||
return;
|
||||
}
|
||||
|
||||
table.dataset.pdfColumnCount = String(columnCount);
|
||||
table.classList.toggle("claw-pdf-wide-table", columnCount >= 5);
|
||||
table.classList.toggle("claw-pdf-extra-wide-table", columnCount >= 7);
|
||||
|
||||
const scrollWrapper = table.closest(".chat-card-x-scroll");
|
||||
scrollWrapper?.parentElement?.classList.add("claw-pdf-table-card");
|
||||
tableCard?.classList.add("claw-pdf-table-card");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user