Skip to content

Commit 7be361d

Browse files
authored
Merge pull request #21 from jacksonkasi1/dev
feat: Export Configuration Overhaul & Build Stability Fixes
2 parents 922c028 + a71b99a commit 7be361d

File tree

15 files changed

+367
-86
lines changed

15 files changed

+367
-86
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Thumbs.db
2626
# Temporary files
2727
*.tmp
2828
*.temp
29+
request.json
2930

3031
.explore
3132
apps-explore/tnks-data-table

apps/vite-web-example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@base-ui/react": "^1.1.0",
1414
"@fontsource-variable/inter": "^5.2.8",
1515
"@tablecraft/client": "^0.1.8",
16-
"@tablecraft/table": "^0.2.10",
16+
"@tablecraft/table": "workspace:*",
1717
"@tailwindcss/vite": "^4.1.17",
1818
"@tanstack/react-query": "^5.62.8",
1919
"@tanstack/react-table": "^8.20.0",

apps/vite-web-example/src/pages/orders-page.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,28 @@
1-
import { DataTable, defaultColumnOrder } from '@tablecraft/table';
1+
import { DataTable, defaultColumnOrder, defineExportConfig } from '@tablecraft/table';
22
import { createOrdersAdapter, type OrdersRow } from '../generated';
33
import type { OrdersColumn } from '../generated';
44

55
// ** import apis
66
import { API_BASE_URL } from '../api';
77

