@updog/data-editor 0.1.14 → 0.1.16

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.
Files changed (4) hide show
  1. package/README.md +4 -10
  2. package/index.d.ts +8 -18
  3. package/index.js +55 -43
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -62,14 +62,13 @@ export function App() {
62
62
  const res = await fetch("/api/employees");
63
63
  onChunk(await res.json());
64
64
  }}
65
- onComplete={async (result, actions) => {
65
+ onComplete={async (result) => {
66
66
  for (const source of result.sources) {
67
67
  const inserts = source.rows.filter((r) => r.isNew && !r.isDeleted && r.isValid);
68
68
  const updates = source.rows.filter((r) => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
69
69
  const deletes = source.rows.filter((r) => r.isDeleted && !r.isNew);
70
70
  await persist(source.sourceId, { inserts, updates, deletes });
71
71
  }
72
- actions.reset();
73
72
  setOpen(false);
74
73
  }}
75
74
  />
@@ -121,7 +120,7 @@ In inline mode, `open` and `onClose` don't apply.
121
120
  | `open` | `boolean` | Modal only | — | Controlled modal visibility. |
122
121
  | `onClose` | `() => void` | Modal only | — | Called when the user closes the modal (X button or Escape). |
123
122
  | `loadData` | `(onChunk) => Promise<void>` | No | — | Async data loader. Stream rows in chunks, optionally tagged by source. |
124
- | `onComplete` | `(result, actions) => void` | No | — | Called on submit. See [Submission Result](#submission-result). |
123
+ | `onComplete` | `(result) => void \| Promise<void>` | No | — | Called on submit. See [Submission Result](#submission-result). |
125
124
  | `variant` | `"editor"` \| `"uploader"` | No | `"editor"` | Initial view. `"uploader"` opens the import wizard first. |
126
125
  | `translations` | `DataEditorTranslations` | No | — | Partial i18n overrides. |
127
126
  | `locale` | `string` | No | `"en"` | BCP 47 locale tag. |
@@ -263,7 +262,7 @@ loadData={async (onChunk) => {
263
262
 
264
263
  ## Submission Result
265
264
 
266
- `onComplete(result, actions)` fires when the user submits.
265
+ `onComplete(result)` fires when the user submits. May return a promise.
267
266
 
268
267
  ```ts
269
268
  type DataEditorResult<TRow> = {
@@ -280,23 +279,18 @@ type DataEditorResult<TRow> = {
280
279
  }[];
281
280
  counts: { new: number; changed: number; deleted: number; invalid: number };
282
281
  };
283
-
284
- type DataEditorActions = {
285
- reset: () => void; // discard changes and reload via loadData
286
- };
287
282
  ```
288
283
 
289
284
  Pristine backend rows (unchanged, not deleted, not new) are omitted from `sources[].rows` — they're no-ops. The flags are orthogonal: a row can be new, changed, and deleted at once. You own the routing rules:
290
285
 
291
286
  ```tsx
292
- onComplete={async (result, actions) => {
287
+ onComplete={async (result) => {
293
288
  for (const source of result.sources) {
294
289
  const inserts = source.rows.filter((r) => r.isNew && !r.isDeleted && r.isValid);
295
290
  const updates = source.rows.filter((r) => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
296
291
  const deletes = source.rows.filter((r) => r.isDeleted && !r.isNew);
297
292
  await persist(source.sourceId, { inserts, updates, deletes });
298
293
  }
299
- actions.reset();
300
294
  }}
301
295
  ```
302
296
 
package/index.d.ts CHANGED
@@ -2606,14 +2606,6 @@ type UndoRedoResult = {
2606
2606
  success: boolean;
2607
2607
  targetCell?: CellLocation;
2608
2608
  };
2609
- /**
2610
- * Actions available inside the `onComplete` callback. Call `reset()` after a
2611
- * successful save to clear the editor and re-fetch via `loadData`.
2612
- */
2613
- type DataEditorActions = {
2614
- /** Discard all changes and reload the original data via `loadData`. */
2615
- reset: () => void;
2616
- };
2617
2609
  /**
2618
2610
  * A single row in the submit result, with orthogonal flags describing its state.
2619
2611
  *
@@ -2651,14 +2643,13 @@ type DataEditorSourceResult<TRow extends DataEditorRow = DataEditorRow> = {
2651
2643
  *
2652
2644
  * @example
2653
2645
  * ```ts
2654
- * onComplete={async (result, actions) => {
2646
+ * onComplete={async (result) => {
2655
2647
  * for (const source of result.sources) {
2656
2648
  * const inserts = source.rows.filter(r => r.isNew && !r.isDeleted && r.isValid);
2657
2649
  * const updates = source.rows.filter(r => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
2658
2650
  * const deletes = source.rows.filter(r => r.isDeleted && !r.isNew);
2659
2651
  * await persist(source.sourceId, { inserts, updates, deletes });
2660
2652
  * }
2661
- * actions.reset();
2662
2653
  * }}
2663
2654
  * ```
2664
2655
  */
@@ -2847,22 +2838,21 @@ type DataEditorBaseProps<TRow extends DataEditorRow = DataEditorRow> = {
2847
2838
  loadData?: (onChunk: (rows: TRow[], options?: ChunkSourceOptions) => void) => Promise<void>;
2848
2839
  /**
2849
2840
  * Called when the user clicks "Submit". Receives the edited data grouped
2850
- * by source. Use `actions.reset()` to clear changes after a successful save.
2841
+ * by source. May return a promise.
2851
2842
  *
2852
2843
  * @example
2853
2844
  * ```ts
2854
- * onComplete={async (result, actions) => {
2845
+ * onComplete={async (result) => {
2855
2846
  * for (const source of result.sources) {
2856
2847
  * const inserts = source.rows.filter(r => r.isNew && !r.isDeleted && r.isValid);
2857
2848
  * const updates = source.rows.filter(r => !r.isNew && r.isChanged && !r.isDeleted && r.isValid);
2858
2849
  * const deletes = source.rows.filter(r => r.isDeleted && !r.isNew);
2859
2850
  * await persist(source.sourceId, { inserts, updates, deletes });
2860
2851
  * }
2861
- * actions.reset();
2862
2852
  * }}
2863
2853
  * ```
2864
2854
  */
2865
- onComplete?: (result: DataEditorResult<TRow>, actions: DataEditorActions) => void;
2855
+ onComplete?: (result: DataEditorResult<TRow>) => void | Promise<void>;
2866
2856
  /**
2867
2857
  * Controls the initial view. `"editor"` opens directly to the spreadsheet
2868
2858
  * grid. `"uploader"` opens the file import wizard first — the user uploads
@@ -2930,7 +2920,7 @@ type DataEditorBaseProps<TRow extends DataEditorRow = DataEditorRow> = {
2930
2920
  */
2931
2921
  rtl?: boolean;
2932
2922
  /**
2933
- * Allow creating new columns for unmatched CSV headers during import. When
2923
+ * Allow creating new columns for unmatched headers during import. When
2934
2924
  * enabled, users can keep data from columns that don't match your schema by
2935
2925
  * creating dynamic columns on the fly.
2936
2926
  * @default true
@@ -3072,7 +3062,7 @@ type DataEditorInlineProps<TRow extends DataEditorRow = DataEditorRow> = DataEdi
3072
3062
  * columns={columns}
3073
3063
  * primaryKey="id"
3074
3064
  * loadData={async (onChunk) => onChunk(await fetchRows())}
3075
- * onComplete={(result, actions) => { saveChanges(result); actions.reset(); }}
3065
+ * onComplete={async (result) => { await saveChanges(result); }}
3076
3066
  * />
3077
3067
  *
3078
3068
  * // Inline mode
@@ -3082,7 +3072,7 @@ type DataEditorInlineProps<TRow extends DataEditorRow = DataEditorRow> = DataEdi
3082
3072
  * columns={columns}
3083
3073
  * primaryKey="id"
3084
3074
  * loadData={async (onChunk) => onChunk(await fetchRows())}
3085
- * onComplete={(result, actions) => { saveChanges(result); actions.reset(); }}
3075
+ * onComplete={async (result) => { await saveChanges(result); }}
3086
3076
  * />
3087
3077
  * ```
3088
3078
  */
@@ -3133,7 +3123,7 @@ declare function exportDataEditor<TRow extends DataEditorRow>(params: ExportPara
3133
3123
  * primaryKey="id"
3134
3124
  * server={{ url: scaleUrl }}
3135
3125
  * loadData={async (onChunk) => onChunk(await fetchRows())}
3136
- * onComplete={(result, actions) => { saveChanges(result); actions.reset(); }}
3126
+ * onComplete={async (result) => { await saveChanges(result); }}
3137
3127
  * />
3138
3128
  * ```
3139
3129
  */
package/index.js CHANGED
@@ -13017,49 +13017,53 @@ async function Lh(e) {
13017
13017
  }
13018
13018
  //#endregion
13019
13019
  //#region src/components/DataEditor/ConfirmCompleteDialog/index.tsx
13020
- var Rh = ({ open: e, onClose: t, onConfirm: n }) => {
13021
- let { store: r } = X(), { t: i } = q(), { newRowCount: a, dirtyRowCount: o, deletedRowCount: s, errorCount: c } = Mo(r), l = o - a, u = c > 0;
13020
+ var Rh = ({ open: e, onClose: t, onConfirm: n, loading: r = !1 }) => {
13021
+ let { store: i } = X(), { t: a } = q(), { newRowCount: o, dirtyRowCount: s, deletedRowCount: c, errorCount: l } = Mo(i), u = s - o, d = l > 0;
13022
13022
  return /* @__PURE__ */ w(Vi, {
13023
13023
  open: e,
13024
- onOpenChange: t,
13025
- title: i("dataEditor.confirmSubmit.title"),
13024
+ onOpenChange: f((e) => {
13025
+ r || e || t();
13026
+ }, [r, t]),
13027
+ title: a("dataEditor.confirmSubmit.title"),
13026
13028
  className: "updog__confirm-complete-dialog",
13027
13029
  children: [/* @__PURE__ */ w(zi, {
13028
13030
  className: "updog__confirm-complete-dialog__body",
13029
13031
  children: [
13030
- /* @__PURE__ */ C(J, { children: i("dataEditor.confirmSubmit.text") }),
13032
+ /* @__PURE__ */ C(J, { children: a("dataEditor.confirmSubmit.text") }),
13031
13033
  /* @__PURE__ */ w("ul", {
13032
13034
  className: "updog__confirm-complete-dialog__list",
13033
13035
  children: [
13034
- a > 0 && /* @__PURE__ */ C(J, {
13036
+ o > 0 && /* @__PURE__ */ C(J, {
13035
13037
  as: "li",
13036
- children: i("dataEditor.confirmSubmit.createRows", { count: a })
13038
+ children: a("dataEditor.confirmSubmit.createRows", { count: o })
13037
13039
  }),
13038
- l > 0 && /* @__PURE__ */ C(J, {
13040
+ u > 0 && /* @__PURE__ */ C(J, {
13039
13041
  as: "li",
13040
- children: i("dataEditor.confirmSubmit.updateRows", { count: l })
13042
+ children: a("dataEditor.confirmSubmit.updateRows", { count: u })
13041
13043
  }),
13042
- s > 0 && /* @__PURE__ */ C(J, {
13044
+ c > 0 && /* @__PURE__ */ C(J, {
13043
13045
  as: "li",
13044
- children: i("dataEditor.confirmSubmit.deleteRows", { count: s })
13046
+ children: a("dataEditor.confirmSubmit.deleteRows", { count: c })
13045
13047
  })
13046
13048
  ]
13047
13049
  }),
13048
- u && /* @__PURE__ */ C(ti, {
13050
+ d && /* @__PURE__ */ C(ti, {
13049
13051
  color: "yellow",
13050
- text: i("dataEditor.confirmSubmit.bannerTitle", { count: c })
13052
+ text: a("dataEditor.confirmSubmit.bannerTitle", { count: l })
13051
13053
  })
13052
13054
  ]
13053
13055
  }), /* @__PURE__ */ w(Bi, { children: [/* @__PURE__ */ C(Y, {
13054
13056
  variant: "outlined",
13055
13057
  color: "neutral",
13056
13058
  onClick: t,
13057
- children: i("dataEditor.common.cancel")
13059
+ disabled: r,
13060
+ children: a("dataEditor.common.cancel")
13058
13061
  }), /* @__PURE__ */ C(Y, {
13059
13062
  onClick: n,
13060
13063
  variant: "filled",
13061
13064
  color: "primary",
13062
- children: i("dataEditor.confirmSubmit.action")
13065
+ loading: r,
13066
+ children: a("dataEditor.confirmSubmit.action")
13063
13067
  })] })]
13064
13068
  });
13065
13069
  }, zh = () => {
@@ -13078,67 +13082,74 @@ var Rh = ({ open: e, onClose: t, onConfirm: n }) => {
13078
13082
  })
13079
13083
  });
13080
13084
  }, Bh = () => {
13081
- let { store: e, columns: t, onComplete: n, exportFormats: r, readonly: i, navigateToCell: a } = X(), { t: o, rtl: s } = q(), { canUndo: c, canRedo: l, undo: u, redo: d } = Vo(e, a), { dirtyRowCount: p, deletedRowCount: m, isLoading: h, sources: g, phase: _ } = Mo(e), y = p > 0 || m > 0, x = h || g.some((e) => e.isLoading), T = _ === "processing", [E, D] = b(!1), { value: O, setTrue: k, setFalse: A } = Ao(!1), j = f(() => {
13082
- n && (n(e.getResultBySource(), { reset: () => e.clear() }), A());
13085
+ let { store: e, columns: t, onComplete: n, exportFormats: r, readonly: i, navigateToCell: a } = X(), { t: o, rtl: s } = q(), { canUndo: c, canRedo: l, undo: u, redo: d } = Vo(e, a), { dirtyRowCount: p, deletedRowCount: m, isLoading: h, sources: g, phase: _ } = Mo(e), y = p > 0 || m > 0, x = h || g.some((e) => e.isLoading), T = _ === "processing", [E, D] = b(!1), { value: O, setTrue: k, setFalse: A } = Ao(!1), [j, M] = b(!1), N = f(async () => {
13086
+ if (!n) return;
13087
+ let t = e.getResultBySource();
13088
+ M(!0);
13089
+ try {
13090
+ await n(t), e.clear(), A();
13091
+ } catch {} finally {
13092
+ M(!1);
13093
+ }
13083
13094
  }, [
13084
13095
  e,
13085
13096
  n,
13086
13097
  A
13087
- ]), M = o("dataEditor.footer.export"), N = o("dataEditor.footer.exportAll"), P = o("dataEditor.footer.exportFiltered"), F = o("dataEditor.footer.csvFormat"), I = o("dataEditor.footer.excelFormat"), L = o("dataEditor.footer.jsonFormat"), z = o("dataEditor.footer.tsvFormat"), B = o("dataEditor.footer.xmlFormat"), V = v(() => [
13098
+ ]), P = o("dataEditor.footer.export"), F = o("dataEditor.footer.exportAll"), I = o("dataEditor.footer.exportFiltered"), L = o("dataEditor.footer.csvFormat"), z = o("dataEditor.footer.excelFormat"), B = o("dataEditor.footer.jsonFormat"), V = o("dataEditor.footer.tsvFormat"), H = o("dataEditor.footer.xmlFormat"), ee = v(() => [
13088
13099
  {
13089
13100
  menuId: "csv",
13090
13101
  format: "csv",
13091
- text: F
13102
+ text: L
13092
13103
  },
13093
13104
  {
13094
13105
  menuId: "tsv",
13095
13106
  format: "tsv",
13096
- text: z
13107
+ text: V
13097
13108
  },
13098
13109
  {
13099
13110
  menuId: "excel",
13100
13111
  format: "xlsx",
13101
- text: I
13112
+ text: z
13102
13113
  },
13103
13114
  {
13104
13115
  menuId: "json",
13105
13116
  format: "json",
13106
- text: L
13117
+ text: B
13107
13118
  },
13108
13119
  {
13109
13120
  menuId: "xml",
13110
13121
  format: "xml",
13111
- text: B
13122
+ text: H
13112
13123
  }
13113
13124
  ], [
13114
- F,
13115
- z,
13116
- I,
13117
13125
  L,
13118
- B
13119
- ]), H = r.length > 0 && (!e.isServer() || e.hasServerExport), ee = v(() => {
13120
- let e = V.filter((e) => r.includes(e.format));
13126
+ V,
13127
+ z,
13128
+ B,
13129
+ H
13130
+ ]), U = r.length > 0 && (!e.isServer() || e.hasServerExport), te = v(() => {
13131
+ let e = ee.filter((e) => r.includes(e.format));
13121
13132
  return [{
13122
13133
  id: "export-all",
13123
- text: N,
13134
+ text: F,
13124
13135
  options: e.map((e) => ({
13125
13136
  id: `all-${e.menuId}`,
13126
13137
  text: e.text
13127
13138
  }))
13128
13139
  }, {
13129
13140
  id: "export-filtered",
13130
- text: P,
13141
+ text: I,
13131
13142
  options: e.map((e) => ({
13132
13143
  id: `filtered-${e.menuId}`,
13133
13144
  text: e.text
13134
13145
  }))
13135
13146
  }];
13136
13147
  }, [
13137
- N,
13138
- P,
13139
- V,
13148
+ F,
13149
+ I,
13150
+ ee,
13140
13151
  r
13141
- ]), U = f((n) => {
13152
+ ]), ne = f((n) => {
13142
13153
  let [r, i] = n.split("-"), a = {
13143
13154
  csv: "csv",
13144
13155
  tsv: "tsv",
@@ -13157,7 +13168,7 @@ var Rh = ({ open: e, onClose: t, onConfirm: n }) => {
13157
13168
  e,
13158
13169
  t,
13159
13170
  s
13160
- ]), te = o("dataEditor.footer.submit");
13171
+ ]), re = o("dataEditor.footer.submit");
13161
13172
  return /* @__PURE__ */ w(S, { children: [/* @__PURE__ */ w("footer", {
13162
13173
  className: "updog__data-editor-footer",
13163
13174
  children: [/* @__PURE__ */ C(zh, {}), /* @__PURE__ */ w("div", {
@@ -13184,9 +13195,9 @@ var Rh = ({ open: e, onClose: t, onConfirm: n }) => {
13184
13195
  children: /* @__PURE__ */ C(Ce, { className: "rtl-mirror" })
13185
13196
  })
13186
13197
  })] }),
13187
- H && /* @__PURE__ */ C(Ma, {
13188
- options: ee,
13189
- onSelect: U,
13198
+ U && /* @__PURE__ */ C(Ma, {
13199
+ options: te,
13200
+ onSelect: ne,
13190
13201
  placement: "top-end",
13191
13202
  children: /* @__PURE__ */ C(Y, {
13192
13203
  variant: "outlined",
@@ -13195,20 +13206,21 @@ var Rh = ({ open: e, onClose: t, onConfirm: n }) => {
13195
13206
  disabled: x || T,
13196
13207
  spinnerPosition: "end",
13197
13208
  endIcon: /* @__PURE__ */ C(R, {}),
13198
- children: M
13209
+ children: P
13199
13210
  })
13200
13211
  }),
13201
13212
  !i && /* @__PURE__ */ C(Y, {
13202
- disabled: !y || T,
13213
+ disabled: !y || T || j,
13203
13214
  onClick: k,
13204
- children: te
13215
+ children: re
13205
13216
  })
13206
13217
  ]
13207
13218
  })]
13208
13219
  }), /* @__PURE__ */ C(Rh, {
13209
13220
  open: O,
13210
13221
  onClose: A,
13211
- onConfirm: j
13222
+ onConfirm: N,
13223
+ loading: j
13212
13224
  })] });
13213
13225
  }, Vh = ({ initialValues: e, existingNames: t, onClose: n, onSubmit: r }) => {
13214
13226
  let { t: i } = q(), a = e.id !== void 0, o = v(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@updog/data-editor",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Client-side CSV importer and spreadsheet editor SDK for React. Import CSV, Excel, JSON, TSV, and XML, match columns, validate, and edit 1M+ rows entirely in the browser.",
5
5
  "author": "Mikhail Kutateladze <admin@updog.tech>",
6
6
  "homepage": "https://updog.tech",