react-csv-autopilot 0.0.1-beta.2
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/README.md +275 -0
- package/dist/index.cjs +294 -0
- package/dist/index.d.cts +52 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +271 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# react-csv-autopilot
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/react-csv-autopilot)
|
|
4
|
+
[](https://www.npmjs.com/package/react-csv-autopilot)
|
|
5
|
+
[](https://bundlephobia.com/package/react-csv-autopilot)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
**Drop the function, we handle the rest.**
|
|
10
|
+
|
|
11
|
+
A React library for exporting large datasets to CSV with automatic pagination, streaming, and progress tracking. Built with Web Workers for non-blocking performance and File System Access API for efficient file writing.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **Automatic Pagination** - Just provide a `getNextPage` function, we handle the rest
|
|
18
|
+
- **Streaming Export** - Uses ReadableStream for memory-efficient large file exports
|
|
19
|
+
- **Non-blocking** - Web Workers for CSV conversion without freezing the UI
|
|
20
|
+
- **Progress Tracking** - Real-time progress updates via BroadcastChannel
|
|
21
|
+
- **TypeScript** - Full type safety with TypeScript definitions
|
|
22
|
+
- **Zero Dependencies** - Only React as peer dependency
|
|
23
|
+
- **Framework Agnostic Core** - Use the core logic in any JavaScript environment
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install react-csv-autopilot
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
yarn add react-csv-autopilot
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pnpm add react-csv-autopilot
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { useExportCSV, useMessageExportCSV } from 'react-csv-autopilot';
|
|
47
|
+
|
|
48
|
+
function ExportButton() {
|
|
49
|
+
const { handler } = useExportCSV();
|
|
50
|
+
|
|
51
|
+
// Track export progress
|
|
52
|
+
useMessageExportCSV((progress) => {
|
|
53
|
+
if (progress.type === 'progress') {
|
|
54
|
+
console.log(`${progress.loadedItemsCount}/${progress.total}`);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const handleExport = async () => {
|
|
59
|
+
await handler.execute({
|
|
60
|
+
fileName: 'users-export',
|
|
61
|
+
columns: [
|
|
62
|
+
{ key: 'id', label: 'ID' },
|
|
63
|
+
{ key: 'name', label: 'Full Name' },
|
|
64
|
+
{ key: 'email', label: 'Email Address' },
|
|
65
|
+
],
|
|
66
|
+
getNextPage: async (offset) => {
|
|
67
|
+
// Fetch your data - this will be called automatically
|
|
68
|
+
const response = await fetch(`/api/users?page=${offset}&limit=100`);
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
rows: data.users,
|
|
73
|
+
total: data.totalCount,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return <button onClick={handleExport}>Export to CSV</button>;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 📖 API Reference
|
|
86
|
+
|
|
87
|
+
### `useExportCSV()`
|
|
88
|
+
|
|
89
|
+
Hook that provides access to the CSV export controller.
|
|
90
|
+
|
|
91
|
+
**Returns:**
|
|
92
|
+
```typescript
|
|
93
|
+
{
|
|
94
|
+
handler: ExportController // Controller instance for executing exports
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Example:**
|
|
99
|
+
```typescript
|
|
100
|
+
const { handler } = useExportCSV();
|
|
101
|
+
|
|
102
|
+
await handler.execute({
|
|
103
|
+
fileName: 'data-export',
|
|
104
|
+
columns: [...],
|
|
105
|
+
getNextPage: async (offset) => {...}
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### `useMessageExportCSV(callback, channelName?)`
|
|
112
|
+
|
|
113
|
+
Hook for tracking export progress in real-time.
|
|
114
|
+
|
|
115
|
+
**Parameters:**
|
|
116
|
+
- `callback: (payload: ExportProgressPayload) => void` - Called on each progress update
|
|
117
|
+
- `channelName?: string` - Optional custom channel name (default: `'EXPORT_CSV_CHANNEL'`)
|
|
118
|
+
|
|
119
|
+
**Payload Type:**
|
|
120
|
+
```typescript
|
|
121
|
+
type ExportProgressPayload = {
|
|
122
|
+
total: number;
|
|
123
|
+
loadedItemsCount: number;
|
|
124
|
+
type: 'progress' | 'done' | 'failed';
|
|
125
|
+
};
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Example:**
|
|
129
|
+
```typescript
|
|
130
|
+
const [progress, setProgress] = useState({ loaded: 0, total: 0 });
|
|
131
|
+
|
|
132
|
+
useMessageExportCSV((payload) => {
|
|
133
|
+
setProgress({
|
|
134
|
+
loaded: payload.loadedItemsCount,
|
|
135
|
+
total: payload.total,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (payload.type === 'done') {
|
|
139
|
+
console.log('Export completed!');
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
### Types
|
|
147
|
+
|
|
148
|
+
#### `ExportParams`
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
type ExportParams = {
|
|
152
|
+
fileName: string;
|
|
153
|
+
columns: Column[];
|
|
154
|
+
getNextPage: (offset: number) => Promise<{ rows: any[]; total: number }>;
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### `Column`
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
type Column = {
|
|
162
|
+
key: string;
|
|
163
|
+
label: string;
|
|
164
|
+
timezone?: 'UTC' | string;
|
|
165
|
+
formatType?: 'dateFull' | 'dateMediumTime' | 'timeShort' | 'numDecimal' | 'numCompact' | 'numCurrency' | 'numPercent';
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
### Pagination Strategies
|
|
171
|
+
|
|
172
|
+
**API with page numbers:**
|
|
173
|
+
```typescript
|
|
174
|
+
getNextPage: async (offset) => {
|
|
175
|
+
const page = offset + 1; // Convert to 1-based pagination
|
|
176
|
+
const response = await fetch(`/api/data?page=${page}&size=100`);
|
|
177
|
+
return await response.json();
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**API with cursor-based pagination:**
|
|
182
|
+
```typescript
|
|
183
|
+
let nextCursor = null;
|
|
184
|
+
|
|
185
|
+
getNextPage: async (offset) => {
|
|
186
|
+
const url = offset === 0
|
|
187
|
+
? '/api/data?limit=100'
|
|
188
|
+
: `/api/data?cursor=${nextCursor}&limit=100`;
|
|
189
|
+
|
|
190
|
+
const response = await fetch(url);
|
|
191
|
+
const data = await response.json();
|
|
192
|
+
|
|
193
|
+
nextCursor = data.nextCursor;
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
rows: data.items,
|
|
197
|
+
total: data.totalCount,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Architecture
|
|
205
|
+
|
|
206
|
+
### How It Works
|
|
207
|
+
|
|
208
|
+
1. **Stream-based Export** - Uses `ReadableStream` and File System Access API for efficient file writing
|
|
209
|
+
2. **Web Workers** - CSV conversion happens in a separate thread to keep UI responsive
|
|
210
|
+
3. **Automatic Pagination** - Calls your `getNextPage` function repeatedly until all data is fetched
|
|
211
|
+
4. **Progress Tracking** - Uses `BroadcastChannel` to communicate progress across components
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
┌─────────────────┐
|
|
215
|
+
│ React Hook │
|
|
216
|
+
│ useExportCSV() │
|
|
217
|
+
└────────┬────────┘
|
|
218
|
+
│
|
|
219
|
+
▼
|
|
220
|
+
┌─────────────────┐ ┌──────────────┐
|
|
221
|
+
│ Export │─────▶│ Web Worker │
|
|
222
|
+
│ Controller │ │ (CSV Convert)│
|
|
223
|
+
└────────┬────────┘ └──────────────┘
|
|
224
|
+
│
|
|
225
|
+
▼
|
|
226
|
+
┌─────────────────┐ ┌──────────────┐
|
|
227
|
+
│ ReadableStream │─────▶│ File System │
|
|
228
|
+
│ (Pagination) │ │ Access API │
|
|
229
|
+
└─────────────────┘ └──────────────┘
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Browser Compatibility
|
|
235
|
+
|
|
236
|
+
Requires browsers with support for:
|
|
237
|
+
- **File System Access API** (Chrome 86+, Edge 86+)
|
|
238
|
+
- **Web Workers** (All modern browsers)
|
|
239
|
+
- **ReadableStream** (All modern browsers)
|
|
240
|
+
|
|
241
|
+
> **Note:** For browsers without File System Access API, the library will fall back to Blob-based download.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Contributing
|
|
246
|
+
|
|
247
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 📄 License
|
|
252
|
+
|
|
253
|
+
MIT © [Pavlo Kuzina](../../LICENSE)
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 🔗 Links
|
|
258
|
+
|
|
259
|
+
- **npm Package**: [react-csv-autopilot](https://www.npmjs.com/package/react-csv-autopilot)
|
|
260
|
+
- **Repository**: [GitHub - utils-kit](https://github.com/PashaSchool/utils-kit)
|
|
261
|
+
- **Issues**: [GitHub Issues](https://github.com/PashaSchool/utils-kit/issues)
|
|
262
|
+
- **Monorepo**: Part of [utils-kit](../../README.md) collection
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Related Packages
|
|
267
|
+
|
|
268
|
+
- [**react-url-query-params**](../react-url-query-params) - Type-safe URL query parameter management
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
<div align="center">
|
|
273
|
+
<sub>Built with ❤️ by <a href="https://github.com/PashaSchool">Pavlo Kuzina</a></sub>
|
|
274
|
+
</div>
|
|
275
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __typeError = (msg) => {
|
|
7
|
+
throw TypeError(msg);
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
23
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
26
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
27
|
+
|
|
28
|
+
// src/index.ts
|
|
29
|
+
var index_exports = {};
|
|
30
|
+
__export(index_exports, {
|
|
31
|
+
useExportCSV: () => useExportCSV_default,
|
|
32
|
+
useMessageExportCSV: () => useMessageExportCSV_default
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/hooks/useExportCSV.ts
|
|
37
|
+
var import_react = require("react");
|
|
38
|
+
|
|
39
|
+
// src/core/controllers/ExportController.ts
|
|
40
|
+
var _ExportController_instances, canUseFSAccess_fn, resolveStrategy_fn;
|
|
41
|
+
var ExportController = class {
|
|
42
|
+
constructor(deps) {
|
|
43
|
+
this.deps = deps;
|
|
44
|
+
__privateAdd(this, _ExportController_instances);
|
|
45
|
+
}
|
|
46
|
+
async start(params) {
|
|
47
|
+
const strategy = __privateMethod(this, _ExportController_instances, resolveStrategy_fn).call(this);
|
|
48
|
+
return strategy.export(params);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
_ExportController_instances = new WeakSet();
|
|
52
|
+
canUseFSAccess_fn = function() {
|
|
53
|
+
return typeof window.showSaveFilePicker === "function";
|
|
54
|
+
};
|
|
55
|
+
resolveStrategy_fn = function() {
|
|
56
|
+
if (__privateMethod(this, _ExportController_instances, canUseFSAccess_fn).call(this)) {
|
|
57
|
+
return this.deps.fsAccessStrategy;
|
|
58
|
+
}
|
|
59
|
+
return this.deps.blobExportStrategy;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// src/core/strategy/BolbExportStrategy.ts
|
|
63
|
+
var BolbExportStrategy = class {
|
|
64
|
+
export(params) {
|
|
65
|
+
return Promise.resolve({});
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var BolbExportStrategy_default = BolbExportStrategy;
|
|
69
|
+
|
|
70
|
+
// src/core/contants/index.ts
|
|
71
|
+
var WEB_WORKER_NAME = "scv-worker";
|
|
72
|
+
var BROADCAST_CHANNEL_NAME = "react-csv-exporter";
|
|
73
|
+
|
|
74
|
+
// src/core/WorkerManager.ts
|
|
75
|
+
var import_meta = {};
|
|
76
|
+
var pending = /* @__PURE__ */ new Map();
|
|
77
|
+
var _worker, _WorkerManager_instances, listenerRegistry_fn;
|
|
78
|
+
var _WorkerManager = class _WorkerManager {
|
|
79
|
+
constructor() {
|
|
80
|
+
__privateAdd(this, _WorkerManager_instances);
|
|
81
|
+
__privateAdd(this, _worker);
|
|
82
|
+
let workerUrl;
|
|
83
|
+
try {
|
|
84
|
+
workerUrl = new URL("./worker.js", import_meta.url);
|
|
85
|
+
} catch {
|
|
86
|
+
workerUrl = "/worker.js";
|
|
87
|
+
}
|
|
88
|
+
__privateSet(this, _worker, new Worker(workerUrl, {
|
|
89
|
+
type: "module",
|
|
90
|
+
name: WEB_WORKER_NAME
|
|
91
|
+
}));
|
|
92
|
+
__privateMethod(this, _WorkerManager_instances, listenerRegistry_fn).call(this);
|
|
93
|
+
}
|
|
94
|
+
static initialise() {
|
|
95
|
+
return new _WorkerManager();
|
|
96
|
+
}
|
|
97
|
+
async triggerWorker(payload) {
|
|
98
|
+
const id = payload.id ?? Math.random().toString(36).substr(2);
|
|
99
|
+
const p = new Promise((resolve, reject) => {
|
|
100
|
+
pending.set(id, { resolve, reject });
|
|
101
|
+
});
|
|
102
|
+
__privateGet(this, _worker).postMessage(payload);
|
|
103
|
+
return p;
|
|
104
|
+
}
|
|
105
|
+
terminate() {
|
|
106
|
+
if (__privateGet(this, _worker)) {
|
|
107
|
+
__privateGet(this, _worker).terminate();
|
|
108
|
+
__privateSet(this, _worker, null);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
_worker = new WeakMap();
|
|
113
|
+
_WorkerManager_instances = new WeakSet();
|
|
114
|
+
listenerRegistry_fn = function() {
|
|
115
|
+
__privateGet(this, _worker).addEventListener("message", (event) => {
|
|
116
|
+
const { id, result, error } = event.data;
|
|
117
|
+
const entity = pending.get(id);
|
|
118
|
+
if (!entity) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
pending.delete(id);
|
|
122
|
+
if (error) {
|
|
123
|
+
entity.reject(error);
|
|
124
|
+
} else {
|
|
125
|
+
entity.resolve(result);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
__privateGet(this, _worker).addEventListener("error", (event) => {
|
|
129
|
+
for (const [, { reject }] of pending) {
|
|
130
|
+
reject(event);
|
|
131
|
+
}
|
|
132
|
+
pending.clear();
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
var WorkerManager = _WorkerManager;
|
|
136
|
+
var WorkerManager_default = WorkerManager;
|
|
137
|
+
|
|
138
|
+
// src/core/strategy/FsAccessExportStrategy.ts
|
|
139
|
+
var FsAccessExportStrategy = class {
|
|
140
|
+
constructor() {
|
|
141
|
+
this.workerManager = WorkerManager_default.initialise();
|
|
142
|
+
}
|
|
143
|
+
async export(params) {
|
|
144
|
+
const _suggestedName = params?.fileName || "export";
|
|
145
|
+
const fileHandle = await window.showSaveFilePicker({
|
|
146
|
+
suggestedName: _suggestedName,
|
|
147
|
+
types: [{ description: "CSV file", accept: { "text/csv": [".csv"] } }]
|
|
148
|
+
});
|
|
149
|
+
const writableFileStream = await fileHandle.createWritable();
|
|
150
|
+
let iterator = 0;
|
|
151
|
+
let totalRowsLoaded = 0;
|
|
152
|
+
const encoder = new TextEncoder();
|
|
153
|
+
const messaging = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
|
|
154
|
+
const readable = new ReadableStream({
|
|
155
|
+
pull: async (controller) => {
|
|
156
|
+
try {
|
|
157
|
+
const response = await params.getNextPage(iterator++);
|
|
158
|
+
const safeRows = Array.isArray(response.rows) ? response?.rows : [];
|
|
159
|
+
const safeTotal = response.total ?? 0;
|
|
160
|
+
const isRowsEmpty = !safeRows || !safeRows.length;
|
|
161
|
+
totalRowsLoaded = isRowsEmpty ? safeTotal : totalRowsLoaded += safeRows.length;
|
|
162
|
+
const isFinished = totalRowsLoaded >= safeTotal;
|
|
163
|
+
if (isRowsEmpty) {
|
|
164
|
+
messaging.postMessage(
|
|
165
|
+
JSON.stringify({
|
|
166
|
+
type: "done",
|
|
167
|
+
total: safeTotal,
|
|
168
|
+
loadedItemsCount: totalRowsLoaded
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
await this.workerManager.triggerWorker({ id: iterator, type: "completed" });
|
|
172
|
+
messaging.close();
|
|
173
|
+
controller.close();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const csvChunks = await this.workerManager.triggerWorker({
|
|
177
|
+
id: iterator,
|
|
178
|
+
type: "to_csv_chunk",
|
|
179
|
+
data: safeRows,
|
|
180
|
+
columns: params.columns
|
|
181
|
+
});
|
|
182
|
+
messaging.postMessage(
|
|
183
|
+
JSON.stringify({
|
|
184
|
+
type: "progress",
|
|
185
|
+
total: safeTotal,
|
|
186
|
+
loadedItemsCount: totalRowsLoaded
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
controller.enqueue(encoder.encode(csvChunks));
|
|
190
|
+
if (isFinished) {
|
|
191
|
+
messaging.postMessage(
|
|
192
|
+
JSON.stringify({
|
|
193
|
+
type: "done",
|
|
194
|
+
total: safeTotal,
|
|
195
|
+
loadedItemsCount: totalRowsLoaded
|
|
196
|
+
})
|
|
197
|
+
);
|
|
198
|
+
await this.workerManager.triggerWorker({ id: iterator, type: "completed" });
|
|
199
|
+
messaging.close();
|
|
200
|
+
controller.close();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
controller.error(error);
|
|
205
|
+
messaging.postMessage(
|
|
206
|
+
JSON.stringify({
|
|
207
|
+
type: "failed",
|
|
208
|
+
total: 0,
|
|
209
|
+
loadedItemsCount: totalRowsLoaded
|
|
210
|
+
})
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
try {
|
|
216
|
+
await readable.pipeTo(writableFileStream);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
console.error("Export failed:", err);
|
|
219
|
+
throw err;
|
|
220
|
+
} finally {
|
|
221
|
+
this.workerManager.terminate();
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
finished: true,
|
|
225
|
+
totalRowsLoaded,
|
|
226
|
+
logs: {
|
|
227
|
+
warnings: []
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
var FsAccessExportStrategy_default = FsAccessExportStrategy;
|
|
233
|
+
|
|
234
|
+
// src/core/fabric/ExportControlFabric.ts
|
|
235
|
+
var ExportControlFabric = class {
|
|
236
|
+
static create() {
|
|
237
|
+
const controller = new ExportController({
|
|
238
|
+
fsAccessStrategy: new FsAccessExportStrategy_default(),
|
|
239
|
+
blobExportStrategy: new BolbExportStrategy_default()
|
|
240
|
+
});
|
|
241
|
+
return controller;
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// src/core/ExportControllerSingleton.ts
|
|
246
|
+
var _ExportControllerSingleton = class _ExportControllerSingleton {
|
|
247
|
+
static init() {
|
|
248
|
+
if (_ExportControllerSingleton.instance) {
|
|
249
|
+
return _ExportControllerSingleton.instance;
|
|
250
|
+
}
|
|
251
|
+
_ExportControllerSingleton.instance = ExportControlFabric.create();
|
|
252
|
+
_ExportControllerSingleton.initialized = true;
|
|
253
|
+
return _ExportControllerSingleton.instance;
|
|
254
|
+
}
|
|
255
|
+
static getInstance() {
|
|
256
|
+
if (!_ExportControllerSingleton.instance) {
|
|
257
|
+
return _ExportControllerSingleton.init();
|
|
258
|
+
}
|
|
259
|
+
return _ExportControllerSingleton.instance;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
_ExportControllerSingleton.instance = null;
|
|
263
|
+
_ExportControllerSingleton.initialized = false;
|
|
264
|
+
var ExportControllerSingleton = _ExportControllerSingleton;
|
|
265
|
+
var ExportControllerSingleton_default = ExportControllerSingleton;
|
|
266
|
+
|
|
267
|
+
// src/hooks/useExportCSV.ts
|
|
268
|
+
function useExportCSV() {
|
|
269
|
+
const exportCallbackRef = (0, import_react.useRef)(ExportControllerSingleton_default.init());
|
|
270
|
+
return {
|
|
271
|
+
handler: exportCallbackRef.current
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
var useExportCSV_default = useExportCSV;
|
|
275
|
+
|
|
276
|
+
// src/hooks/useMessageExportCSV.ts
|
|
277
|
+
var import_react2 = require("react");
|
|
278
|
+
function useMessageExportCSV(cb) {
|
|
279
|
+
(0, import_react2.useEffect)(() => {
|
|
280
|
+
const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
|
|
281
|
+
channel.addEventListener("message", (params) => {
|
|
282
|
+
try {
|
|
283
|
+
const json = JSON.parse(params.data);
|
|
284
|
+
cb(json);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
console.error({ error });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
return () => {
|
|
290
|
+
channel.close();
|
|
291
|
+
};
|
|
292
|
+
}, []);
|
|
293
|
+
}
|
|
294
|
+
var useMessageExportCSV_default = useMessageExportCSV;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
type formatterTypes = "dateFull" | "dateMediumTime" | "timeShort" | "numDecimal" | "numCompact" | "numCurrency" | "numPercent";
|
|
2
|
+
type Column = {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
timezone?: "UTC" | string;
|
|
6
|
+
formatType?: formatterTypes;
|
|
7
|
+
};
|
|
8
|
+
type ExportParams = {
|
|
9
|
+
fileName: string;
|
|
10
|
+
columns: Column[];
|
|
11
|
+
getNextPage: (offset: number) => Promise<{
|
|
12
|
+
rows: any[];
|
|
13
|
+
total: number;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
interface ExportStrategy {
|
|
17
|
+
export(params: ExportParams): Promise<any>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
declare class BolbExportStrategy implements ExportStrategy {
|
|
21
|
+
export(params: ExportParams): Promise<any>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare class FsAccessExportStrategy implements ExportStrategy {
|
|
25
|
+
private workerManager;
|
|
26
|
+
constructor();
|
|
27
|
+
export(params: ExportParams): Promise<any>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ExportControllerDeps = {
|
|
31
|
+
fsAccessStrategy: FsAccessExportStrategy;
|
|
32
|
+
blobExportStrategy: BolbExportStrategy;
|
|
33
|
+
};
|
|
34
|
+
declare class ExportController {
|
|
35
|
+
#private;
|
|
36
|
+
private readonly deps;
|
|
37
|
+
constructor(deps: ExportControllerDeps);
|
|
38
|
+
start(params: ExportParams): Promise<any>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare function useExportCSV(): {
|
|
42
|
+
handler: ExportController;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type Payload = {
|
|
46
|
+
total: number;
|
|
47
|
+
loadedItemsCount: number;
|
|
48
|
+
state: "progress" | "failed" | "done";
|
|
49
|
+
};
|
|
50
|
+
declare function useMessageExportCSV(cb: (payload: Payload) => void): void;
|
|
51
|
+
|
|
52
|
+
export { type Column, ExportController, type ExportParams, useExportCSV, useMessageExportCSV };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
type formatterTypes = "dateFull" | "dateMediumTime" | "timeShort" | "numDecimal" | "numCompact" | "numCurrency" | "numPercent";
|
|
2
|
+
type Column = {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
timezone?: "UTC" | string;
|
|
6
|
+
formatType?: formatterTypes;
|
|
7
|
+
};
|
|
8
|
+
type ExportParams = {
|
|
9
|
+
fileName: string;
|
|
10
|
+
columns: Column[];
|
|
11
|
+
getNextPage: (offset: number) => Promise<{
|
|
12
|
+
rows: any[];
|
|
13
|
+
total: number;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
interface ExportStrategy {
|
|
17
|
+
export(params: ExportParams): Promise<any>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
declare class BolbExportStrategy implements ExportStrategy {
|
|
21
|
+
export(params: ExportParams): Promise<any>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare class FsAccessExportStrategy implements ExportStrategy {
|
|
25
|
+
private workerManager;
|
|
26
|
+
constructor();
|
|
27
|
+
export(params: ExportParams): Promise<any>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ExportControllerDeps = {
|
|
31
|
+
fsAccessStrategy: FsAccessExportStrategy;
|
|
32
|
+
blobExportStrategy: BolbExportStrategy;
|
|
33
|
+
};
|
|
34
|
+
declare class ExportController {
|
|
35
|
+
#private;
|
|
36
|
+
private readonly deps;
|
|
37
|
+
constructor(deps: ExportControllerDeps);
|
|
38
|
+
start(params: ExportParams): Promise<any>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare function useExportCSV(): {
|
|
42
|
+
handler: ExportController;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type Payload = {
|
|
46
|
+
total: number;
|
|
47
|
+
loadedItemsCount: number;
|
|
48
|
+
state: "progress" | "failed" | "done";
|
|
49
|
+
};
|
|
50
|
+
declare function useMessageExportCSV(cb: (payload: Payload) => void): void;
|
|
51
|
+
|
|
52
|
+
export { type Column, ExportController, type ExportParams, useExportCSV, useMessageExportCSV };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
5
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
6
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
7
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
8
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
9
|
+
|
|
10
|
+
// src/hooks/useExportCSV.ts
|
|
11
|
+
import { useRef } from "react";
|
|
12
|
+
|
|
13
|
+
// src/core/controllers/ExportController.ts
|
|
14
|
+
var _ExportController_instances, canUseFSAccess_fn, resolveStrategy_fn;
|
|
15
|
+
var ExportController = class {
|
|
16
|
+
constructor(deps) {
|
|
17
|
+
this.deps = deps;
|
|
18
|
+
__privateAdd(this, _ExportController_instances);
|
|
19
|
+
}
|
|
20
|
+
async start(params) {
|
|
21
|
+
const strategy = __privateMethod(this, _ExportController_instances, resolveStrategy_fn).call(this);
|
|
22
|
+
return strategy.export(params);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
_ExportController_instances = new WeakSet();
|
|
26
|
+
canUseFSAccess_fn = function() {
|
|
27
|
+
return typeof window.showSaveFilePicker === "function";
|
|
28
|
+
};
|
|
29
|
+
resolveStrategy_fn = function() {
|
|
30
|
+
if (__privateMethod(this, _ExportController_instances, canUseFSAccess_fn).call(this)) {
|
|
31
|
+
return this.deps.fsAccessStrategy;
|
|
32
|
+
}
|
|
33
|
+
return this.deps.blobExportStrategy;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/core/strategy/BolbExportStrategy.ts
|
|
37
|
+
var BolbExportStrategy = class {
|
|
38
|
+
export(params) {
|
|
39
|
+
return Promise.resolve({});
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var BolbExportStrategy_default = BolbExportStrategy;
|
|
43
|
+
|
|
44
|
+
// src/core/contants/index.ts
|
|
45
|
+
var WEB_WORKER_NAME = "scv-worker";
|
|
46
|
+
var BROADCAST_CHANNEL_NAME = "react-csv-exporter";
|
|
47
|
+
|
|
48
|
+
// src/core/WorkerManager.ts
|
|
49
|
+
var pending = /* @__PURE__ */ new Map();
|
|
50
|
+
var _worker, _WorkerManager_instances, listenerRegistry_fn;
|
|
51
|
+
var _WorkerManager = class _WorkerManager {
|
|
52
|
+
constructor() {
|
|
53
|
+
__privateAdd(this, _WorkerManager_instances);
|
|
54
|
+
__privateAdd(this, _worker);
|
|
55
|
+
let workerUrl;
|
|
56
|
+
try {
|
|
57
|
+
workerUrl = new URL("./worker.js", import.meta.url);
|
|
58
|
+
} catch {
|
|
59
|
+
workerUrl = "/worker.js";
|
|
60
|
+
}
|
|
61
|
+
__privateSet(this, _worker, new Worker(workerUrl, {
|
|
62
|
+
type: "module",
|
|
63
|
+
name: WEB_WORKER_NAME
|
|
64
|
+
}));
|
|
65
|
+
__privateMethod(this, _WorkerManager_instances, listenerRegistry_fn).call(this);
|
|
66
|
+
}
|
|
67
|
+
static initialise() {
|
|
68
|
+
return new _WorkerManager();
|
|
69
|
+
}
|
|
70
|
+
async triggerWorker(payload) {
|
|
71
|
+
const id = payload.id ?? Math.random().toString(36).substr(2);
|
|
72
|
+
const p = new Promise((resolve, reject) => {
|
|
73
|
+
pending.set(id, { resolve, reject });
|
|
74
|
+
});
|
|
75
|
+
__privateGet(this, _worker).postMessage(payload);
|
|
76
|
+
return p;
|
|
77
|
+
}
|
|
78
|
+
terminate() {
|
|
79
|
+
if (__privateGet(this, _worker)) {
|
|
80
|
+
__privateGet(this, _worker).terminate();
|
|
81
|
+
__privateSet(this, _worker, null);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
_worker = new WeakMap();
|
|
86
|
+
_WorkerManager_instances = new WeakSet();
|
|
87
|
+
listenerRegistry_fn = function() {
|
|
88
|
+
__privateGet(this, _worker).addEventListener("message", (event) => {
|
|
89
|
+
const { id, result, error } = event.data;
|
|
90
|
+
const entity = pending.get(id);
|
|
91
|
+
if (!entity) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
pending.delete(id);
|
|
95
|
+
if (error) {
|
|
96
|
+
entity.reject(error);
|
|
97
|
+
} else {
|
|
98
|
+
entity.resolve(result);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
__privateGet(this, _worker).addEventListener("error", (event) => {
|
|
102
|
+
for (const [, { reject }] of pending) {
|
|
103
|
+
reject(event);
|
|
104
|
+
}
|
|
105
|
+
pending.clear();
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
var WorkerManager = _WorkerManager;
|
|
109
|
+
var WorkerManager_default = WorkerManager;
|
|
110
|
+
|
|
111
|
+
// src/core/strategy/FsAccessExportStrategy.ts
|
|
112
|
+
var FsAccessExportStrategy = class {
|
|
113
|
+
constructor() {
|
|
114
|
+
this.workerManager = WorkerManager_default.initialise();
|
|
115
|
+
}
|
|
116
|
+
async export(params) {
|
|
117
|
+
const _suggestedName = params?.fileName || "export";
|
|
118
|
+
const fileHandle = await window.showSaveFilePicker({
|
|
119
|
+
suggestedName: _suggestedName,
|
|
120
|
+
types: [{ description: "CSV file", accept: { "text/csv": [".csv"] } }]
|
|
121
|
+
});
|
|
122
|
+
const writableFileStream = await fileHandle.createWritable();
|
|
123
|
+
let iterator = 0;
|
|
124
|
+
let totalRowsLoaded = 0;
|
|
125
|
+
const encoder = new TextEncoder();
|
|
126
|
+
const messaging = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
|
|
127
|
+
const readable = new ReadableStream({
|
|
128
|
+
pull: async (controller) => {
|
|
129
|
+
try {
|
|
130
|
+
const response = await params.getNextPage(iterator++);
|
|
131
|
+
const safeRows = Array.isArray(response.rows) ? response?.rows : [];
|
|
132
|
+
const safeTotal = response.total ?? 0;
|
|
133
|
+
const isRowsEmpty = !safeRows || !safeRows.length;
|
|
134
|
+
totalRowsLoaded = isRowsEmpty ? safeTotal : totalRowsLoaded += safeRows.length;
|
|
135
|
+
const isFinished = totalRowsLoaded >= safeTotal;
|
|
136
|
+
if (isRowsEmpty) {
|
|
137
|
+
messaging.postMessage(
|
|
138
|
+
JSON.stringify({
|
|
139
|
+
type: "done",
|
|
140
|
+
total: safeTotal,
|
|
141
|
+
loadedItemsCount: totalRowsLoaded
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
await this.workerManager.triggerWorker({ id: iterator, type: "completed" });
|
|
145
|
+
messaging.close();
|
|
146
|
+
controller.close();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const csvChunks = await this.workerManager.triggerWorker({
|
|
150
|
+
id: iterator,
|
|
151
|
+
type: "to_csv_chunk",
|
|
152
|
+
data: safeRows,
|
|
153
|
+
columns: params.columns
|
|
154
|
+
});
|
|
155
|
+
messaging.postMessage(
|
|
156
|
+
JSON.stringify({
|
|
157
|
+
type: "progress",
|
|
158
|
+
total: safeTotal,
|
|
159
|
+
loadedItemsCount: totalRowsLoaded
|
|
160
|
+
})
|
|
161
|
+
);
|
|
162
|
+
controller.enqueue(encoder.encode(csvChunks));
|
|
163
|
+
if (isFinished) {
|
|
164
|
+
messaging.postMessage(
|
|
165
|
+
JSON.stringify({
|
|
166
|
+
type: "done",
|
|
167
|
+
total: safeTotal,
|
|
168
|
+
loadedItemsCount: totalRowsLoaded
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
await this.workerManager.triggerWorker({ id: iterator, type: "completed" });
|
|
172
|
+
messaging.close();
|
|
173
|
+
controller.close();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
controller.error(error);
|
|
178
|
+
messaging.postMessage(
|
|
179
|
+
JSON.stringify({
|
|
180
|
+
type: "failed",
|
|
181
|
+
total: 0,
|
|
182
|
+
loadedItemsCount: totalRowsLoaded
|
|
183
|
+
})
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
try {
|
|
189
|
+
await readable.pipeTo(writableFileStream);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
console.error("Export failed:", err);
|
|
192
|
+
throw err;
|
|
193
|
+
} finally {
|
|
194
|
+
this.workerManager.terminate();
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
finished: true,
|
|
198
|
+
totalRowsLoaded,
|
|
199
|
+
logs: {
|
|
200
|
+
warnings: []
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var FsAccessExportStrategy_default = FsAccessExportStrategy;
|
|
206
|
+
|
|
207
|
+
// src/core/fabric/ExportControlFabric.ts
|
|
208
|
+
var ExportControlFabric = class {
|
|
209
|
+
static create() {
|
|
210
|
+
const controller = new ExportController({
|
|
211
|
+
fsAccessStrategy: new FsAccessExportStrategy_default(),
|
|
212
|
+
blobExportStrategy: new BolbExportStrategy_default()
|
|
213
|
+
});
|
|
214
|
+
return controller;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
// src/core/ExportControllerSingleton.ts
|
|
219
|
+
var _ExportControllerSingleton = class _ExportControllerSingleton {
|
|
220
|
+
static init() {
|
|
221
|
+
if (_ExportControllerSingleton.instance) {
|
|
222
|
+
return _ExportControllerSingleton.instance;
|
|
223
|
+
}
|
|
224
|
+
_ExportControllerSingleton.instance = ExportControlFabric.create();
|
|
225
|
+
_ExportControllerSingleton.initialized = true;
|
|
226
|
+
return _ExportControllerSingleton.instance;
|
|
227
|
+
}
|
|
228
|
+
static getInstance() {
|
|
229
|
+
if (!_ExportControllerSingleton.instance) {
|
|
230
|
+
return _ExportControllerSingleton.init();
|
|
231
|
+
}
|
|
232
|
+
return _ExportControllerSingleton.instance;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
_ExportControllerSingleton.instance = null;
|
|
236
|
+
_ExportControllerSingleton.initialized = false;
|
|
237
|
+
var ExportControllerSingleton = _ExportControllerSingleton;
|
|
238
|
+
var ExportControllerSingleton_default = ExportControllerSingleton;
|
|
239
|
+
|
|
240
|
+
// src/hooks/useExportCSV.ts
|
|
241
|
+
function useExportCSV() {
|
|
242
|
+
const exportCallbackRef = useRef(ExportControllerSingleton_default.init());
|
|
243
|
+
return {
|
|
244
|
+
handler: exportCallbackRef.current
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
var useExportCSV_default = useExportCSV;
|
|
248
|
+
|
|
249
|
+
// src/hooks/useMessageExportCSV.ts
|
|
250
|
+
import { useEffect } from "react";
|
|
251
|
+
function useMessageExportCSV(cb) {
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
|
|
254
|
+
channel.addEventListener("message", (params) => {
|
|
255
|
+
try {
|
|
256
|
+
const json = JSON.parse(params.data);
|
|
257
|
+
cb(json);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error({ error });
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
return () => {
|
|
263
|
+
channel.close();
|
|
264
|
+
};
|
|
265
|
+
}, []);
|
|
266
|
+
}
|
|
267
|
+
var useMessageExportCSV_default = useMessageExportCSV;
|
|
268
|
+
export {
|
|
269
|
+
useExportCSV_default as useExportCSV,
|
|
270
|
+
useMessageExportCSV_default as useMessageExportCSV
|
|
271
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-csv-autopilot",
|
|
3
|
+
"version": "0.0.1-beta.2",
|
|
4
|
+
"description": "React hooks for CSV export with automatic pagination - drop the function, we handle the rest",
|
|
5
|
+
"author": "Pavlo Kuzina",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "dist/index.cjs",
|
|
10
|
+
"module": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": "^18 || ^19"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/react": "^18",
|
|
33
|
+
"@types/wicg-file-system-access": "^2023.10.7",
|
|
34
|
+
"react": "^19.2.3",
|
|
35
|
+
"tsup": "^8.0.0",
|
|
36
|
+
"typescript": "^5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/PashaSchool/utils-kit.git",
|
|
41
|
+
"directory": "packages/react-csv-autopilot"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/PashaSchool/utils-kit/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/PashaSchool/utils-kit/blob/main/packages/react-csv-autopilot/README.md",
|
|
47
|
+
"keywords": [
|
|
48
|
+
"react",
|
|
49
|
+
"csv",
|
|
50
|
+
"export",
|
|
51
|
+
"hooks",
|
|
52
|
+
"autopilot",
|
|
53
|
+
"pagination",
|
|
54
|
+
"streaming",
|
|
55
|
+
"typescript",
|
|
56
|
+
"download",
|
|
57
|
+
"file-export",
|
|
58
|
+
"web-workers",
|
|
59
|
+
"progress-tracking",
|
|
60
|
+
"file-system-access",
|
|
61
|
+
"large-datasets",
|
|
62
|
+
"data-export"
|
|
63
|
+
]
|
|
64
|
+
}
|