8+
// ** Type-safe export config — only valid column names are accepted
9+
const exportConfig = defineExportConfig<OrdersRow>()({
10+
entityName: 'orders',
11+
removeHeaders: ['deletedAt', 'tenantId'],
12+
columnMapping: {
13+
createdAt: 'Order Date',
14+
vatAmount: 'VAT Amount',
15+
itemCount: 'Items',
16+
},
17+
transformFunction: (row) => ({
18+
...row,
19+
createdAt: row.createdAt
20+
? new Date(row.createdAt).toLocaleDateString('en-IN')
21+
: '',
22+
total: `₹${row.total.toFixed(2)}`,
23+
}),
24+
});
25+
826
export function OrdersPage() {
927
const adapter = createOrdersAdapter({
1028
baseUrl: API_BASE_URL,
@@ -23,6 +41,7 @@ export function OrdersPage() {
2341
defaultPageSize: 10,
2442
pageSizeOptions: [5, 10, 20, 50],
2543
}}
44+
exportConfig={exportConfig}
2645
defaultColumnOrder={defaultColumnOrder<OrdersColumn>([
2746
'id',
2847
'status',

bun.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* [Features](features.md)
2424
* [Date Filtering](date-filtering.md)
2525
* [Custom Filters](custom-filters.md)
26+
* [Export Configuration](export-config.md)
2627
* [Type Generation](codegen.md)
2728
* [FAQ & Troubleshooting](faq.md)
2829

docs/export-config.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# Export Configuration
2+
3+
TableCraft's `<DataTable>` includes a built-in export system that lets users download table data as **CSV** or **Excel (XLSX)** files. The `exportConfig` prop gives you full control over what gets exported and how.
4+
5+
## Quick Start
6+
7+
```tsx
8+
import { DataTable, defineExportConfig } from '@tablecraft/table';
9+
import type { OrdersRow } from './generated/orders';
10+
11+
const exportConfig = defineExportConfig<OrdersRow>()({
12+
entityName: 'orders',
13+
});
14+
15+
<DataTable<OrdersRow>
16+
adapter={adapter}
17+
config={{ enableExport: true }}
18+
exportConfig={exportConfig}
19+
/>
20+
```
21+
22+
All visible columns are exported with their raw values by default.
23+
24+
---
25+
26+
## Excluding Columns (`removeHeaders`)
27+
28+
Use `removeHeaders` to hide specific columns from the export. All other visible columns are included automatically.
29+
30+
```tsx
31+
const exportConfig = defineExportConfig<OrdersRow>()({
32+
entityName: 'orders',
33+
removeHeaders: ['deletedAt', 'tenantId'],
34+
// everything else is exported
35+
});
36+
```
37+
38+
> **Why `removeHeaders`?** If you have 20 columns and only want to hide 2, it's much simpler to list the 2 to exclude than the 18 to include.
39+
40+
---
41+
42+
## Renaming Columns (`columnMapping`)
43+
44+
Rename column headers in the export without affecting the UI. Works independently from `removeHeaders`.
45+
46+
```tsx
47+
const exportConfig = defineExportConfig<OrdersRow>()({
48+
entityName: 'orders',
49+
removeHeaders: ['deletedAt'],
50+
columnMapping: {
51+
createdAt: 'Order Date',
52+
vatAmount: 'VAT (₹)',
53+
email: 'Customer Email',
54+
},
55+
});
56+
```
57+
58+
| In UI | In Export File |
59+
|-------|---------------|
60+
| `createdAt` | Order Date |
61+
| `vatAmount` | VAT (₹) |
62+
| `email` | Customer Email |
63+
| `status` | status (unchanged) |
64+
65+
---
66+
67+
## Transforming Values (`transformFunction`)
68+
69+
Format values before they're written to the file:
70+
71+
```tsx
72+
const exportConfig = defineExportConfig<OrdersRow>()({
73+
entityName: 'orders',
74+
columnMapping: { createdAt: 'Order Date' },
75+
transformFunction: (row) => ({
76+
...row,
77+
createdAt: row.createdAt
78+
? new Date(row.createdAt).toLocaleDateString('en-IN')
79+
: '',
80+
total: `₹${Number(row.total).toFixed(2)}`,
81+
}),
82+
});
83+
```
84+
85+
> Custom column renderers (JSX from `columnOverrides`) are **not** exported. Use `transformFunction` for custom text in the exported file.
86+
87+
---
88+
89+
## CSV / Excel Toggle
90+
91+
```tsx
92+
const exportConfig = defineExportConfig<OrdersRow>()({
93+
entityName: 'orders',
94+
enableCsv: true, // default: true
95+
enableExcel: false, // disable Excel
96+
});
97+
```
98+
99+
---
100+
101+
## Column Widths (Excel Only)
102+
103+
```tsx
104+
const exportConfig = defineExportConfig<OrdersRow>()({
105+
entityName: 'orders',
106+
removeHeaders: ['deletedAt'],
107+
columnWidths: [
108+
{ wch: 8 }, // first column — narrow
109+
{ wch: 30 }, // second column — wide
110+
{ wch: 12 }, // third column — medium
111+
],
112+
});
113+
```
114+
115+
---
116+
117+
## Cross-Page Export
118+
119+
When users select rows across multiple pages:
120+
- **Same-page selections** — uses in-memory data (no API call)
121+
- **Cross-page selections** — fetches selected IDs from backend via `queryByIds`
122+
123+
This is automatic — no extra config needed.
124+
125+
---
126+
127+
## Type-Safe Helper: `defineExportConfig<T>()`
128+
129+
For configs defined outside JSX, use the helper for autocomplete:
130+
131+
```tsx
132+
const exportConfig = defineExportConfig<OrdersRow>()({
133+
entityName: 'orders',
134+
removeHeaders: ['deletedAt'], // ← autocomplete works
135+
columnMapping: { email: 'Email' }, // ← autocomplete works
136+
});
137+
```
138+
139+
Inline usage within `<DataTable<OrdersRow>>` is also type-safe — no helper needed.
140+
141+
---
142+
143+
## Full Example
144+
145+
```tsx
146+
import { DataTable, defaultColumnOrder, defineExportConfig } from '@tablecraft/table';
147+
import { createOrdersAdapter, type OrdersRow } from './generated/orders';
148+
import type { OrdersColumn } from './generated/orders';
149+
150+
const exportConfig = defineExportConfig<OrdersRow>()({
151+
entityName: 'orders',
152+
removeHeaders: ['deletedAt', 'tenantId'],
153+
columnMapping: {
154+
createdAt: 'Order Date',
155+
vatAmount: 'VAT Amount',
156+
itemCount: 'Items',
157+
},
158+
transformFunction: (row) => ({
159+
...row,
160+
createdAt: row.createdAt
161+
? new Date(row.createdAt).toLocaleDateString('en-IN')
162+
: '',
163+
total: `₹${Number(row.total).toFixed(2)}`,
164+
}),
165+
});
166+
167+
export function OrdersPage() {
168+
const adapter = createOrdersAdapter({ baseUrl: '/api/engine' });
169+
170+
return (
171+
<DataTable<OrdersRow>
172+
adapter={adapter}
173+
config={{ enableExport: true }}
174+
exportConfig={exportConfig}
175+
defaultColumnOrder={defaultColumnOrder<OrdersColumn>([
176+
'id', 'status', 'email', 'total', 'vatAmount', 'itemCount', 'createdAt',
177+
])}
178+
/>
179+
);
180+
}
181+
```

packages/client/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
"README.md"
1818
],
1919
"scripts": {
20-
"build": "bun run --bun tsc",
21-
"dev": "bun run --bun tsc --watch",
20+
"build": "tsc",
21+
"dev": "tsc --watch",
2222
"test": "vitest",
23-
"typecheck": "bun run --bun tsc --noEmit"
23+
"typecheck": "tsc --noEmit"
2424
},
2525
"peerDependencies": {
2626
"axios": "^1.13.0"
@@ -53,4 +53,4 @@
5353
"url": "https://github.com/jacksonkasi1/TableCraft/issues"
5454
},
5555
"license": "MIT"
56-
}
56+
}

packages/table/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tablecraft/table",
3-
"version": "0.2.10",
3+
"version": "0.2.16",
44
"description": "Schema-driven data table for React — built on TanStack Table + Shadcn UI with native TableCraft engine support",
55
"type": "module",
66
"main": "dist/index.js",
@@ -18,10 +18,10 @@
1818
"README.md"
1919
],
2020
"scripts": {
21-
"build": "bun run --bun tsc",
22-
"dev": "bun run --bun tsc --watch",
21+
"build": "tsc",
22+
"dev": "tsc --watch",
2323
"test": "vitest",
24-
"typecheck": "bun run --bun tsc --noEmit"
24+
"typecheck": "tsc --noEmit"
2525
},
2626
"peerDependencies": {
2727
"@tanstack/react-table": ">=8.0.0",

packages/table/src/auto/rest-adapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export interface RestAdapterOptions<T> {
44
/** Function that fetches data given table params */
55
queryFn: (params: QueryParams) => Promise<QueryResult<T>>;
66
/** Function to fetch specific items by IDs (for cross-page export) */
7-
queryByIdsFn?: (ids: (string | number)[]) => Promise<T[]>;
7+
queryByIdsFn?: (ids: (string | number)[], options?: { sortBy?: string; sortOrder?: "asc" | "desc" }) => Promise<T[]>;
88
/** Function to fetch table metadata (enables auto-columns) */
99
metaFn?: () => Promise<TableMetadata>;
1010
}

0 commit comments

Comments
 (0)