@expresscsv/react 0.1.5 → 0.1.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ExpressCSV
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # @expresscsv/react
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@expresscsv/react.svg)](https://www.npmjs.com/package/@expresscsv/react)
4
- [![license](https://img.shields.io/npm/l/@expresscsv/react.svg)](https://github.com/nicholasgriffintn/expresscsv/blob/main/LICENSE)
4
+ ![license](https://img.shields.io/npm/l/@expresscsv/react.svg)
5
5
 
6
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.
7
7
 
@@ -93,7 +93,7 @@ const handleImport = () => {
93
93
  };
94
94
  ```
95
95
 
96
- To disable preloading:
96
+ To disable preloading (there will be a brief loading screen instead):
97
97
 
98
98
  ```tsx
99
99
  const { open } = useExpressCSV({
@@ -104,6 +104,139 @@ const { open } = useExpressCSV({
104
104
  });
105
105
  ```
106
106
 
107
+ ## Theming and Styling
108
+
109
+ Customize the widget's appearance with the `theme`, `colorMode`, `customCSS`, and `fonts` options.
110
+
111
+ ### Theme
112
+
113
+ Use the `theme` option to override CSS variables (colors, radius, typography). Pass either a single theme (applies to both light and dark) or a dual-mode theme with separate light/dark values:
114
+
115
+ ```tsx
116
+ import { useExpressCSV, x, type ECSVTheme } from "@expresscsv/react";
117
+
118
+ // Single theme (both modes)
119
+ const theme: ECSVTheme = {
120
+ primary: "#4F46E5",
121
+ "primary-foreground": "#ffffff",
122
+ background: "#ffffff",
123
+ foreground: "#0f172a",
124
+ border: "#e5e7eb",
125
+ ring: "#A5B4FC",
126
+ radius: "0.5rem",
127
+ };
128
+
129
+ // Dual-mode (light and dark)
130
+ const dualTheme: ECSVTheme = {
131
+ modes: {
132
+ light: {
133
+ primary: "#4F46E5",
134
+ background: "#ffffff",
135
+ foreground: "#0f172a",
136
+ },
137
+ dark: {
138
+ primary: "#a5b4fc",
139
+ background: "#09090b",
140
+ foreground: "#fafafa",
141
+ },
142
+ },
143
+ };
144
+
145
+ function App() {
146
+ const { open } = useExpressCSV({
147
+ schema,
148
+ publishableKey: "your-publishable-key",
149
+ importIdentifier: "user-import",
150
+ theme,
151
+ });
152
+ // ...
153
+ }
154
+ ```
155
+
156
+ Theme variables (light mode defaults):
157
+
158
+ | Variable | Default |
159
+ |---|---|
160
+ | `radius` | `0.625rem` |
161
+ | `background` | `oklch(1 0 0)` |
162
+ | `foreground` | `oklch(0.145 0 0)` |
163
+ | `card` | `oklch(1 0 0)` |
164
+ | `card-foreground` | `oklch(0.145 0 0)` |
165
+ | `popover` | `oklch(1 0 0)` |
166
+ | `popover-foreground` | `oklch(0.145 0 0)` |
167
+ | `primary` | `oklch(0.205 0 0)` |
168
+ | `primary-foreground` | `oklch(0.985 0 0)` |
169
+ | `secondary` | `oklch(0.97 0 0)` |
170
+ | `secondary-foreground` | `oklch(0.205 0 0)` |
171
+ | `muted` | `oklch(0.97 0 0)` |
172
+ | `muted-foreground` | `oklch(0.556 0 0)` |
173
+ | `accent` | `oklch(0.7 0.2 145)` |
174
+ | `accent-foreground` | `oklch(0.985 0 0)` |
175
+ | `destructive` | `oklch(0.577 0.245 27.325)` |
176
+ | `destructive-foreground` | `oklch(0.985 0 0)` |
177
+ | `success` | `oklch(0.7 0.2 145)` |
178
+ | `success-foreground` | `oklch(0.985 0 0)` |
179
+ | `warning` | `oklch(0.769 0.188 70)` |
180
+ | `warning-foreground` | `oklch(0.985 0 0)` |
181
+ | `border` | `oklch(0.922 0 0)` |
182
+ | `input` | `oklch(0.922 0 0)` |
183
+ | `ring` | `oklch(0.708 0 0)` |
184
+ | `font-title` | `inherit` |
185
+ | `font-body` | `inherit` |
186
+
187
+ ### Color Mode
188
+
189
+ Control light/dark mode with `colorMode`:
190
+
191
+ ```tsx
192
+ const { open } = useExpressCSV({
193
+ schema,
194
+ publishableKey: "your-publishable-key",
195
+ importIdentifier: "user-import",
196
+ colorMode: "system", // 'light' | 'dark' | 'system'
197
+ });
198
+ ```
199
+
200
+ ### Custom CSS
201
+
202
+ Inject custom CSS for fine-grained styling overrides.
203
+
204
+ ```tsx
205
+ const { open } = useExpressCSV({
206
+ schema,
207
+ publishableKey: "your-publishable-key",
208
+ importIdentifier: "user-import",
209
+ customCSS: `
210
+ .ecsv [data-step="upload"] {
211
+ border-radius: 1rem;
212
+ }
213
+ .ecsv button {
214
+ font-weight: 600;
215
+ }
216
+ `,
217
+ });
218
+ ```
219
+
220
+ ### Custom Fonts
221
+
222
+ Load custom fonts via the `fonts` option:
223
+
224
+ ```tsx
225
+ const { open } = useExpressCSV({
226
+ schema,
227
+ publishableKey: "your-publishable-key",
228
+ importIdentifier: "user-import",
229
+ fonts: {
230
+ title: { source: "google", name: "Space Grotesk", weights: [400, 600, 700] },
231
+ body: { source: "custom", url: "https://example.com/font.woff2", format: "woff2" },
232
+ },
233
+ theme: {
234
+ "font-title": "'Space Grotesk', sans-serif",
235
+ "font-body": "'Custom Font', sans-serif",
236
+ },
237
+ });
238
+ ```
239
+
107
240
  ## Webhook Delivery
108
241
 
109
242
  Deliver data to a server endpoint instead of (or in addition to) processing locally:
@@ -261,7 +394,7 @@ function useExpressCSV<TSchema>(
261
394
  | `debug` | `boolean` | No | `false` | Enable debug logging |
262
395
  | `developerMode` | `boolean` | No | `false` | Enable developer mode features |
263
396
  | `theme` | `ECSVTheme` | No | - | Custom theme configuration |
264
- | `colorMode` | `ColorModeConfig` | No | - | Light/dark mode settings |
397
+ | `colorMode` | `ColorModePref` | No | - | Light/dark mode (`'light'`, `'dark'`, or `'system'`) |
265
398
  | `customCSS` | `string` | No | - | Custom CSS to inject into the widget |
266
399
  | `fonts` | `Record<string, ECSVFontSource>` | No | - | Custom font sources |
267
400
  | `stepDisplay` | `'progressBar' \| 'segmented' \| 'numbered'` | No | `'progressBar'` | Step indicator style |
@@ -301,7 +434,7 @@ Options passed to `open()`. At least one of `onData` or `webhook` must be provid
301
434
 
302
435
  ```typescript
303
436
  interface RecordsChunk<T> {
304
- records: T[];
437
+ records: T[]; // Automatically typed to your schema
305
438
  totalChunks: number;
306
439
  currentChunkIndex: number;
307
440
  totalRecords: number;
@@ -323,24 +456,86 @@ interface WebhookConfig {
323
456
 
324
457
  ### Schema Builder
325
458
 
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.
459
+ The `x` schema builder provides a type-safe, fluent API for defining your CSV structure.
460
+
461
+ #### Field Types
327
462
 
328
463
  ```tsx
329
464
  import { x } from "@expresscsv/react";
330
465
 
331
466
  const schema = x.row({
332
- name: x.string().label("Name").min(2).max(100),
467
+ // Strings with validation
468
+ name: x.string().label("Full Name").min(2).max(100),
333
469
  email: x.string().email().label("Email"),
334
- age: x.number().label("Age").min(0).integer(),
470
+ website: x.string().url().label("Website").optional(),
471
+
472
+ // Numbers with constraints
473
+ age: x.number().label("Age").min(0).max(150).integer(),
474
+ salary: x.number().currency("USD").label("Salary").min(0),
475
+
476
+ // Boolean
477
+ isActive: x.boolean().label("Active"),
478
+
479
+ // Dates and times
480
+ startDate: x.date().label("Start Date"),
481
+ createdAt: x.datetime().label("Created At"),
482
+
483
+ // Single selection (requires { label, value } objects)
335
484
  role: x.select([
336
485
  { label: "Admin", value: "admin" },
337
- { label: "User", value: "user" },
486
+ { label: "Editor", value: "editor" },
487
+ { label: "Viewer", value: "viewer" },
338
488
  ]).label("Role"),
339
- startDate: x.date().label("Start Date"),
340
- isActive: x.boolean().label("Active"),
489
+
490
+ // Multi-selection
491
+ tags: x.multiselect([
492
+ { label: "Engineering", value: "eng" },
493
+ { label: "Design", value: "design" },
494
+ { label: "Marketing", value: "mkt" },
495
+ ]).label("Tags"),
341
496
  });
342
497
  ```
343
498
 
499
+ #### Common Modifiers
500
+
501
+ All field types support:
502
+
503
+ | Modifier | Description |
504
+ |---|---|
505
+ | `.label(text)` | User-facing label shown in the widget |
506
+ | `.description(text)` | Help text for the field |
507
+ | `.example(text)` | Example value shown as placeholder |
508
+ | `.optional()` | Makes the field optional (default is required) |
509
+ | `.refine(fn)` | Custom validation function |
510
+
511
+ #### String Modifiers
512
+
513
+ | Modifier | Description |
514
+ |---|---|
515
+ | `.email()` | Validates email format |
516
+ | `.url()` | Validates URL format |
517
+ | `.uuid()` | Validates UUID format |
518
+ | `.ip()` | Validates IP address |
519
+ | `.phone()` | Validates phone number |
520
+ | `.regex(pattern)` | Matches a regular expression |
521
+ | `.min(n)` | Minimum string length |
522
+ | `.max(n)` | Maximum string length |
523
+ | `.length(n)` | Exact string length |
524
+ | `.includes(str)` | Must contain substring |
525
+ | `.startsWith(str)` | Must start with prefix |
526
+ | `.endsWith(str)` | Must end with suffix |
527
+
528
+ #### Number Modifiers
529
+
530
+ | Modifier | Description |
531
+ |---|---|
532
+ | `.min(n)` | Minimum value |
533
+ | `.max(n)` | Maximum value |
534
+ | `.integer()` | Must be a whole number |
535
+ | `.multipleOf(n)` | Must be a multiple of n |
536
+ | `.currency(code)` | Formats as currency (e.g. `"USD"`) |
537
+ | `.percentage()` | Formats as percentage |
538
+
344
539
  ## TypeScript
345
540
 
346
541
  Full type inference from your schema is built in:
package/dist/index.d.mts CHANGED
@@ -4,15 +4,6 @@ declare interface BICOptions {
4
4
 
5
5
  declare type BooleanControlType = 'toggle' | 'checkbox' | 'dropdown';
6
6
 
7
- /**
8
- * Color mode configuration
9
- */
10
- export declare interface ColorModeConfig {
11
- default?: ColorModePref;
12
- persist?: boolean;
13
- onChange?: (mode: ColorModePref) => void;
14
- }
15
-
16
7
  /**
17
8
  * Color mode preference
18
9
  */
@@ -3744,7 +3735,7 @@ export declare interface UseExpressCSVOptions<TSchema extends ExType<unknown, Ex
3744
3735
  developerMode?: boolean;
3745
3736
  preload?: boolean;
3746
3737
  theme?: ECSVTheme;
3747
- colorMode?: ColorModeConfig;
3738
+ colorMode?: ColorModePref;
3748
3739
  customCSS?: string;
3749
3740
  fonts?: Record<string, ECSVFontSource>;
3750
3741
  stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expresscsv/react",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "React component wrapper for ExpressCSV SDK",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -25,6 +25,6 @@
25
25
  "access": "public"
26
26
  },
27
27
  "peerDependencies": {
28
- "react": "^18.0.0"
28
+ "react": ">=16.8.0"
29
29
  }
30
30
  }