@undevy-org/tablewright 0.7.10 → 0.8.0
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.
- package/dist/es/components/data-table/TableCsvExportButton.js +48 -0
- package/dist/es/index.js +5 -0
- package/dist/es/lib/export-table-csv.js +82 -0
- package/dist/tablewright.cjs +115 -0
- package/dist/types/components/data-table/TableCsvExportButton.d.ts +20 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/lib/export-table-csv.d.ts +26 -0
- package/package.json +1 -1
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FileDown } from "lucide-react";
|
|
3
|
+
import { resolveCsvExportLabel, resolveExportColumnsFromTable, exportTableToCsv, downloadCsvFile, resolveTableExportButtonSize, resolveTableExportButtonVariant, defaultTableRowCellText } from "../../lib/export-table-csv.js";
|
|
4
|
+
import { cn } from "../../lib/utils.js";
|
|
5
|
+
import { Button } from "../ui/button.js";
|
|
6
|
+
function TableCsvExportButton({
|
|
7
|
+
table,
|
|
8
|
+
filename,
|
|
9
|
+
getCellText,
|
|
10
|
+
utf8Bom,
|
|
11
|
+
columnIds,
|
|
12
|
+
labels,
|
|
13
|
+
className,
|
|
14
|
+
variant,
|
|
15
|
+
size
|
|
16
|
+
}) {
|
|
17
|
+
const buttonVariant = resolveTableExportButtonVariant(variant);
|
|
18
|
+
const buttonSize = resolveTableExportButtonSize(size);
|
|
19
|
+
const exportLabel = resolveCsvExportLabel(labels);
|
|
20
|
+
const handleExport = () => {
|
|
21
|
+
const columns = resolveExportColumnsFromTable(table, columnIds, labels?.columns);
|
|
22
|
+
const rows = table.getRowModel().rows.map((row) => row.original);
|
|
23
|
+
const resolveCellText = getCellText ?? ((row, columnId) => defaultTableRowCellText(row, columnId, table));
|
|
24
|
+
const csv = exportTableToCsv({
|
|
25
|
+
columns,
|
|
26
|
+
rows,
|
|
27
|
+
getCellText: resolveCellText
|
|
28
|
+
});
|
|
29
|
+
downloadCsvFile(csv, filename, { utf8Bom });
|
|
30
|
+
};
|
|
31
|
+
return /* @__PURE__ */ jsxs(
|
|
32
|
+
Button,
|
|
33
|
+
{
|
|
34
|
+
type: "button",
|
|
35
|
+
variant: buttonVariant,
|
|
36
|
+
size: buttonSize,
|
|
37
|
+
className: cn("shrink-0", className),
|
|
38
|
+
onClick: handleExport,
|
|
39
|
+
children: [
|
|
40
|
+
/* @__PURE__ */ jsx(FileDown, { className: "h-4 w-4" }),
|
|
41
|
+
exportLabel
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
TableCsvExportButton
|
|
48
|
+
};
|
package/dist/es/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import { RowActionsMenu } from "./components/data-table/RowActionsMenu.js";
|
|
|
32
32
|
import { RowControlCell, RowControlHeader } from "./components/data-table/RowControlCell.js";
|
|
33
33
|
import { SortableHeader } from "./components/data-table/SortableHeader.js";
|
|
34
34
|
import { TableFooter } from "./components/data-table/TableFooter.js";
|
|
35
|
+
import { TableCsvExportButton } from "./components/data-table/TableCsvExportButton.js";
|
|
35
36
|
import { TableLayout } from "./components/data-table/TableLayout.js";
|
|
36
37
|
import { useColumnResize } from "./components/data-table/hooks/use-column-resize.js";
|
|
37
38
|
import { getVisiblePageNumbers } from "./components/data-table/hooks/use-table-pagination.js";
|
|
@@ -65,6 +66,7 @@ import { getCompoundValue, getDateRange, getNumberRange, getStringArray, isFilte
|
|
|
65
66
|
import { useTableOrchestration } from "./hooks/useTableOrchestration.js";
|
|
66
67
|
import { createTableOrchestrationStore, defaultValueForFilter } from "./hooks/table-orchestration-store.js";
|
|
67
68
|
import { useRowDragOverride } from "./hooks/useRowDragOverride.js";
|
|
69
|
+
import { downloadCsvFile, exportTableToCsv } from "./lib/export-table-csv.js";
|
|
68
70
|
import { computeColumnGaps, gapColumnId } from "./lib/column-gaps.js";
|
|
69
71
|
import { computeColumnStats } from "./lib/filter-stats.js";
|
|
70
72
|
import { formatEnumLabel } from "./lib/format-enum.js";
|
|
@@ -162,6 +164,7 @@ export {
|
|
|
162
164
|
SidebarAccountMenu,
|
|
163
165
|
SidebarUser,
|
|
164
166
|
SortableHeader,
|
|
167
|
+
TableCsvExportButton,
|
|
165
168
|
TableFooter,
|
|
166
169
|
TableLayout,
|
|
167
170
|
Tabs,
|
|
@@ -183,6 +186,8 @@ export {
|
|
|
183
186
|
createTableOrchestrationStore,
|
|
184
187
|
defaultValueForFilter,
|
|
185
188
|
displayTimeZone,
|
|
189
|
+
downloadCsvFile,
|
|
190
|
+
exportTableToCsv,
|
|
186
191
|
formatDateParts,
|
|
187
192
|
formatEnumLabel,
|
|
188
193
|
formatLiveTimestamp,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const DEFAULT_CSV_EXPORT_LABEL = "Export CSV";
|
|
2
|
+
function resolveCsvExportLabel(labels) {
|
|
3
|
+
return labels?.export ?? DEFAULT_CSV_EXPORT_LABEL;
|
|
4
|
+
}
|
|
5
|
+
function resolveTableExportButtonSize(size) {
|
|
6
|
+
return size ?? "default";
|
|
7
|
+
}
|
|
8
|
+
function resolveTableExportButtonVariant(variant) {
|
|
9
|
+
return variant ?? "secondary";
|
|
10
|
+
}
|
|
11
|
+
function escapeCsvField(value) {
|
|
12
|
+
if (/[",\r\n]/.test(value)) {
|
|
13
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function exportTableToCsv(options) {
|
|
18
|
+
const { columns, rows, getCellText } = options;
|
|
19
|
+
const headerLine = columns.map((column) => escapeCsvField(column.header)).join(",");
|
|
20
|
+
const bodyLines = rows.map(
|
|
21
|
+
(row) => columns.map((column) => escapeCsvField(getCellText(row, column.id))).join(",")
|
|
22
|
+
);
|
|
23
|
+
return [headerLine, ...bodyLines].join("\r\n");
|
|
24
|
+
}
|
|
25
|
+
function downloadCsvFile(content, filename, options) {
|
|
26
|
+
const utf8Bom = options?.utf8Bom ?? false;
|
|
27
|
+
const payload = utf8Bom ? `\uFEFF${content}` : content;
|
|
28
|
+
const blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
|
|
29
|
+
const url = URL.createObjectURL(blob);
|
|
30
|
+
const anchor = document.createElement("a");
|
|
31
|
+
anchor.href = url;
|
|
32
|
+
anchor.download = filename.endsWith(".csv") ? filename : `${filename}.csv`;
|
|
33
|
+
anchor.rel = "noopener";
|
|
34
|
+
anchor.style.display = "none";
|
|
35
|
+
document.body.appendChild(anchor);
|
|
36
|
+
anchor.click();
|
|
37
|
+
document.body.removeChild(anchor);
|
|
38
|
+
URL.revokeObjectURL(url);
|
|
39
|
+
}
|
|
40
|
+
function resolveColumnHeader(table, columnId, columnLabels) {
|
|
41
|
+
if (columnLabels?.[columnId]) {
|
|
42
|
+
return columnLabels[columnId];
|
|
43
|
+
}
|
|
44
|
+
const column = table.getColumn(columnId);
|
|
45
|
+
if (!column) {
|
|
46
|
+
return columnId;
|
|
47
|
+
}
|
|
48
|
+
const header = column.columnDef.header;
|
|
49
|
+
return typeof header === "string" ? header : columnId;
|
|
50
|
+
}
|
|
51
|
+
function resolveExportColumnsFromTable(table, columnIds, columnLabels) {
|
|
52
|
+
const ids = columnIds ?? table.getVisibleLeafColumns().map((column) => column.id);
|
|
53
|
+
return ids.map((id) => ({
|
|
54
|
+
id,
|
|
55
|
+
header: resolveColumnHeader(table, id, columnLabels)
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
function defaultTableRowCellText(row, columnId, table) {
|
|
59
|
+
const tableRow = table.getRowModel().rows.find((candidate) => candidate.original === row);
|
|
60
|
+
if (!tableRow) {
|
|
61
|
+
return "";
|
|
62
|
+
}
|
|
63
|
+
const cell = tableRow.getAllCells().find((candidate) => candidate.column.id === columnId);
|
|
64
|
+
if (!cell) {
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
const value = cell.getValue();
|
|
68
|
+
if (value == null) {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
return String(value);
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
DEFAULT_CSV_EXPORT_LABEL,
|
|
75
|
+
defaultTableRowCellText,
|
|
76
|
+
downloadCsvFile,
|
|
77
|
+
exportTableToCsv,
|
|
78
|
+
resolveCsvExportLabel,
|
|
79
|
+
resolveExportColumnsFromTable,
|
|
80
|
+
resolveTableExportButtonSize,
|
|
81
|
+
resolveTableExportButtonVariant
|
|
82
|
+
};
|
package/dist/tablewright.cjs
CHANGED
|
@@ -1814,6 +1814,118 @@ function renderNumericPager(props) {
|
|
|
1814
1814
|
)
|
|
1815
1815
|
] });
|
|
1816
1816
|
}
|
|
1817
|
+
const DEFAULT_CSV_EXPORT_LABEL = "Export CSV";
|
|
1818
|
+
function resolveCsvExportLabel(labels) {
|
|
1819
|
+
return labels?.export ?? DEFAULT_CSV_EXPORT_LABEL;
|
|
1820
|
+
}
|
|
1821
|
+
function resolveTableExportButtonSize(size) {
|
|
1822
|
+
return size ?? "default";
|
|
1823
|
+
}
|
|
1824
|
+
function resolveTableExportButtonVariant(variant) {
|
|
1825
|
+
return variant ?? "secondary";
|
|
1826
|
+
}
|
|
1827
|
+
function escapeCsvField(value) {
|
|
1828
|
+
if (/[",\r\n]/.test(value)) {
|
|
1829
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
1830
|
+
}
|
|
1831
|
+
return value;
|
|
1832
|
+
}
|
|
1833
|
+
function exportTableToCsv(options) {
|
|
1834
|
+
const { columns, rows, getCellText } = options;
|
|
1835
|
+
const headerLine = columns.map((column) => escapeCsvField(column.header)).join(",");
|
|
1836
|
+
const bodyLines = rows.map(
|
|
1837
|
+
(row) => columns.map((column) => escapeCsvField(getCellText(row, column.id))).join(",")
|
|
1838
|
+
);
|
|
1839
|
+
return [headerLine, ...bodyLines].join("\r\n");
|
|
1840
|
+
}
|
|
1841
|
+
function downloadCsvFile(content, filename, options) {
|
|
1842
|
+
const utf8Bom = options?.utf8Bom ?? false;
|
|
1843
|
+
const payload = utf8Bom ? `\uFEFF${content}` : content;
|
|
1844
|
+
const blob = new Blob([payload], { type: "text/csv;charset=utf-8" });
|
|
1845
|
+
const url = URL.createObjectURL(blob);
|
|
1846
|
+
const anchor = document.createElement("a");
|
|
1847
|
+
anchor.href = url;
|
|
1848
|
+
anchor.download = filename.endsWith(".csv") ? filename : `${filename}.csv`;
|
|
1849
|
+
anchor.rel = "noopener";
|
|
1850
|
+
anchor.style.display = "none";
|
|
1851
|
+
document.body.appendChild(anchor);
|
|
1852
|
+
anchor.click();
|
|
1853
|
+
document.body.removeChild(anchor);
|
|
1854
|
+
URL.revokeObjectURL(url);
|
|
1855
|
+
}
|
|
1856
|
+
function resolveColumnHeader(table, columnId, columnLabels) {
|
|
1857
|
+
if (columnLabels?.[columnId]) {
|
|
1858
|
+
return columnLabels[columnId];
|
|
1859
|
+
}
|
|
1860
|
+
const column = table.getColumn(columnId);
|
|
1861
|
+
if (!column) {
|
|
1862
|
+
return columnId;
|
|
1863
|
+
}
|
|
1864
|
+
const header = column.columnDef.header;
|
|
1865
|
+
return typeof header === "string" ? header : columnId;
|
|
1866
|
+
}
|
|
1867
|
+
function resolveExportColumnsFromTable(table, columnIds, columnLabels) {
|
|
1868
|
+
const ids = columnIds ?? table.getVisibleLeafColumns().map((column) => column.id);
|
|
1869
|
+
return ids.map((id) => ({
|
|
1870
|
+
id,
|
|
1871
|
+
header: resolveColumnHeader(table, id, columnLabels)
|
|
1872
|
+
}));
|
|
1873
|
+
}
|
|
1874
|
+
function defaultTableRowCellText(row, columnId, table) {
|
|
1875
|
+
const tableRow = table.getRowModel().rows.find((candidate) => candidate.original === row);
|
|
1876
|
+
if (!tableRow) {
|
|
1877
|
+
return "";
|
|
1878
|
+
}
|
|
1879
|
+
const cell = tableRow.getAllCells().find((candidate) => candidate.column.id === columnId);
|
|
1880
|
+
if (!cell) {
|
|
1881
|
+
return "";
|
|
1882
|
+
}
|
|
1883
|
+
const value = cell.getValue();
|
|
1884
|
+
if (value == null) {
|
|
1885
|
+
return "";
|
|
1886
|
+
}
|
|
1887
|
+
return String(value);
|
|
1888
|
+
}
|
|
1889
|
+
function TableCsvExportButton({
|
|
1890
|
+
table,
|
|
1891
|
+
filename,
|
|
1892
|
+
getCellText,
|
|
1893
|
+
utf8Bom,
|
|
1894
|
+
columnIds,
|
|
1895
|
+
labels,
|
|
1896
|
+
className,
|
|
1897
|
+
variant,
|
|
1898
|
+
size
|
|
1899
|
+
}) {
|
|
1900
|
+
const buttonVariant = resolveTableExportButtonVariant(variant);
|
|
1901
|
+
const buttonSize = resolveTableExportButtonSize(size);
|
|
1902
|
+
const exportLabel = resolveCsvExportLabel(labels);
|
|
1903
|
+
const handleExport = () => {
|
|
1904
|
+
const columns = resolveExportColumnsFromTable(table, columnIds, labels?.columns);
|
|
1905
|
+
const rows = table.getRowModel().rows.map((row) => row.original);
|
|
1906
|
+
const resolveCellText = getCellText ?? ((row, columnId) => defaultTableRowCellText(row, columnId, table));
|
|
1907
|
+
const csv = exportTableToCsv({
|
|
1908
|
+
columns,
|
|
1909
|
+
rows,
|
|
1910
|
+
getCellText: resolveCellText
|
|
1911
|
+
});
|
|
1912
|
+
downloadCsvFile(csv, filename, { utf8Bom });
|
|
1913
|
+
};
|
|
1914
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1915
|
+
Button,
|
|
1916
|
+
{
|
|
1917
|
+
type: "button",
|
|
1918
|
+
variant: buttonVariant,
|
|
1919
|
+
size: buttonSize,
|
|
1920
|
+
className: cn("shrink-0", className),
|
|
1921
|
+
onClick: handleExport,
|
|
1922
|
+
children: [
|
|
1923
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileDown, { className: "h-4 w-4" }),
|
|
1924
|
+
exportLabel
|
|
1925
|
+
]
|
|
1926
|
+
}
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1817
1929
|
function useColumnResize(defaults, initialWidths) {
|
|
1818
1930
|
const [widths, setWidths] = React.useState(() => {
|
|
1819
1931
|
const w = {};
|
|
@@ -5943,6 +6055,7 @@ exports.Sidebar = Sidebar;
|
|
|
5943
6055
|
exports.SidebarAccountMenu = SidebarAccountMenu;
|
|
5944
6056
|
exports.SidebarUser = SidebarUser;
|
|
5945
6057
|
exports.SortableHeader = SortableHeader;
|
|
6058
|
+
exports.TableCsvExportButton = TableCsvExportButton;
|
|
5946
6059
|
exports.TableFooter = TableFooter;
|
|
5947
6060
|
exports.TableLayout = TableLayout;
|
|
5948
6061
|
exports.Tabs = Tabs;
|
|
@@ -5964,6 +6077,8 @@ exports.computeColumnStats = computeColumnStats;
|
|
|
5964
6077
|
exports.createTableOrchestrationStore = createTableOrchestrationStore;
|
|
5965
6078
|
exports.defaultValueForFilter = defaultValueForFilter;
|
|
5966
6079
|
exports.displayTimeZone = displayTimeZone;
|
|
6080
|
+
exports.downloadCsvFile = downloadCsvFile;
|
|
6081
|
+
exports.exportTableToCsv = exportTableToCsv;
|
|
5967
6082
|
exports.formatDateParts = formatDateParts;
|
|
5968
6083
|
exports.formatEnumLabel = formatEnumLabel;
|
|
5969
6084
|
exports.formatLiveTimestamp = formatLiveTimestamp;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Table } from "@tanstack/react-table";
|
|
2
|
+
import { type ButtonProps } from "../ui/button.js";
|
|
3
|
+
export type TableCsvExportButtonLabels = {
|
|
4
|
+
/** @default "Export CSV" */
|
|
5
|
+
export?: string;
|
|
6
|
+
columns?: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
export type TableCsvExportButtonProps<TData> = {
|
|
9
|
+
table: Table<TData>;
|
|
10
|
+
filename: string;
|
|
11
|
+
getCellText?: (row: TData, columnId: string) => string;
|
|
12
|
+
utf8Bom?: boolean;
|
|
13
|
+
/** Defaults to visible leaf columns on the table instance. */
|
|
14
|
+
columnIds?: string[];
|
|
15
|
+
labels?: TableCsvExportButtonLabels;
|
|
16
|
+
className?: string;
|
|
17
|
+
variant?: ButtonProps["variant"];
|
|
18
|
+
size?: ButtonProps["size"];
|
|
19
|
+
};
|
|
20
|
+
export declare function TableCsvExportButton<TData>({ table, filename, getCellText, utf8Bom, columnIds, labels, className, variant, size, }: TableCsvExportButtonProps<TData>): import("react").JSX.Element;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -46,6 +46,8 @@ export { RowControlHeader } from "./components/data-table/RowControlCell.js";
|
|
|
46
46
|
export type { RowControlHeaderProps } from "./components/data-table/RowControlCell.js";
|
|
47
47
|
export { SortableHeader } from "./components/data-table/SortableHeader.js";
|
|
48
48
|
export { TableFooter } from "./components/data-table/TableFooter.js";
|
|
49
|
+
export { TableCsvExportButton } from "./components/data-table/TableCsvExportButton.js";
|
|
50
|
+
export type { TableCsvExportButtonProps } from "./components/data-table/TableCsvExportButton.js";
|
|
49
51
|
export { TableLayout } from "./components/data-table/TableLayout.js";
|
|
50
52
|
export { useColumnResize } from "./components/data-table/hooks/use-column-resize.js";
|
|
51
53
|
export { getVisiblePageNumbers } from "./components/data-table/hooks/use-table-pagination.js";
|
|
@@ -100,6 +102,8 @@ export { createTableOrchestrationStore, defaultValueForFilter, } from "./hooks/t
|
|
|
100
102
|
export type { TableOrchestrationState, TableOrchestrationActions, TableOrchestrationStore, TableOrchestrationStoreApi, } from "./hooks/table-orchestration-store.js";
|
|
101
103
|
export { useRowDragOverride } from "./hooks/useRowDragOverride.js";
|
|
102
104
|
export type { UseRowDragOverrideResult } from "./hooks/useRowDragOverride.js";
|
|
105
|
+
export { downloadCsvFile, exportTableToCsv, } from "./lib/export-table-csv.js";
|
|
106
|
+
export type { DownloadCsvFileOptions, ExportTableToCsvOptions, } from "./lib/export-table-csv.js";
|
|
103
107
|
export { computeColumnGaps, gapColumnId } from "./lib/column-gaps.js";
|
|
104
108
|
export { computeColumnStats } from "./lib/filter-stats.js";
|
|
105
109
|
export { formatEnumLabel } from "./lib/format-enum.js";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Table } from "@tanstack/react-table";
|
|
2
|
+
export type ExportTableToCsvOptions<TRow> = {
|
|
3
|
+
columns: {
|
|
4
|
+
id: string;
|
|
5
|
+
header: string;
|
|
6
|
+
}[];
|
|
7
|
+
rows: TRow[];
|
|
8
|
+
getCellText: (row: TRow, columnId: string) => string;
|
|
9
|
+
};
|
|
10
|
+
export declare const DEFAULT_CSV_EXPORT_LABEL = "Export CSV";
|
|
11
|
+
export declare function resolveCsvExportLabel(labels?: {
|
|
12
|
+
export?: string;
|
|
13
|
+
}): string;
|
|
14
|
+
export declare function resolveTableExportButtonSize<TSize extends string | null | undefined>(size: TSize): NonNullable<TSize> | "default";
|
|
15
|
+
export declare function resolveTableExportButtonVariant<TVariant extends string | null | undefined>(variant: TVariant): NonNullable<TVariant> | "secondary";
|
|
16
|
+
export type DownloadCsvFileOptions = {
|
|
17
|
+
/** Prepends a UTF-8 BOM so Excel recognizes UTF-8. @default false */
|
|
18
|
+
utf8Bom?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare function exportTableToCsv<TRow>(options: ExportTableToCsvOptions<TRow>): string;
|
|
21
|
+
export declare function downloadCsvFile(content: string, filename: string, options?: DownloadCsvFileOptions): void;
|
|
22
|
+
export declare function resolveExportColumnsFromTable<TData>(table: Table<TData>, columnIds?: string[], columnLabels?: Record<string, string>): {
|
|
23
|
+
id: string;
|
|
24
|
+
header: string;
|
|
25
|
+
}[];
|
|
26
|
+
export declare function defaultTableRowCellText<TData>(row: TData, columnId: string, table: Table<TData>): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@undevy-org/tablewright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
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": {
|