@updog/data-editor 0.1.82 → 0.1.83

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
@@ -204,7 +204,7 @@ const columns: DataEditorColumn[] = [
204
204
  ];
205
205
  ```
206
206
 
207
- **Cell editors**: `{ type: "text" }` (default), `{ type: "date", minDate?, maxDate? }`, `{ type: "select", options: string[] }`, `{ type: "number", decimalPlaces?, decimalSeparator?, thousandsSeparator?, allowChars? }`.
207
+ **Cell editors**: `{ type: "text" }` (default), `{ type: "date" }`, `{ type: "select", options: string[] }`, `{ type: "number", decimalSeparator?, thousandsSeparator? }`.
208
208
 
209
209
  **Column filters**: `{ type: "select", label?, placeholder?, options?, multiple? }`, `{ type: "number-range", label? }`, `{ type: "date-range", label? }`.
210
210
 
@@ -216,11 +216,10 @@ Validators are declarative objects passed in the `validators` array on each colu
216
216
  |---|---|---|
217
217
  | `"required"` | `message?` | Value must be non-empty. |
218
218
  | `"email"` | `message?` | Value must look like an email address. |
219
- | `"numeric"` | `message?` | Value must parse as a number. Commas are stripped before parsing. |
220
219
  | `"regex"` | `pattern`, `flags?`, `message?` | Value must match the regular expression. Empty values are skipped. |
221
- | `"range"` | `min?`, `max?`, `message?` | Value must be numerically within `[min, max]`. Either bound is optional. |
220
+ | `"number"` | `min?`, `max?`, `decimalPlaces?`, `message?` | Value must be the canonical form — plain digits, a dot for decimals, no grouping, no leading zeros. `min` and `max` are inclusive; `decimalPlaces` caps the decimal digits (`0` means integers) and flags rather than rounds. `min` and `decimalPlaces` also configure the column's number editor. |
222
221
  | `"oneOf"` | `values: string[]`, `message?` | Value must be one of the listed strings. |
223
- | `"date"` | `format?: "YYYY-MM-DD" \| "DD/MM/YYYY"`, `message?` | Value must parse as a date in the given format. When `format` is omitted, either format is accepted. |
222
+ | `"date"` | `min?`, `max?`, `message?` | Value must be an ISO `YYYY-MM-DD` date that exists in the calendar. `min` and `max` are inclusive ISO bounds, and they also set the range the cell's date picker offers. |
224
223
  | `"unique"` | `message?` | Value must not appear in any other row of the column. Always checked last, after all other validators pass — list position is ignored. |
225
224
 
226
225
  ```tsx
@@ -234,7 +233,7 @@ const columns: DataEditorColumn[] = [
234
233
  { type: "email", message: "Invalid email" },
235
234
  ],
236
235
  },
