Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Thumbs.db
# Temporary files
*.tmp
*.temp
request.json

.explore
apps-explore/tnks-data-table
Expand Down
2 changes: 1 addition & 1 deletion apps/vite-web-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"@base-ui/react": "^1.1.0",
"@fontsource-variable/inter": "^5.2.8",
"@tablecraft/client": "^0.1.8",
"@tablecraft/table": "^0.2.10",
"@tablecraft/table": "workspace:*",
"@tailwindcss/vite": "^4.1.17",
"@tanstack/react-query": "^5.62.8",
"@tanstack/react-table": "^8.20.0",
Expand Down
21 changes: 20 additions & 1 deletion apps/vite-web-example/src/pages/orders-page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
import { DataTable, defaultColumnOrder } from '@tablecraft/table';
import { DataTable, defaultColumnOrder, defineExportConfig } from '@tablecraft/table';
import { createOrdersAdapter, type OrdersRow } from '../generated';
import type { OrdersColumn } from '../generated';

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

// ** Type-safe export config — only valid column names are accepted
const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
removeHeaders: ['deletedAt', 'tenantId'],
columnMapping: {
createdAt: 'Order Date',
vatAmount: 'VAT Amount',
itemCount: 'Items',
},
transformFunction: (row) => ({
...row,
createdAt: row.createdAt
? new Date(row.createdAt).toLocaleDateString('en-IN')
: '',
total: `₹${row.total.toFixed(2)}`,
}),
});

