/* eslint-disable sonarjs/no-duplicate-string */ import { assertNever } from "../common/support.js"; import { GridCellKind, type GridCell, BooleanEmpty, BooleanIndeterminate, } from "../internal/data-grid/data-grid-types.js"; type StringArrayCellBuffer = { formatted: string[]; rawValue: string[]; format: "string-array"; }; type BasicCellBuffer = { formatted: string; rawValue: string | number | boolean | BooleanEmpty | BooleanIndeterminate | undefined; format: "string" | "number" | "boolean" | "url"; }; export type CellBuffer = StringArrayCellBuffer | BasicCellBuffer; export type CopyBuffer = CellBuffer[][]; function convertCellToBuffer(cell: GridCell): CellBuffer { if (cell.copyData !== undefined) { return { formatted: cell.copyData, rawValue: cell.copyData, format: "string", }; } switch (cell.kind) { case GridCellKind.Boolean: return { formatted: cell.data === true ? "TRUE" : cell.data === false ? "FALSE" : cell.data === BooleanIndeterminate ? "INDETERMINATE" : "", rawValue: cell.data, format: "boolean", }; case GridCellKind.Custom: return { formatted: cell.copyData, rawValue: cell.copyData, format: "string", }; case GridCellKind.Image: case GridCellKind.Bubble: return { formatted: cell.data, rawValue: cell.data, format: "string-array", }; case GridCellKind.Drilldown: return { formatted: cell.data.map(x => x.text), rawValue: cell.data.map(x => x.text), format: "string-array", }; case GridCellKind.Text: return { formatted: cell.displayData ?? cell.data, rawValue: cell.data, format: "string", }; case GridCellKind.Uri: return { formatted: cell.displayData ?? cell.data, rawValue: cell.data, format: "url", }; case GridCellKind.Markdown: case GridCellKind.RowID: return { formatted: cell.data, rawValue: cell.data, format: "string", }; case GridCellKind.Number: return { formatted: cell.displayData, rawValue: cell.data, format: "number", }; case GridCellKind.Loading: return { formatted: "#LOADING", rawValue: "", format: "string", }; case GridCellKind.Protected: return { formatted: "************", rawValue: "", format: "string", }; default: assertNever(cell); } } function createBufferFromGridCells( cells: readonly (readonly GridCell[])[], columnIndexes: readonly number[] ): CopyBuffer { const copyBuffer: CopyBuffer = cells.map((row, index) => { const mappedIndex = columnIndexes[index]; return row.map(cell => { if (cell.span !== undefined && cell.span[0] !== mappedIndex) return { formatted: "", rawValue: "", format: "string", }; return convertCellToBuffer(cell); }); }); return copyBuffer; } function escapeIfNeeded(str: string, withComma: boolean): string { if ((withComma ? /[\t\n",]/ : /[\t\n"]/).test(str)) { str = `"${str.replace(/"/g, '""')}"`; } return str; } function createTextBuffer(copyBuffer: CopyBuffer): string { const lines: string[] = []; for (const row of copyBuffer) { const line: string[] = []; for (const cell of row) { if (cell.format === "url") { line.push(cell.rawValue?.toString() ?? ""); } else if (cell.format === "string-array") { line.push(cell.formatted.map(x => escapeIfNeeded(x, true)).join(",")); } else { line.push(escapeIfNeeded(cell.formatted, false)); } } lines.push(line.join("\t")); } return lines.join("\n"); } function formatHtmlTextContent(text: string): string { // The following formatting for the `html` variable ensures that when pasting, // spaces are preserved in both Google Sheets and Excel. This is done by: // 1. Replacing tabs with four spaces for consistency. Also google sheets disallows any tabs. // 2. Wrapping each space with a span element to prevent them from being collapsed or ignored during the // paste operation return text.replace(/\t/g, " ").replace(/ {2,}/g, match => " ".repeat(match.length)); } function formatHtmlAttributeContent(attrText: string): string { // Escape all quotes, lt, gt, and other special characters return ( '"' + attrText.replace(/&/g, "&").replace(/"/g, """).replace(//g, ">") + '"' ); } function restoreHtmlEntities(str: string): string { // Unescape all quotes, lt, gt, and other special characters return str .replace(/"/g, '"') .replace(/</g, "<") .replace(/>/g, ">") .replace(/&/g, "&"); } function createHtmlBuffer(copyBuffer: CopyBuffer): string { const lines: string[] = []; lines.push(``, "
| ${formatHtmlTextContent(cell.formatted)} | ` ); } else { if (cell.format === "string-array") { lines.push( `
| `
);
} else {
lines.push(
`${formatHtmlTextContent(cell.formatted)} | ` ); } } } lines.push("