237
- { id: "salary", title: "Salary", validators: [{ type: "numeric", message: "Must be a number" }] },
236
+ { id: "salary", title: "Salary", validators: [{ type: "number", min: 0, decimalPlaces: 2 }] },
238
237
  {
239
238
  id: "status",
240
239
  title: "Status",
@@ -243,7 +242,7 @@ const columns: DataEditorColumn[] = [
243
242
  {
244
243
  id: "startDate",
245
244
  title: "Start",
246
- validators: [{ type: "date", format: "YYYY-MM-DD", message: "Use YYYY-MM-DD" }],
245
+ validators: [{ type: "date", message: "Use a valid date" }],
247
246
  editor: { type: "date" },
248
247
  dependentFields: ["endDate"],
249
248
  },
package/index.d.ts CHANGED
@@ -417,6 +417,7 @@ declare var export_default = {
417
417
  outOfRange: "Out of range",
418
418
  required: "This field is required",
419
419
  structuralMismatch: "This might be in the wrong column.",
420
+ tooManyDecimals: "Too many decimal places",
420
421
  valueMustBeUnique: "Value must be unique",
421
422
  },
422
423
  license: {
@@ -694,13 +695,9 @@ type CellValidator = (value: unknown, row: DataEditorRow) => ValidationError | n
694
695
  type TextEditorCell = {
695
696
  type: "text";
696
697
  };
697
- /** Date picker cell. Optionally restrict the selectable date range. */
698
+ /** Date picker cell. Bounds come from the column's `{ type: "date" }` validator. */
698
699
  type DateEditorCell = {
699
700
  type: "date";
700
- /** Earliest selectable date. */
701
- minDate?: Date;
702
- /** Latest selectable date. */
703
- maxDate?: Date;
704
701
  };
705
702
  /** Dropdown select cell. The user picks from a fixed list of options. */
706
703
  type SelectEditorCell = {
@@ -723,23 +720,19 @@ type MultiSelectEditorCell = {
723
720
  /** Splits a raw imported cell and joins on export. Omitted: import auto-detects, export joins with `", "`. */
724
721
  delimiter?: string;
725
722
  };
726
- /** Number input cell with locale-aware formatting. */
723
+ /** Number input cell with locale-aware formatting. Bounds and decimal digits come from the column's `{ type: "number" }` validator. */
727
724
  type NumberEditorCell = {
728
725
  type: "number";
729
- /** Maximum number of decimal digits allowed. When omitted, decimals are unrestricted. */
730
- decimalPlaces?: number;
731
726
  /** Character used as the decimal point (e.g. `"."` or `","`). Defaults to the browser locale. */
732
727
  decimalSeparator?: string;
733
728
  /** Character inserted between groups of three digits (e.g. `","` or `"."`). Defaults to the browser locale. */
734
729
  thousandsSeparator?: string;
735
- /** Extra characters to allow beyond digits, decimal separator, and minus sign (e.g. `"%-"`). When defined, minus is only kept if explicitly included. */
736
- allowChars?: string;
737
730
  };
738
731
  /**
739
732
  * Controls how a cell is edited.
740
733
  *
741
734
  * - `"text"` — plain text input (default).
742
- * - `"date"` — date picker with optional min/max bounds.
735
+ * - `"date"` — date picker; the calendar honours the column's date validator bounds.
743
736
  * - `"select"` — dropdown with a fixed list of options.
744
737
  * - `"multiselect"` — dropdown allowing zero or more options; stored as `string[]`.
745
738
  * - `"number"` — number input with locale-aware formatting.
@@ -788,7 +781,7 @@ type ColumnLockMode = "all" | "default";
788
781
  * { id: "name", title: "Full Name", size: 200, validators: [{ type: "required", message: "Name is required" }] },
789
782
  * { id: "email", title: "Email", size: 250, validators: [{ type: "required", message: "Email is required" }, { type: "email", message: "Invalid email" }, { type: "unique" }] },
790
783
  * { id: "role", title: "Role", editor: { type: "select", options: ["Admin", "Editor", "Viewer"] } },
791
- * { id: "salary", title: "Salary", validators: [{ type: "numeric", message: "Must be a number" }], formatter: (v) => v ? `$${v}` : "" },
784
+ * { id: "salary", title: "Salary", validators: [{ type: "number", message: "Must be a number" }], formatter: (v) => v ? `$${v}` : "" },
792
785
  * ];
793
786
  * ```
794
787
  */
@@ -799,7 +792,7 @@ type DataEditorColumn = {
799
792
  title: string;
800
793
  /**
801
794
  * One or more validators run on every edit. Accepts:
802
- * - Built-in object literals: `{ type: "required" | "email" | "regex" | "range" | "oneOf" | "date" | "numeric" | "unique", ... }`.
795
+ * - Built-in object literals: `{ type: "required" | "email" | "regex" | "number" | "oneOf" | "date" | "unique", ... }`.
803
796
  * - `{ type: "function", fn }` — a JS predicate run inline.
804
797
  * - `{ type: "asyncFunction", fn }` — batched remote checks.
805
798
  */
@@ -839,8 +832,7 @@ type DataEditorColumn = {
839
832
  };
840
833
 
841
834
  /**
842
- * A built-in declarative validator — a declarative object the SDK interprets,
843
- * per `api/validators.json`.
835
+ * A built-in declarative validator — a declarative object the SDK interprets.
844
836
  */
845
837
  type BuiltInValidator = {
846
838
  type: "required";
@@ -855,19 +847,21 @@ type BuiltInValidator = {
855
847
  values: string[];
856
848
  message?: string;
857
849
  } | {
858
- type: "range";
850
+ type: "number";
851
+ /** Inclusive lower bound. */
859
852
  min?: number;
853
+ /** Inclusive upper bound. */
860
854
  max?: number;
855
+ /** Maximum decimal digits; `0` means integers. Values with more are flagged, never rounded. */
856
+ decimalPlaces?: number;
861
857
  message?: string;
862
858
  } | {
863
859
  type: "email";
864
860
  message?: string;
865
861
  } | {
866
862
  type: "date";
867
- format?: "YYYY-MM-DD" | "DD/MM/YYYY";
868
- message?: string;
869
- } | {
870
- type: "numeric";
863
+ min?: string;
864
+ max?: string;
871
865
  message?: string;
872
866
  } | {
873
867
  type: "unique";
@@ -1233,14 +1227,26 @@ type UniqueConfig = {
1233
1227
  fn?: UniqueRemoteFn;
1234
1228
  };
1235
1229
  /**
1236
- * Core-internal column shape produced by normalizeUniqueColumns. Uniqueness
1230
+ * Core-internal column shape produced by normalizeColumns. Uniqueness
1237
1231
  * lives in `uniqueConfig` and asyncFunction rules in `asyncConfig` — the
1238
1232
  * single internal sources of truth — never as entries in `validators`.
1239
1233
  * Never exported from the package entry.
1240
1234
  */
1241
- type NormalizedColumn = DataEditorColumn & {
1235
+ type NormalizedColumn = Omit<DataEditorColumn, "validators"> & {
1236
+ validators?: ValidatorRule[];
1242
1237
  uniqueConfig?: UniqueConfig;
1243
1238
  asyncConfig?: AsyncFunctionValidator[];
1239
+ numberConfig?: NumberConfig;
1240
+ dateConfig?: DateConfig;
1241
+ };
1242
+ type NumberConfig = {
1243
+ min?: number;
1244
+ max?: number;
1245
+ decimalPlaces?: number;
1246
+ };
1247
+ type DateConfig = {
1248
+ min?: string;
1249
+ max?: string;
1244
1250
  };
1245
1251
 
1246
1252
  type PrimaryKeyInput = string | readonly string[];
@@ -1992,6 +1998,14 @@ declare class DataStore<TRow extends DataEditorRow = DataEditorRow> {
1992
1998
  getRealRowPosition(rowId: TRowId): number;
1993
1999
  insertRow(sourceId: DataSourceId, row: TRow, position: number): Promise<TRowId>;
1994
2000
  insertRowDirect(rowId: TRowId, row: TRow, sourceId: DataSourceId, position: number): void;
2001
+ /**
2002
+ * Append rows supplied by the client through `loadData`. Date columns are
2003
+ * canonicalised to ISO, number columns to the canonical stored form, and
2004
+ * `column.transformer` runs, matching what a file import does. `appendRows` stays a plain append, so the import path — which
2005
+ * has already normalised through buildImportRow — never transforms twice.
2006
+ */
2007
+ appendClientRows(sourceId: DataSourceId, newRows: TRow[]): TRowId[];
2008
+ private normalizeClientRows;
1995
2009
  appendRows(sourceId: DataSourceId, newRows: TRow[], rowIdMap?: Map<number, TRowId>): TRowId[];
1996
2010
  seedInitialChanges(assignedRowIds: TRowId[], currentRows: TRow[], changes: InitialRowChange<TRow>[]): void;
1997
2011
  upsertRows(sourceId: DataSourceId, newRows: TRow[], options?: UpsertOptions): UpsertResult<TRow>;