@undevy-org/tablewright 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,17 +8,39 @@ function resolveTableExportButtonSize(size) {
8
8
  function resolveTableExportButtonVariant(variant) {
9
9
  return variant ?? "secondary";
10
10
  }
11
+ const PLAIN_DECIMAL_CORE = /^(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?$/;
12
+ const FORMULA_TRIGGER_PREFIX = /^[=+\-@\t\r]/;
11
13
  function escapeCsvField(value) {
12
14
  if (/[",\r\n]/.test(value)) {
13
15
  return `"${value.replace(/"/g, '""')}"`;
14
16
  }
15
17
  return value;
16
18
  }
19
+ function isSignedPlainDecimal(value) {
20
+ const first = value[0];
21
+ if (first !== "+" && first !== "-") {
22
+ return false;
23
+ }
24
+ return PLAIN_DECIMAL_CORE.test(value.slice(1));
25
+ }
26
+ function shouldPrefixFormulaEscape(value) {
27
+ if (!FORMULA_TRIGGER_PREFIX.test(value)) {
28
+ return false;
29
+ }
30
+ if (isSignedPlainDecimal(value)) {
31
+ return false;
32
+ }
33
+ return true;
34
+ }
35
+ function formatCsvField(value, escapeFormulas) {
36
+ const prepared = escapeFormulas && shouldPrefixFormulaEscape(value) ? `'${value}` : value;
37
+ return escapeCsvField(prepared);
38
+ }
17
39
  function exportTableToCsv(options) {
18
- const { columns, rows, getCellText } = options;
19
- const headerLine = columns.map((column) => escapeCsvField(column.header)).join(",");
40
+ const { columns, rows, getCellText, escapeFormulas = true } = options;
41
+ const headerLine = columns.map((column) => formatCsvField(column.header, escapeFormulas)).join(",");
20
42
  const bodyLines = rows.map(
21
- (row) => columns.map((column) => escapeCsvField(getCellText(row, column.id))).join(",")
43
+ (row) => columns.map((column) => formatCsvField(getCellText(row, column.id), escapeFormulas)).join(",")
22
44
  );
23
45
  return [headerLine, ...bodyLines].join("\r\n");
24
46
  }
@@ -1824,17 +1824,39 @@ function resolveTableExportButtonSize(size) {
1824
1824
  function resolveTableExportButtonVariant(variant) {
1825
1825
  return variant ?? "secondary";
1826
1826
  }
1827
+ const PLAIN_DECIMAL_CORE = /^(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?$/;
1828
+ const FORMULA_TRIGGER_PREFIX = /^[=+\-@\t\r]/;
1827
1829
  function escapeCsvField(value) {
1828
1830
  if (/[",\r\n]/.test(value)) {
1829
1831
  return `"${value.replace(/"/g, '""')}"`;
1830
1832
  }
1831
1833
  return value;
1832
1834
  }
1835
+ function isSignedPlainDecimal(value) {
1836
+ const first = value[0];
1837
+ if (first !== "+" && first !== "-") {
1838
+ return false;
1839
+ }
1840
+ return PLAIN_DECIMAL_CORE.test(value.slice(1));
1841
+ }
1842
+ function shouldPrefixFormulaEscape(value) {
1843
+ if (!FORMULA_TRIGGER_PREFIX.test(value)) {
1844
+ return false;
1845
+ }
1846
+ if (isSignedPlainDecimal(value)) {
1847
+ return false;
1848
+ }
1849
+ return true;
1850
+ }
1851
+ function formatCsvField(value, escapeFormulas) {
1852
+ const prepared = escapeFormulas && shouldPrefixFormulaEscape(value) ? `'${value}` : value;
1853
+ return escapeCsvField(prepared);
1854
+ }
1833
1855
  function exportTableToCsv(options) {
1834
- const { columns, rows, getCellText } = options;
1835
- const headerLine = columns.map((column) => escapeCsvField(column.header)).join(",");
1856
+ const { columns, rows, getCellText, escapeFormulas = true } = options;
1857
+ const headerLine = columns.map((column) => formatCsvField(column.header, escapeFormulas)).join(",");
1836
1858
  const bodyLines = rows.map(
1837
- (row) => columns.map((column) => escapeCsvField(getCellText(row, column.id))).join(",")
1859
+ (row) => columns.map((column) => formatCsvField(getCellText(row, column.id), escapeFormulas)).join(",")
1838
1860
  );
1839
1861
  return [headerLine, ...bodyLines].join("\r\n");
1840
1862
  }
@@ -6,6 +6,11 @@ export type ExportTableToCsvOptions<TRow> = {
6
6
  }[];
7
7
  rows: TRow[];
8
8
  getCellText: (row: TRow, columnId: string) => string;
9
+ /**
10
+ * Prefix formula-triggering cell text with `'` so spreadsheet apps do not evaluate it.
11
+ * Plain decimal numbers (`-5`, `+3.2`) are left unchanged. @default true
12
+ */
13
+ escapeFormulas?: boolean;
9
14
  };
10
15
  export declare const DEFAULT_CSV_EXPORT_LABEL = "Export CSV";
11
16
  export declare function resolveCsvExportLabel(labels?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@undevy-org/tablewright",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "React data-table and application-shell components: filters, drawers, row controls, sortable and resizable columns, built on Radix UI and TanStack Table.",
5
5
  "license": "MIT",
6
6
  "repository": {