@expresscsv/react 0.1.3 → 0.1.5
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 +195 -246
- package/dist/index.d.mts +3415 -22
- package/package.json +1 -1
- package/dist/.dts/CSVImporter.d.ts +0 -27
- package/dist/.dts/index.d.ts +0 -4
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @expresscsv/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@expresscsv/react)
|
|
4
|
+
[](https://github.com/nicholasgriffintn/expresscsv/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
React hook for embedding the [ExpressCSV](https://expresscsv.com) CSV import widget. Wraps [`@expresscsv/sdk`](https://www.npmjs.com/package/@expresscsv/sdk) with automatic lifecycle management and reactive state.
|
|
4
7
|
|
|
5
8
|
## Installation
|
|
6
9
|
|
|
@@ -15,47 +18,45 @@ npm install @expresscsv/react
|
|
|
15
18
|
yarn add @expresscsv/react
|
|
16
19
|
```
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
The React SDK provides a `useExpressCSV` hook for easy integration into your React components.
|
|
21
|
-
|
|
22
|
-
### Basic Usage
|
|
21
|
+
> **Peer dependency:** React 18+
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
## Quick Start
|
|
25
24
|
|
|
26
25
|
```tsx
|
|
27
|
-
import { useExpressCSV, x
|
|
26
|
+
import { useExpressCSV, x } from "@expresscsv/react";
|
|
28
27
|
|
|
29
28
|
const schema = x.row({
|
|
30
|
-
name: x.string().label(
|
|
31
|
-
email: x.string().email().label(
|
|
32
|
-
age: x.number().label(
|
|
29
|
+
name: x.string().label("Full Name"),
|
|
30
|
+
email: x.string().email().label("Email Address"),
|
|
31
|
+
age: x.number().label("Age").min(18),
|
|
33
32
|
});
|
|
34
33
|
|
|
35
34
|
function App() {
|
|
36
|
-
const { open, isOpen
|
|
35
|
+
const { open, isOpen } = useExpressCSV({
|
|
37
36
|
schema,
|
|
38
|
-
|
|
37
|
+
publishableKey: "your-publishable-key",
|
|
39
38
|
importIdentifier: "user-import",
|
|
39
|
+
title: "Import Users",
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
const handleImport = () => {
|
|
43
43
|
open({
|
|
44
|
-
chunkSize:
|
|
44
|
+
chunkSize: 500,
|
|
45
45
|
onData: async (chunk, next) => {
|
|
46
|
-
console.log(
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
console.log(
|
|
47
|
+
`Chunk ${chunk.currentChunkIndex + 1}/${chunk.totalChunks}`
|
|
48
|
+
);
|
|
49
|
+
console.log("Records:", chunk.records);
|
|
49
50
|
next();
|
|
50
51
|
},
|
|
51
52
|
onComplete: () => {
|
|
52
|
-
console.log(
|
|
53
|
+
console.log("All chunks processed");
|
|
53
54
|
},
|
|
54
55
|
onCancel: () => {
|
|
55
|
-
console.log(
|
|
56
|
+
console.log("User cancelled");
|
|
56
57
|
},
|
|
57
58
|
onError: (error) => {
|
|
58
|
-
console.error(
|
|
59
|
+
console.error("Import error:", error);
|
|
59
60
|
},
|
|
60
61
|
});
|
|
61
62
|
};
|
|
@@ -68,226 +69,166 @@ function App() {
|
|
|
68
69
|
}
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
Your `publishableKey` is available from the [ExpressCSV dashboard](https://expresscsv.com). Two key types are available: **production** keys for live usage, and **dev/testing** keys that provide unlimited test imports.
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
## Preloading
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
import { useExpressCSV, x } from '@expresscsv/react';
|
|
76
|
+
By default the widget preloads in a hidden iframe so it appears instantly when `open()` is called:
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
```tsx
|
|
79
|
+
const { open } = useExpressCSV({
|
|
80
|
+
schema,
|
|
81
|
+
publishableKey: "your-publishable-key",
|
|
82
|
+
importIdentifier: "user-import",
|
|
81
83
|
});
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
// Widget displays instantly
|
|
86
|
+
const handleImport = () => {
|
|
87
|
+
open({
|
|
88
|
+
onData: (chunk, next) => {
|
|
89
|
+
console.log("Records:", chunk.records);
|
|
90
|
+
next();
|
|
91
|
+
},
|
|
89
92
|
});
|
|
90
|
-
|
|
91
|
-
const handleImport = () => {
|
|
92
|
-
open({
|
|
93
|
-
onData: async (chunk, next) => {
|
|
94
|
-
console.log('CSV data:', chunk.records);
|
|
95
|
-
next();
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<button onClick={handleImport}>
|
|
102
|
-
Import CSV
|
|
103
|
-
</button>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
93
|
+
};
|
|
106
94
|
```
|
|
107
95
|
|
|
108
|
-
To disable
|
|
96
|
+
To disable preloading:
|
|
109
97
|
|
|
110
98
|
```tsx
|
|
111
99
|
const { open } = useExpressCSV({
|
|
112
100
|
schema,
|
|
113
|
-
|
|
101
|
+
publishableKey: "your-publishable-key",
|
|
114
102
|
importIdentifier: "user-import",
|
|
115
|
-
preload: false,
|
|
103
|
+
preload: false,
|
|
116
104
|
});
|
|
117
105
|
```
|
|
118
106
|
|
|
119
|
-
|
|
107
|
+
## Webhook Delivery
|
|
120
108
|
|
|
121
|
-
|
|
109
|
+
Deliver data to a server endpoint instead of (or in addition to) processing locally:
|
|
122
110
|
|
|
123
111
|
```tsx
|
|
124
|
-
import { useExpressCSV, x
|
|
112
|
+
import { useExpressCSV, x } from "@expresscsv/react";
|
|
125
113
|
|
|
126
114
|
const schema = x.row({
|
|
127
|
-
name: x.string().label(
|
|
128
|
-
email: x.string().email().label(
|
|
115
|
+
name: x.string().label("Full Name"),
|
|
116
|
+
email: x.string().email().label("Email Address"),
|
|
129
117
|
});
|
|
130
118
|
|
|
131
119
|
function App() {
|
|
132
120
|
const { open } = useExpressCSV({
|
|
133
121
|
schema,
|
|
134
|
-
|
|
122
|
+
publishableKey: "your-publishable-key",
|
|
135
123
|
importIdentifier: "user-import",
|
|
124
|
+
title: "Import Users",
|
|
136
125
|
});
|
|
137
126
|
|
|
138
127
|
const handleImport = () => {
|
|
139
128
|
open({
|
|
140
|
-
chunkSize: 500, // Optional: chunk size for webhook delivery (default: 1000)
|
|
141
129
|
webhook: {
|
|
142
|
-
url:
|
|
143
|
-
method:
|
|
130
|
+
url: "https://api.example.com/webhooks/csv-import",
|
|
131
|
+
method: "POST",
|
|
144
132
|
headers: {
|
|
145
|
-
|
|
146
|
-
'X-Custom-Header': 'custom-value',
|
|
133
|
+
Authorization: "Bearer your-api-token",
|
|
147
134
|
},
|
|
148
135
|
metadata: {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
userId: 'user-123',
|
|
136
|
+
source: "react-app",
|
|
137
|
+
userId: "user-123",
|
|
152
138
|
},
|
|
153
139
|
},
|
|
154
140
|
onComplete: () => {
|
|
155
|
-
console.log(
|
|
141
|
+
console.log("Webhook delivery initiated");
|
|
156
142
|
},
|
|
157
143
|
onError: (error) => {
|
|
158
|
-
console.error(
|
|
144
|
+
console.error("Delivery error:", error);
|
|
159
145
|
},
|
|
160
146
|
});
|
|
161
147
|
};
|
|
162
148
|
|
|
163
|
-
return
|
|
164
|
-
<button onClick={handleImport}>
|
|
165
|
-
Import CSV via Webhook
|
|
166
|
-
</button>
|
|
167
|
-
);
|
|
149
|
+
return <button onClick={handleImport}>Import CSV via Webhook</button>;
|
|
168
150
|
}
|
|
169
151
|
```
|
|
170
152
|
|
|
171
153
|
### Combined Local Callback and Webhook
|
|
172
154
|
|
|
173
|
-
You can use both `onData` callback and webhook delivery simultaneously:
|
|
174
|
-
|
|
175
155
|
```tsx
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
156
|
+
open({
|
|
157
|
+
chunkSize: 500,
|
|
158
|
+
onData: async (chunk, next) => {
|
|
159
|
+
await saveToLocalDatabase(chunk.records);
|
|
160
|
+
next();
|
|
161
|
+
},
|
|
162
|
+
webhook: {
|
|
163
|
+
url: "https://api.example.com/webhooks/csv-import",
|
|
164
|
+
headers: { Authorization: "Bearer your-api-token" },
|
|
165
|
+
},
|
|
166
|
+
onComplete: () => {
|
|
167
|
+
console.log("Local processing and webhook delivery complete");
|
|
168
|
+
},
|
|
181
169
|
});
|
|
182
|
-
|
|
183
|
-
function App() {
|
|
184
|
-
const { open } = useExpressCSV({
|
|
185
|
-
schema,
|
|
186
|
-
title: "Import Users",
|
|
187
|
-
importIdentifier: "user-import",
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const handleImport = () => {
|
|
191
|
-
open({
|
|
192
|
-
chunkSize: 100,
|
|
193
|
-
// Process locally
|
|
194
|
-
onData: async (chunk, next) => {
|
|
195
|
-
console.log(`Processing chunk ${chunk.currentChunkIndex + 1}/${chunk.totalChunks}`);
|
|
196
|
-
// Your local processing logic
|
|
197
|
-
await processChunkLocally(chunk.records);
|
|
198
|
-
next();
|
|
199
|
-
},
|
|
200
|
-
// Also deliver to webhook
|
|
201
|
-
webhook: {
|
|
202
|
-
url: 'https://api.example.com/webhooks/csv-import',
|
|
203
|
-
headers: {
|
|
204
|
-
'Authorization': 'Bearer your-api-token',
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
onComplete: () => {
|
|
208
|
-
console.log('All chunks processed and webhook delivery initiated');
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
return (
|
|
214
|
-
<button onClick={handleImport}>
|
|
215
|
-
Import CSV
|
|
216
|
-
</button>
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
170
|
```
|
|
220
171
|
|
|
221
|
-
|
|
172
|
+
## Advanced Example
|
|
222
173
|
|
|
223
174
|
```tsx
|
|
224
|
-
import { useExpressCSV, x, type Infer } from
|
|
225
|
-
import { useState } from
|
|
175
|
+
import { useExpressCSV, x, type Infer } from "@expresscsv/react";
|
|
176
|
+
import { useState } from "react";
|
|
226
177
|
|
|
227
178
|
const candidateSchema = x.row({
|
|
228
|
-
firstName: x.string().label(
|
|
229
|
-
lastName: x.string().label(
|
|
230
|
-
email: x.string().email().label(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
179
|
+
firstName: x.string().label("First Name"),
|
|
180
|
+
lastName: x.string().label("Last Name"),
|
|
181
|
+
email: x.string().email().label("Email"),
|
|
182
|
+
level: x
|
|
183
|
+
.select([
|
|
184
|
+
{ label: "Entry Level", value: "entry" },
|
|
185
|
+
{ label: "Mid Level", value: "mid" },
|
|
186
|
+
{ label: "Senior Level", value: "senior" },
|
|
187
|
+
])
|
|
188
|
+
.label("Experience Level"),
|
|
189
|
+
salary: x.number().currency("USD").min(30000).label("Expected Salary"),
|
|
237
190
|
});
|
|
238
191
|
|
|
239
192
|
function CandidateImporter() {
|
|
240
|
-
const [
|
|
241
|
-
const [error, setError] = useState<string | null>(null);
|
|
193
|
+
const [status, setStatus] = useState<string | null>(null);
|
|
242
194
|
|
|
243
|
-
const { open } = useExpressCSV({
|
|
195
|
+
const { open, isOpen } = useExpressCSV({
|
|
244
196
|
schema: candidateSchema,
|
|
245
|
-
|
|
197
|
+
publishableKey: "your-publishable-key",
|
|
246
198
|
importIdentifier: "candidate-import",
|
|
247
|
-
|
|
248
|
-
|
|
199
|
+
title: "Import Candidates",
|
|
200
|
+
developerMode: process.env.NODE_ENV === "development",
|
|
249
201
|
});
|
|
250
202
|
|
|
251
203
|
const handleImport = () => {
|
|
252
|
-
|
|
253
|
-
setError(null);
|
|
204
|
+
setStatus(null);
|
|
254
205
|
|
|
255
206
|
open({
|
|
256
207
|
onData: async (chunk, next) => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
setError('Failed to import candidates. Please try again.');
|
|
263
|
-
throw err;
|
|
264
|
-
}
|
|
208
|
+
setStatus(
|
|
209
|
+
`Processing chunk ${chunk.currentChunkIndex + 1}/${chunk.totalChunks}...`
|
|
210
|
+
);
|
|
211
|
+
await importCandidates(chunk.records);
|
|
212
|
+
next();
|
|
265
213
|
},
|
|
266
214
|
onComplete: () => {
|
|
267
|
-
|
|
268
|
-
alert('Successfully imported all candidates!');
|
|
215
|
+
setStatus("Import complete!");
|
|
269
216
|
},
|
|
270
217
|
onError: (error) => {
|
|
271
|
-
|
|
272
|
-
setError(`Import failed: ${error.message}`);
|
|
218
|
+
setStatus(`Error: ${error.message}`);
|
|
273
219
|
},
|
|
274
220
|
onCancel: () => {
|
|
275
|
-
|
|
221
|
+
setStatus(null);
|
|
276
222
|
},
|
|
277
223
|
});
|
|
278
224
|
};
|
|
279
225
|
|
|
280
226
|
return (
|
|
281
227
|
<div>
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
<button
|
|
285
|
-
onClick={handleImport}
|
|
286
|
-
disabled={isLoading}
|
|
287
|
-
className="import-button"
|
|
288
|
-
>
|
|
289
|
-
{isLoading ? 'Processing...' : 'Import Candidates'}
|
|
228
|
+
<button onClick={handleImport} disabled={isOpen}>
|
|
229
|
+
{isOpen ? "Importing..." : "Import Candidates"}
|
|
290
230
|
</button>
|
|
231
|
+
{status && <p>{status}</p>}
|
|
291
232
|
</div>
|
|
292
233
|
);
|
|
293
234
|
}
|
|
@@ -295,126 +236,134 @@ function CandidateImporter() {
|
|
|
295
236
|
|
|
296
237
|
## API Reference
|
|
297
238
|
|
|
298
|
-
### useExpressCSV
|
|
239
|
+
### `useExpressCSV(options)`
|
|
299
240
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
```tsx
|
|
305
|
-
interface UseExpressCSVOptions<TSchema> {
|
|
306
|
-
schema: TSchema;
|
|
307
|
-
title?: string;
|
|
308
|
-
importIdentifier: string;
|
|
309
|
-
publishableKey: string;
|
|
310
|
-
debug?: boolean;
|
|
311
|
-
developerMode?: boolean;
|
|
312
|
-
preload?: boolean; // Defaults to true
|
|
313
|
-
theme?: ECSVTheme;
|
|
314
|
-
colorMode?: ColorModeConfig;
|
|
315
|
-
customCSS?: string;
|
|
316
|
-
fonts?: Record<string, ECSVFontSource>;
|
|
317
|
-
stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
#### Returns
|
|
322
|
-
|
|
323
|
-
```tsx
|
|
324
|
-
{
|
|
241
|
+
```typescript
|
|
242
|
+
function useExpressCSV<TSchema>(
|
|
243
|
+
options: UseExpressCSVOptions<TSchema>
|
|
244
|
+
): {
|
|
325
245
|
open: (options: OpenOptions<Infer<TSchema>>) => void;
|
|
326
246
|
widgetState: WidgetState;
|
|
327
247
|
isInitialising: boolean;
|
|
328
248
|
isOpen: boolean;
|
|
329
|
-
}
|
|
249
|
+
};
|
|
330
250
|
```
|
|
331
251
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
252
|
+
#### Options
|
|
253
|
+
|
|
254
|
+
| Option | Type | Required | Default | Description |
|
|
255
|
+
|---|---|---|---|---|
|
|
256
|
+
| `schema` | Schema | Yes | - | Schema definition created with `x.row()` |
|
|
257
|
+
| `publishableKey` | `string` | Yes | - | Your publishable key from the [dashboard](https://expresscsv.com) |
|
|
258
|
+
| `importIdentifier` | `string` | Yes | - | Unique identifier for this import type |
|
|
259
|
+
| `title` | `string` | No | - | Title shown in the widget header |
|
|
260
|
+
| `preload` | `boolean` | No | `true` | Preload widget for instant display |
|
|
261
|
+
| `debug` | `boolean` | No | `false` | Enable debug logging |
|
|
262
|
+
| `developerMode` | `boolean` | No | `false` | Enable developer mode features |
|
|
263
|
+
| `theme` | `ECSVTheme` | No | - | Custom theme configuration |
|
|
264
|
+
| `colorMode` | `ColorModeConfig` | No | - | Light/dark mode settings |
|
|
265
|
+
| `customCSS` | `string` | No | - | Custom CSS to inject into the widget |
|
|
266
|
+
| `fonts` | `Record<string, ECSVFontSource>` | No | - | Custom font sources |
|
|
267
|
+
| `stepDisplay` | `'progressBar' \| 'segmented' \| 'numbered'` | No | `'progressBar'` | Step indicator style |
|
|
268
|
+
| `previewSchemaBeforeUpload` | `boolean` | No | `true` | Show schema preview before upload |
|
|
269
|
+
| `templateDownload` | `TemplateDownloadConfig` | No | - | Template download configuration |
|
|
270
|
+
| `saveSession` | `boolean` | No | - | Persist session state |
|
|
271
|
+
| `locale` | `DeepPartial<ExpressCSVLocaleInput>` | No | - | Localization overrides |
|
|
272
|
+
|
|
273
|
+
#### Return Value
|
|
274
|
+
|
|
275
|
+
| Property | Type | Description |
|
|
276
|
+
|---|---|---|
|
|
277
|
+
| `open` | `(options: OpenOptions) => void` | Opens the widget. Requires at least one of `onData` or `webhook`. |
|
|
278
|
+
| `widgetState` | `WidgetState` | Current widget state (reactive) |
|
|
279
|
+
| `isInitialising` | `boolean` | `true` while the widget is initializing or opening |
|
|
280
|
+
| `isOpen` | `boolean` | `true` while the widget is open |
|
|
281
|
+
|
|
282
|
+
### `OpenOptions<T>`
|
|
283
|
+
|
|
284
|
+
Options passed to `open()`. At least one of `onData` or `webhook` must be provided.
|
|
285
|
+
|
|
286
|
+
| Option | Type | Required | Description |
|
|
287
|
+
|---|---|---|---|
|
|
288
|
+
| `onData` | `(chunk: RecordsChunk<T>, next: () => void) => void` | * | Callback for each chunk. Call `next()` to continue. |
|
|
289
|
+
| `webhook` | `WebhookConfig` | * | Webhook endpoint for server-side delivery |
|
|
290
|
+
| `chunkSize` | `number` | No | Records per chunk (default: 1000) |
|
|
291
|
+
| `onComplete` | `() => void` | No | Called when all chunks have been processed |
|
|
292
|
+
| `onCancel` | `() => void` | No | Called when the user cancels the import |
|
|
293
|
+
| `onError` | `(error: Error) => void` | No | Called when an error occurs |
|
|
294
|
+
| `onWidgetOpen` | `() => void` | No | Called when the widget opens |
|
|
295
|
+
| `onWidgetClose` | `(reason: string) => void` | No | Called when the widget closes |
|
|
296
|
+
| `onStepChange` | `(stepId, previousStepId?) => void` | No | Called when the wizard step changes |
|
|
297
|
+
|
|
298
|
+
\* At least one of `onData` or `webhook` is required.
|
|
299
|
+
|
|
300
|
+
### `RecordsChunk<T>`
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
interface RecordsChunk<T> {
|
|
304
|
+
records: T[];
|
|
305
|
+
totalChunks: number;
|
|
306
|
+
currentChunkIndex: number;
|
|
307
|
+
totalRecords: number;
|
|
347
308
|
}
|
|
348
309
|
```
|
|
349
310
|
|
|
350
|
-
### WebhookConfig
|
|
351
|
-
|
|
352
|
-
Configuration for webhook delivery:
|
|
311
|
+
### `WebhookConfig`
|
|
353
312
|
|
|
354
|
-
```
|
|
313
|
+
```typescript
|
|
355
314
|
interface WebhookConfig {
|
|
356
|
-
url: string;
|
|
357
|
-
headers?: Record<string, string>;
|
|
358
|
-
method?:
|
|
359
|
-
timeout?: number;
|
|
360
|
-
retries?: number;
|
|
361
|
-
metadata?: Record<string, unknown>;
|
|
315
|
+
url: string;
|
|
316
|
+
headers?: Record<string, string>;
|
|
317
|
+
method?: "POST" | "PUT" | "PATCH";
|
|
318
|
+
timeout?: number;
|
|
319
|
+
retries?: number;
|
|
320
|
+
metadata?: Record<string, unknown>;
|
|
362
321
|
}
|
|
363
322
|
```
|
|
364
323
|
|
|
365
|
-
**Note:** The `chunkSize` option in `OpenOptions` controls the chunk size for both `onData` callbacks and webhook delivery. The backend will deliver webhooks in chunks of this size (or default 1000 if not specified).
|
|
366
|
-
|
|
367
324
|
### Schema Builder
|
|
368
325
|
|
|
369
|
-
The schema builder
|
|
326
|
+
The `x` schema builder is re-exported from `@expresscsv/sdk`. See the [SDK README](https://www.npmjs.com/package/@expresscsv/sdk#schema-builder) for the full field type and modifier reference.
|
|
370
327
|
|
|
371
328
|
```tsx
|
|
329
|
+
import { x } from "@expresscsv/react";
|
|
330
|
+
|
|
372
331
|
const schema = x.row({
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
// Email validation
|
|
377
|
-
email: x.string().email().label('Email Address'),
|
|
378
|
-
|
|
379
|
-
// Number fields with constraints
|
|
380
|
-
age: x.number().label('Age').min(18).max(100),
|
|
381
|
-
|
|
382
|
-
// Currency fields
|
|
383
|
-
salary: x.number().currency('USD').min(30000),
|
|
384
|
-
|
|
385
|
-
// Select dropdowns
|
|
332
|
+
name: x.string().label("Name").min(2).max(100),
|
|
333
|
+
email: x.string().email().label("Email"),
|
|
334
|
+
age: x.number().label("Age").min(0).integer(),
|
|
386
335
|
role: x.select([
|
|
387
|
-
{ label:
|
|
388
|
-
{ label:
|
|
389
|
-
]).label(
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
startDate: x.date().label('Start Date'),
|
|
393
|
-
|
|
394
|
-
// Boolean fields
|
|
395
|
-
isActive: x.boolean().label('Active Status'),
|
|
336
|
+
{ label: "Admin", value: "admin" },
|
|
337
|
+
{ label: "User", value: "user" },
|
|
338
|
+
]).label("Role"),
|
|
339
|
+
startDate: x.date().label("Start Date"),
|
|
340
|
+
isActive: x.boolean().label("Active"),
|
|
396
341
|
});
|
|
397
342
|
```
|
|
398
343
|
|
|
399
|
-
## TypeScript
|
|
344
|
+
## TypeScript
|
|
400
345
|
|
|
401
|
-
|
|
346
|
+
Full type inference from your schema is built in:
|
|
402
347
|
|
|
403
348
|
```tsx
|
|
349
|
+
import { x, type Infer } from "@expresscsv/react";
|
|
350
|
+
|
|
404
351
|
const schema = x.row({
|
|
405
352
|
name: x.string(),
|
|
406
353
|
age: x.number(),
|
|
407
354
|
});
|
|
408
355
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
results.forEach(row => {
|
|
412
|
-
console.log(row.name); // string
|
|
413
|
-
console.log(row.age); // number
|
|
414
|
-
});
|
|
415
|
-
};
|
|
356
|
+
type Row = Infer<typeof schema>;
|
|
357
|
+
// { name: string; age: number }
|
|
416
358
|
```
|
|
417
359
|
|
|
360
|
+
The `onData` callback receives `RecordsChunk<Row>` automatically -- no manual type annotations needed.
|
|
361
|
+
|
|
362
|
+
## Resources
|
|
363
|
+
|
|
364
|
+
- [ExpressCSV Dashboard](https://expresscsv.com) -- manage your imports and API keys
|
|
365
|
+
- [`@expresscsv/sdk`](https://www.npmjs.com/package/@expresscsv/sdk) -- vanilla JS SDK (no React dependency)
|
|
366
|
+
|
|
418
367
|
## License
|
|
419
368
|
|
|
420
|
-
|
|
369
|
+
[MIT](./LICENSE)
|