@expresscsv/react 0.1.17 → 0.1.19

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 CHANGED
@@ -5,6 +5,8 @@
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
 
8
+ If you want to define schemas in shared or backend code without any frontend dependencies, use [`@expresscsv/schemas`](https://www.npmjs.com/package/@expresscsv/schemas) for the schema definition itself, then pass that schema into `@expresscsv/react`.
9
+
8
10
  ## Installation
9
11
 
10
12
  ```bash
@@ -104,6 +106,40 @@ const { open } = useExpressCSV({
104
106
  });
105
107
  ```
106
108
 
109
+ ## Template Downloads
110
+
111
+ Generated templates can include example rows that match your schema.
112
+
113
+ ```tsx
114
+ import { useExpressCSV, x, type Infer } from "@expresscsv/react";
115
+
116
+ const candidateSchema = x.row({
117
+ firstName: x.string().label("First Name"),
118
+ email: x.string().email().label("Email"),
119
+ role: x.select([
120
+ { label: "Admin", value: "admin" },
121
+ { label: "Editor", value: "editor" },
122
+ ]).label("Role"),
123
+ });
124
+
125
+ const { open } = useExpressCSV({
126
+ schema: candidateSchema,
127
+ publishableKey: "your-publishable-key",
128
+ importIdentifier: "candidate-import",
129
+ templateDownload: {
130
+ source: "generate",
131
+ formats: ["csv", "xlsx"],
132
+ exampleRows: () => [
133
+ {
134
+ firstName: "Alice",
135
+ email: "alice@example.com",
136
+ role: "admin",
137
+ },
138
+ ],
139
+ },
140
+ });
141
+ ```
142
+
107
143
  ## Theming and Styling
108
144
 
109
145
  Customize the widget's appearance with the `theme`, `colorMode`, `customCSS`, and `fonts` options.
@@ -312,6 +348,8 @@ interface WebhookPayload {
312
348
  }
313
349
  ```
314
350
 
351
+ When you already have a schema, prefer `InferWebhookPayload<typeof schema>` from `@expresscsv/schemas` instead of rewriting this object shape by hand.
352
+
315
353
  Example payload:
316
354
 
317
355
  ```json
@@ -350,6 +388,25 @@ Your endpoint should return a **2xx** status code to acknowledge each chunk. Non
350
388
 
351
389
  Chunks are delivered **serially** — the next chunk is only sent after the previous one succeeds.
352
390
 
391
+ If your schema lives in shared or backend code via `@expresscsv/schemas`, you can reuse the built-in webhook payload helper in your webhook handler:
392
+
393
+ ```typescript
394
+ import express from "express";
395
+ import type { InferWebhookPayload } from "@expresscsv/schemas";
396
+ import { candidateSchema } from "../shared/candidate-schema";
397
+
398
+ const app = express();
399
+ app.use(express.json());
400
+
401
+ app.post("/webhooks/csv-import", async (req, res) => {
402
+ const payload = req.body as InferWebhookPayload<typeof candidateSchema>;
403
+
404
+ await importCandidates(payload.records);
405
+
406
+ res.status(200).json({ ok: true });
407
+ });
408
+ ```
409
+
353
410
  Below is a minimal Express.js example:
354
411
 
355
412
  ```typescript
@@ -507,7 +564,7 @@ function useExpressCSV<TSchema>(
507
564
  | `fonts` | `Record<string, ECSVFontSource>` | No | - | Custom font sources |
508
565
  | `stepDisplay` | `'progressBar' \| 'segmented' \| 'numbered'` | No | `'progressBar'` | Step indicator style |
509
566
  | `previewSchemaBeforeUpload` | `boolean` | No | `true` | Show schema preview before upload |
510
- | `templateDownload` | `TemplateDownloadConfig` | No | - | Template download configuration |
567
+ | `templateDownload` | `TemplateDownloadOptions<TSchema>` | No | - | Template download configuration with optional schema-typed example rows |
511
568
  | `saveSession` | `boolean` | No | - | Persist session state |
512
569
  | `locale` | `DeepPartial<ExpressCSVLocaleInput>` | No | - | Localization overrides |
513
570
 
@@ -566,6 +623,8 @@ interface WebhookConfig {
566
623
 
567
624
  The `x` schema builder provides a type-safe, fluent API for defining your CSV structure.
568
625
 
626
+ For apps that share schemas with backend code or webhook receivers, prefer defining the schema in `@expresscsv/schemas` and importing it into your React app. That keeps schema authoring free of React and widget dependencies, while also giving your backend access to `InferWebhookPayload<typeof schema>`.
627
+
569
628
  #### Field Types
570
629
 
571
630
  ```tsx
@@ -604,6 +663,65 @@ const schema = x.row({
604
663
  });
605
664
  ```
606
665
 
666
+ #### Runtime-Built Schemas
667
+
668
+ For a fixed schema, keep it directly in `x.row(...)`:
669
+
670
+ ```tsx
671
+ const schema = x.row({
672
+ email: x.string().email().label("Email"),
673
+ lifecycleStage: x
674
+ .select([
675
+ { label: "Lead", value: "lead" },
676
+ { label: "Customer", value: "customer" },
677
+ { label: "Churned", value: "churned" },
678
+ ])
679
+ .label("Lifecycle Stage"),
680
+ accountOwner: x.string().label("Account Owner").optional(),
681
+ contractValue: x.number().currency("USD").label("Contract Value").optional(),
682
+ });
683
+ ```
684
+
685
+ `x.row(...)` also works for runtime-built schemas, which is useful when the shape depends on account data, user preferences, or enabled custom fields.
686
+
687
+ ```tsx
688
+ import { x } from "@expresscsv/react";
689
+
690
+ function buildCustomerSchema(options: {
691
+ collectCrmId: boolean;
692
+ collectHealthScore: boolean;
693
+ collectSegment: boolean;
694
+ }) {
695
+ return x.row({
696
+ email: x.string().email().label("Email"),
697
+ companyName: x.string().label("Company Name"),
698
+ ...(options.collectCrmId ? { crmId: x.string().label("CRM ID") } : {}),
699
+ ...(options.collectHealthScore
700
+ ? { healthScore: x.number().label("Health Score") }
701
+ : {}),
702
+ ...(options.collectSegment
703
+ ? {
704
+ segment: x
705
+ .select([
706
+ { label: "SMB", value: "smb" },
707
+ { label: "Mid-Market", value: "mid-market" },
708
+ { label: "Enterprise", value: "enterprise" },
709
+ ])
710
+ .label("Segment"),
711
+ }
712
+ : {}),
713
+ });
714
+ }
715
+
716
+ const schema = buildCustomerSchema({
717
+ collectCrmId: true,
718
+ collectHealthScore: true,
719
+ collectSegment: false,
720
+ });
721
+ ```
722
+
723
+ Dynamic schema assembly preserves the same runtime parsing behavior, but it can widen `Infer<typeof schema>` because the exact keys are no longer fully known to TypeScript. Use it intentionally.
724
+
607
725
  #### Common Modifiers
608
726
 
609
727
  All field types support:
package/dist/index.d.cts CHANGED
@@ -3015,6 +3015,11 @@ declare class ExRow<T extends ExRowShape> extends ExType<{
3015
3015
  * Factory method to create an ExRow validator for a row of data
3016
3016
  *
3017
3017
  * @param shape - Object defining the structure of fields in the row
3018
+ *
3019
+ * Object literals preserve exact key inference. If callers build the shape
3020
+ * through a widened variable or conditional assembly, TypeScript may widen
3021
+ * the resulting row type as well.
3022
+ *
3018
3023
  * @returns A new ExRow instance
3019
3024
  */
3020
3025
  static create<T extends ExRowShape>(shape: T): ExRow<T>;
@@ -3695,19 +3700,29 @@ export declare interface TailwindThemeVars {
3695
3700
 
3696
3701
  /**
3697
3702
  * Configuration for template download in the upload step.
3698
- * When `source` is `"generate"`, a header-only template file is created
3699
- * client-side from the schema column names.
3703
+ * When `source` is `"generate"`, a template file is created client-side
3704
+ * from the schema column names and optional example rows.
3700
3705
  */
3701
3706
  export declare interface TemplateDownloadConfig {
3702
3707
  source: 'generate';
3703
3708
  formats?: TemplateDownloadFormat[];
3709
+ exampleRows?: TemplateDownloadExampleRow[];
3704
3710
  }
3705
3711
 
3712
+ /**
3713
+ * A serializable example row used to populate generated templates.
3714
+ */
3715
+ export declare type TemplateDownloadExampleRow = Record<string, unknown>;
3716
+
3706
3717
  /**
3707
3718
  * Template download format
3708
3719
  */
3709
3720
  export declare type TemplateDownloadFormat = 'csv' | 'xlsx';
3710
3721
 
3722
+ export declare type TemplateDownloadOptions<TSchema extends ExType<unknown, ExBaseDef, unknown>> = Omit<TemplateDownloadConfig, 'exampleRows'> & {
3723
+ exampleRows?: Infer<TSchema>[] | (() => Infer<TSchema>[]);
3724
+ };
3725
+
3711
3726
  /**
3712
3727
  * A branded string type for locale entries that require interpolation via `{variable}` syntax.
3713
3728
  * Widget code cannot render a `TemplateString` directly in JSX — it must go through the `t()` function.
@@ -3754,7 +3769,7 @@ export declare interface UseExpressCSVOptions<TSchema extends ExType<unknown, Ex
3754
3769
  fonts?: Record<string, ECSVFontSource>;
3755
3770
  stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
3756
3771
  previewSchemaBeforeUpload?: boolean;
3757
- templateDownload?: TemplateDownloadConfig;
3772
+ templateDownload?: TemplateDownloadOptions<TSchema>;
3758
3773
  saveSession?: boolean;
3759
3774
  locale?: DeepPartial<ExpressCSVLocaleInput>;
3760
3775
  disableStatusStep?: boolean;
package/dist/index.d.mts CHANGED
@@ -3015,6 +3015,11 @@ declare class ExRow<T extends ExRowShape> extends ExType<{
3015
3015
  * Factory method to create an ExRow validator for a row of data
3016
3016
  *
3017
3017
  * @param shape - Object defining the structure of fields in the row
3018
+ *
3019
+ * Object literals preserve exact key inference. If callers build the shape
3020
+ * through a widened variable or conditional assembly, TypeScript may widen
3021
+ * the resulting row type as well.
3022
+ *
3018
3023
  * @returns A new ExRow instance
3019
3024
  */
3020
3025
  static create<T extends ExRowShape>(shape: T): ExRow<T>;
@@ -3695,19 +3700,29 @@ export declare interface TailwindThemeVars {
3695
3700
 
3696
3701
  /**
3697
3702
  * Configuration for template download in the upload step.
3698
- * When `source` is `"generate"`, a header-only template file is created
3699
- * client-side from the schema column names.
3703
+ * When `source` is `"generate"`, a template file is created client-side
3704
+ * from the schema column names and optional example rows.
3700
3705
  */
3701
3706
  export declare interface TemplateDownloadConfig {
3702
3707
  source: 'generate';
3703
3708
  formats?: TemplateDownloadFormat[];
3709
+ exampleRows?: TemplateDownloadExampleRow[];
3704
3710
  }
3705
3711
 
3712
+ /**
3713
+ * A serializable example row used to populate generated templates.
3714
+ */
3715
+ export declare type TemplateDownloadExampleRow = Record<string, unknown>;
3716
+
3706
3717
  /**
3707
3718
  * Template download format
3708
3719
  */
3709
3720
  export declare type TemplateDownloadFormat = 'csv' | 'xlsx';
3710
3721
 
3722
+ export declare type TemplateDownloadOptions<TSchema extends ExType<unknown, ExBaseDef, unknown>> = Omit<TemplateDownloadConfig, 'exampleRows'> & {
3723
+ exampleRows?: Infer<TSchema>[] | (() => Infer<TSchema>[]);
3724
+ };
3725
+
3711
3726
  /**
3712
3727
  * A branded string type for locale entries that require interpolation via `{variable}` syntax.
3713
3728
  * Widget code cannot render a `TemplateString` directly in JSX — it must go through the `t()` function.
@@ -3754,7 +3769,7 @@ export declare interface UseExpressCSVOptions<TSchema extends ExType<unknown, Ex
3754
3769
  fonts?: Record<string, ECSVFontSource>;
3755
3770
  stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
3756
3771
  previewSchemaBeforeUpload?: boolean;
3757
- templateDownload?: TemplateDownloadConfig;
3772
+ templateDownload?: TemplateDownloadOptions<TSchema>;
3758
3773
  saveSession?: boolean;
3759
3774
  locale?: DeepPartial<ExpressCSVLocaleInput>;
3760
3775
  disableStatusStep?: boolean;
package/dist/index.d.ts CHANGED
@@ -3015,6 +3015,11 @@ declare class ExRow<T extends ExRowShape> extends ExType<{
3015
3015
  * Factory method to create an ExRow validator for a row of data
3016
3016
  *
3017
3017
  * @param shape - Object defining the structure of fields in the row
3018
+ *
3019
+ * Object literals preserve exact key inference. If callers build the shape
3020
+ * through a widened variable or conditional assembly, TypeScript may widen
3021
+ * the resulting row type as well.
3022
+ *
3018
3023
  * @returns A new ExRow instance
3019
3024
  */
3020
3025
  static create<T extends ExRowShape>(shape: T): ExRow<T>;
@@ -3695,19 +3700,29 @@ export declare interface TailwindThemeVars {
3695
3700
 
3696
3701
  /**
3697
3702
  * Configuration for template download in the upload step.
3698
- * When `source` is `"generate"`, a header-only template file is created
3699
- * client-side from the schema column names.
3703
+ * When `source` is `"generate"`, a template file is created client-side
3704
+ * from the schema column names and optional example rows.
3700
3705
  */
3701
3706
  export declare interface TemplateDownloadConfig {
3702
3707
  source: 'generate';
3703
3708
  formats?: TemplateDownloadFormat[];
3709
+ exampleRows?: TemplateDownloadExampleRow[];
3704
3710
  }
3705
3711
 
3712
+ /**
3713
+ * A serializable example row used to populate generated templates.
3714
+ */
3715
+ export declare type TemplateDownloadExampleRow = Record<string, unknown>;
3716
+
3706
3717
  /**
3707
3718
  * Template download format
3708
3719
  */
3709
3720
  export declare type TemplateDownloadFormat = 'csv' | 'xlsx';
3710
3721
 
3722
+ export declare type TemplateDownloadOptions<TSchema extends ExType<unknown, ExBaseDef, unknown>> = Omit<TemplateDownloadConfig, 'exampleRows'> & {
3723
+ exampleRows?: Infer<TSchema>[] | (() => Infer<TSchema>[]);
3724
+ };
3725
+
3711
3726
  /**
3712
3727
  * A branded string type for locale entries that require interpolation via `{variable}` syntax.
3713
3728
  * Widget code cannot render a `TemplateString` directly in JSX — it must go through the `t()` function.
@@ -3754,7 +3769,7 @@ export declare interface UseExpressCSVOptions<TSchema extends ExType<unknown, Ex
3754
3769
  fonts?: Record<string, ECSVFontSource>;
3755
3770
  stepDisplay?: 'progressBar' | 'segmented' | 'numbered';
3756
3771
  previewSchemaBeforeUpload?: boolean;
3757
- templateDownload?: TemplateDownloadConfig;
3772
+ templateDownload?: TemplateDownloadOptions<TSchema>;
3758
3773
  saveSession?: boolean;
3759
3774
  locale?: DeepPartial<ExpressCSVLocaleInput>;
3760
3775
  disableStatusStep?: boolean;