export function OrdersPage() {
const adapter = createOrdersAdapter({
baseUrl: API_BASE_URL,
Expand All @@ -23,6 +41,7 @@ export function OrdersPage() {
defaultPageSize: 10,
pageSizeOptions: [5, 10, 20, 50],
}}
exportConfig={exportConfig}
defaultColumnOrder={defaultColumnOrder<OrdersColumn>([
'id',
'status',
Expand Down
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* [Features](features.md)
* [Date Filtering](date-filtering.md)
* [Custom Filters](custom-filters.md)
* [Export Configuration](export-config.md)
* [Type Generation](codegen.md)
* [FAQ & Troubleshooting](faq.md)

Expand Down
181 changes: 181 additions & 0 deletions docs/export-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# Export Configuration

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.

## Quick Start

```tsx
import { DataTable, defineExportConfig } from '@tablecraft/table';
import type { OrdersRow } from './generated/orders';

const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
});

<DataTable<OrdersRow>
adapter={adapter}
config={{ enableExport: true }}
exportConfig={exportConfig}
/>
```

All visible columns are exported with their raw values by default.

---

## Excluding Columns (`removeHeaders`)

Use `removeHeaders` to hide specific columns from the export. All other visible columns are included automatically.

```tsx
const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
removeHeaders: ['deletedAt', 'tenantId'],
// everything else is exported
});
```

> **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.

---

## Renaming Columns (`columnMapping`)

Rename column headers in the export without affecting the UI. Works independently from `removeHeaders`.

```tsx
const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
removeHeaders: ['deletedAt'],
columnMapping: {
createdAt: 'Order Date',
vatAmount: 'VAT (₹)',
email: 'Customer Email',
},
});
```

| In UI | In Export File |
|-------|---------------|
| `createdAt` | Order Date |
| `vatAmount` | VAT (₹) |
| `email` | Customer Email |
| `status` | status (unchanged) |

---

## Transforming Values (`transformFunction`)

Format values before they're written to the file:

```tsx
const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
columnMapping: { createdAt: 'Order Date' },
transformFunction: (row) => ({
...row,
createdAt: row.createdAt
? new Date(row.createdAt).toLocaleDateString('en-IN')
: '',
total: `₹${Number(row.total).toFixed(2)}`,
}),
});
```

> Custom column renderers (JSX from `columnOverrides`) are **not** exported. Use `transformFunction` for custom text in the exported file.

---

## CSV / Excel Toggle

```tsx
const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
enableCsv: true, // default: true
enableExcel: false, // disable Excel
});
```

---

## Column Widths (Excel Only)

```tsx
const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
removeHeaders: ['deletedAt'],
columnWidths: [
{ wch: 8 }, // first column — narrow
{ wch: 30 }, // second column — wide
{ wch: 12 }, // third column — medium
],
});
```

---

## Cross-Page Export

When users select rows across multiple pages:
- **Same-page selections** — uses in-memory data (no API call)
- **Cross-page selections** — fetches selected IDs from backend via `queryByIds`

This is automatic — no extra config needed.

---

## Type-Safe Helper: `defineExportConfig<T>()`

For configs defined outside JSX, use the helper for autocomplete:

```tsx
const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
removeHeaders: ['deletedAt'], // ← autocomplete works
columnMapping: { email: 'Email' }, // ← autocomplete works
});
```

Inline usage within `<DataTable<OrdersRow>>` is also type-safe — no helper needed.

---

## Full Example

```tsx
import { DataTable, defaultColumnOrder, defineExportConfig } from '@tablecraft/table';
import { createOrdersAdapter, type OrdersRow } from './generated/orders';
import type { OrdersColumn } from './generated/orders';

const exportConfig = defineExportConfig<OrdersRow>()({
entityName: 'orders',
removeHeaders: ['deletedAt', 'tenantId'],
columnMapping: {
createdAt: 'Order Date',
vatAmount: 'VAT Amount',
itemCount: 'Items',
},
transformFunction: (row) => ({
...row,
createdAt: row.createdAt
? new Date(row.createdAt).toLocaleDateString('en-IN')
: '',
total: `₹${Number(row.total).toFixed(2)}`,
}),
});

export function OrdersPage() {
const adapter = createOrdersAdapter({ baseUrl: '/api/engine' });

return (
<DataTable<OrdersRow>
adapter={adapter}
config={{ enableExport: true }}
exportConfig={exportConfig}
defaultColumnOrder={defaultColumnOrder<OrdersColumn>([
'id', 'status', 'email', 'total', 'vatAmount', 'itemCount', 'createdAt',
])}
/>
);
}
```
8 changes: 4 additions & 4 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
"README.md"
],
"scripts": {
"build": "bun run --bun tsc",
"dev": "bun run --bun tsc --watch",
"build": "tsc",
"dev": "tsc --watch",
"test": "vitest",
"typecheck": "bun run --bun tsc --noEmit"
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"axios": "^1.13.0"
Expand Down Expand Up @@ -53,4 +53,4 @@
"url": "https://github.com/jacksonkasi1/TableCraft/issues"
},
"license": "MIT"
}
}
8 changes: 4 additions & 4 deletions packages/table/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tablecraft/table",
"version": "0.2.10",
"version": "0.2.16",
"description": "Schema-driven data table for React — built on TanStack Table + Shadcn UI with native TableCraft engine support",
"type": "module",
"main": "dist/index.js",
Expand All @@ -18,10 +18,10 @@
"README.md"
],
"scripts": {
"build": "bun run --bun tsc",
"dev": "bun run --bun tsc --watch",
"build": "tsc",
"dev": "tsc --watch",
"test": "vitest",
"typecheck": "bun run --bun tsc --noEmit"
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"@tanstack/react-table": ">=8.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/table/src/auto/rest-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface RestAdapterOptions<T> {
/** Function that fetches data given table params */
queryFn: (params: QueryParams) => Promise<QueryResult<T>>;
/** Function to fetch specific items by IDs (for cross-page export) */
queryByIdsFn?: (ids: (string | number)[]) => Promise<T[]>;
queryByIdsFn?: (ids: (string | number)[], options?: { sortBy?: string; sortOrder?: "asc" | "desc" }) => Promise<T[]>;
/** Function to fetch table metadata (enables auto-columns) */
metaFn?: () => Promise<TableMetadata>;
}
Expand Down
Loading