@ai-matrx/records-ui 0.90.1 → 0.92.0

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/dist/index.d.cts CHANGED
@@ -1192,6 +1192,10 @@ interface PersonPickerProps {
1192
1192
  disabled?: boolean | undefined;
1193
1193
  id?: string | undefined;
1194
1194
  className?: string | undefined;
1195
+ /** A grid cell's picker opens at once (`FieldEditorControlProps.autoOpen`). */
1196
+ autoOpen?: boolean | undefined;
1197
+ /** The list closed: the cell commits who is picked. */
1198
+ onClose?: (() => void) | undefined;
1195
1199
  }
1196
1200
  /** A Person record in the kernel Table, as this picker needs to read it. */
1197
1201
  interface PersonRecord {
@@ -1213,7 +1217,7 @@ declare function personRecordForMember(client: RecordsClient, personTableId: Uui
1213
1217
  ok: false;
1214
1218
  error: RecordsError;
1215
1219
  }>;
1216
- declare function PersonPicker({ field, value, onChange, disabled, id, className }: PersonPickerProps): react.JSX.Element;
1220
+ declare function PersonPicker({ field, value, onChange, disabled, id, className, autoOpen, onClose }: PersonPickerProps): react.JSX.Element;
1217
1221
 
1218
1222
  /** Which cell is open. A grid opens exactly one at a time, like every other grid. */
1219
1223
  interface CellAddress {
@@ -1246,14 +1250,16 @@ interface GridEditing {
1246
1250
  * exactly that, replacing the value, as every spreadsheet does; a picker column opens as usual.
1247
1251
  */
1248
1252
  begin: (address: CellAddress, seed?: string | null) => void;
1249
- cancel: () => void;
1253
+ /** `via`: a key (Escape) or a pointer (the picker's own close) — how the grid gets its keys back. */
1254
+ cancel: (via?: EditVia) => void;
1250
1255
  /**
1251
1256
  * Commit the open cell — whatever is in `draft`. `move` opens the
1252
1257
  * next/previous editable cell after it. It takes NO value: a caller that
1253
1258
  * passed one would be a second copy of the typed value, which is the defect
1254
1259
  * this file's header describes.
1255
1260
  */
1256
- commit: (move?: "next" | "previous" | null) => void;
1261
+ /** Answers `false` when the cell stayed open: typed words that are not a value (editors.tsx). */
1262
+ commit: (move?: "next" | "previous" | null, via?: EditVia) => boolean | void;
1257
1263
  /**
1258
1264
  * WRITE ONE CELL OUTRIGHT, with no editor ever opening.
1259
1265
  *
@@ -1330,8 +1336,18 @@ interface Options {
1330
1336
  before: unknown;
1331
1337
  after: unknown;
1332
1338
  }) => void) | undefined;
1339
+ /**
1340
+ * THE GRID GETS ITS KEYS BACK (merged-grid review 2026-09-26, A1). Bound, an editor that closes
1341
+ * by a key or a pick says so — Enter `down`, Tab `right` (Shift-Tab `left`), Escape and a pick
1342
+ * `stay` — and Tab no longer opens the next cell's editor: it commits and the CURSOR moves right,
1343
+ * as in Sheets and Airtable. A blur (a click elsewhere) says nothing: the click has the focus.
1344
+ * Unbound (the classic grid), Tab chains editors as before.
1345
+ */
1346
+ onEnded?: ((exit: "down" | "right" | "left" | "stay", from: CellAddress, how: "keyboard" | "pointer") => void) | undefined;
1333
1347
  }
1334
- declare function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow, onWritten }: Options): GridEditing;
1348
+ /** How an open cell was closed: a key (Enter, Tab, Escape), the picker's own close, or a blur. */
1349
+ type EditVia = "key" | "pick" | "blur";
1350
+ declare function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow, onWritten, onEnded }: Options): GridEditing;
1335
1351
  /**
1336
1352
  * ONE CELL. Read, until somebody asks to change it; then the Field's own
1337
1353
  * editor, in place, with the keys a person expects from a grid.
@@ -1805,8 +1821,14 @@ interface GridProps {
1805
1821
  * the store compiles both into the one WHERE clause.
1806
1822
  */
1807
1823
  filter?: StoreFilter$1 | undefined;
1808
- /** Opened when a row is clicked. A host that has a record page passes its own. */
1824
+ /** Opened from a row's handle, Space or the row menu. A host that has a record page passes its own. */
1809
1825
  onOpenRecord?: ((recordId: Uuid) => void) | undefined;
1826
+ /**
1827
+ * THE RECORD THE HOST HAS OPEN BESIDE THE GRID, if any (merged grid). While one is open the panel
1828
+ * FOLLOWS the cursor: arrowing to another row opens that row (Airtable's expanded record walks
1829
+ * with the arrows), so the panel always shows the row the person is on.
1830
+ */
1831
+ openRecordId?: Uuid | null | undefined;
1810
1832
  /** SCR-2. Called when an admin clicks the "+" at the end of the headers. */
1811
1833
  onAddField?: (() => void) | undefined;
1812
1834
  /**
@@ -1867,7 +1889,7 @@ interface GridProps {
1867
1889
  footer?: FooterPlacement | undefined;
1868
1890
  className?: string | undefined;
1869
1891
  }
1870
- declare function Grid({ tableId, pageSize, filter, onOpenRecord, onAddField, onNewRecordForm, toolbarActions, editable, presentation, onPresentationChange, viewOrder, footer, className, onAskWhoChanged, }: GridProps): react.JSX.Element;
1892
+ declare function Grid({ tableId, pageSize, filter, onOpenRecord, openRecordId, onAddField, onNewRecordForm, toolbarActions, editable, presentation, onPresentationChange, viewOrder, footer, className, onAskWhoChanged, }: GridProps): react.JSX.Element;
1871
1893
 
1872
1894
  /** Answers "what is the record with this id called", for one Field's values. */
1873
1895
  type LabelLookup = (field: Field$1, id: string) => string | null;
@@ -2354,6 +2376,14 @@ interface FieldEditorControlProps {
2354
2376
  onChange: (value: unknown) => void;
2355
2377
  disabled?: boolean | undefined;
2356
2378
  id?: string | undefined;
2379
+ /**
2380
+ * A GRID CELL'S PICKER OPENS AT ONCE (merged-grid review 2026-09-26, A2). One click on a selected
2381
+ * choice, multi-choice or person cell opens its list directly — no inline Save/Cancel form that
2382
+ * grows the row. Honoured by the choice and person pickers; every other control ignores it.
2383
+ */
2384
+ autoOpen?: boolean | undefined;
2385
+ /** The picker's list closed (a pick, Escape, a click away): the cell commits what is chosen. */
2386
+ onClose?: (() => void) | undefined;
2357
2387
  }
2358
2388
  /** The label row every editor shares: one line, the label, the unit, the required mark. */
2359
2389
  declare function FieldLabel({ field, htmlFor, children }: {
@@ -2361,7 +2391,7 @@ declare function FieldLabel({ field, htmlFor, children }: {
2361
2391
  htmlFor: string;
2362
2392
  children?: ReactNode;
2363
2393
  }): react.JSX.Element;
2364
- declare function FieldControl({ field, value, onChange, disabled, id }: FieldEditorControlProps): string | number | bigint | true | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react.JSX.Element;
2394
+ declare function FieldControl({ field, value, onChange, disabled, id, autoOpen, onClose }: FieldEditorControlProps): string | number | bigint | true | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react.JSX.Element;
2365
2395
  /** The chip a picked record shows as — REC-2's "a record is a chip" in one place. */
2366
2396
  declare function RecordChip({ title, onRemove, className, }: {
2367
2397
  title: string;
@@ -2711,6 +2741,8 @@ interface ViewSwitcherProps {
2711
2741
  */
2712
2742
  onViewChange?: ((patch: Partial<SavedViewSpec>) => void) | undefined;
2713
2743
  onOpenRecord?: ((recordId: Uuid) => void) | undefined;
2744
+ /** The record the host has open beside a grid, which the grid's panel then follows (`Grid.openRecordId`). */
2745
+ openRecordId?: Uuid | null | undefined;
2714
2746
  /**
2715
2747
  * Called after a card moves between columns, so the host re-reads the rows it owns. A
2716
2748
  * board that moved a card and left the row list alone would show the card in two places
@@ -2794,7 +2826,7 @@ declare function useViewRecords(view: SavedViewSpec, pageSize?: number, filter?:
2794
2826
  */
2795
2827
  membership: string;
2796
2828
  };
2797
- declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize, titleField, filter, hostLayout, onHostLayoutChange, chooser, openExport, viewId, onOrderPlaced, footer, className, }: ViewSwitcherProps): react.JSX.Element;
2829
+ declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, openRecordId, onMoved, onNewRecordForm, onAskWhoChanged, pageSize, titleField, filter, hostLayout, onHostLayoutChange, chooser, openExport, viewId, onOrderPlaced, footer, className, }: ViewSwitcherProps): react.JSX.Element;
2798
2830
 
2799
2831
  /** What the screen says above the list, so nobody has to be told twice that nothing was destroyed. */
2800
2832
  declare const ARCHIVE_MEANS = "Archiving never destroys anything. A record you remove from the grid waits here until somebody brings it back.";
package/dist/index.d.ts CHANGED
@@ -1192,6 +1192,10 @@ interface PersonPickerProps {
1192
1192
  disabled?: boolean | undefined;
1193
1193
  id?: string | undefined;
1194
1194
  className?: string | undefined;
1195
+ /** A grid cell's picker opens at once (`FieldEditorControlProps.autoOpen`). */
1196
+ autoOpen?: boolean | undefined;
1197
+ /** The list closed: the cell commits who is picked. */
1198
+ onClose?: (() => void) | undefined;
1195
1199
  }
1196
1200
  /** A Person record in the kernel Table, as this picker needs to read it. */
1197
1201
  interface PersonRecord {
@@ -1213,7 +1217,7 @@ declare function personRecordForMember(client: RecordsClient, personTableId: Uui
1213
1217
  ok: false;
1214
1218
  error: RecordsError;
1215
1219
  }>;
1216
- declare function PersonPicker({ field, value, onChange, disabled, id, className }: PersonPickerProps): react.JSX.Element;
1220
+ declare function PersonPicker({ field, value, onChange, disabled, id, className, autoOpen, onClose }: PersonPickerProps): react.JSX.Element;
1217
1221
 
1218
1222
  /** Which cell is open. A grid opens exactly one at a time, like every other grid. */
1219
1223
  interface CellAddress {
@@ -1246,14 +1250,16 @@ interface GridEditing {
1246
1250
  * exactly that, replacing the value, as every spreadsheet does; a picker column opens as usual.
1247
1251
  */
1248
1252
  begin: (address: CellAddress, seed?: string | null) => void;
1249
- cancel: () => void;
1253
+ /** `via`: a key (Escape) or a pointer (the picker's own close) — how the grid gets its keys back. */
1254
+ cancel: (via?: EditVia) => void;
1250
1255
  /**
1251
1256
  * Commit the open cell — whatever is in `draft`. `move` opens the
1252
1257
  * next/previous editable cell after it. It takes NO value: a caller that
1253
1258
  * passed one would be a second copy of the typed value, which is the defect
1254
1259
  * this file's header describes.
1255
1260
  */
1256
- commit: (move?: "next" | "previous" | null) => void;
1261
+ /** Answers `false` when the cell stayed open: typed words that are not a value (editors.tsx). */
1262
+ commit: (move?: "next" | "previous" | null, via?: EditVia) => boolean | void;
1257
1263
  /**
1258
1264
  * WRITE ONE CELL OUTRIGHT, with no editor ever opening.
1259
1265
  *
@@ -1330,8 +1336,18 @@ interface Options {
1330
1336
  before: unknown;
1331
1337
  after: unknown;
1332
1338
  }) => void) | undefined;
1339
+ /**
1340
+ * THE GRID GETS ITS KEYS BACK (merged-grid review 2026-09-26, A1). Bound, an editor that closes
1341
+ * by a key or a pick says so — Enter `down`, Tab `right` (Shift-Tab `left`), Escape and a pick
1342
+ * `stay` — and Tab no longer opens the next cell's editor: it commits and the CURSOR moves right,
1343
+ * as in Sheets and Airtable. A blur (a click elsewhere) says nothing: the click has the focus.
1344
+ * Unbound (the classic grid), Tab chains editors as before.
1345
+ */
1346
+ onEnded?: ((exit: "down" | "right" | "left" | "stay", from: CellAddress, how: "keyboard" | "pointer") => void) | undefined;
1333
1347
  }
1334
- declare function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow, onWritten }: Options): GridEditing;
1348
+ /** How an open cell was closed: a key (Enter, Tab, Escape), the picker's own close, or a blur. */
1349
+ type EditVia = "key" | "pick" | "blur";
1350
+ declare function useGridEditing({ tableId, fields, rows, reload, canWrite, mayWriteRow, onWritten, onEnded }: Options): GridEditing;
1335
1351
  /**
1336
1352
  * ONE CELL. Read, until somebody asks to change it; then the Field's own
1337
1353
  * editor, in place, with the keys a person expects from a grid.
@@ -1805,8 +1821,14 @@ interface GridProps {
1805
1821
  * the store compiles both into the one WHERE clause.
1806
1822
  */
1807
1823
  filter?: StoreFilter$1 | undefined;
1808
- /** Opened when a row is clicked. A host that has a record page passes its own. */
1824
+ /** Opened from a row's handle, Space or the row menu. A host that has a record page passes its own. */
1809
1825
  onOpenRecord?: ((recordId: Uuid) => void) | undefined;
1826
+ /**
1827
+ * THE RECORD THE HOST HAS OPEN BESIDE THE GRID, if any (merged grid). While one is open the panel
1828
+ * FOLLOWS the cursor: arrowing to another row opens that row (Airtable's expanded record walks
1829
+ * with the arrows), so the panel always shows the row the person is on.
1830
+ */
1831
+ openRecordId?: Uuid | null | undefined;
1810
1832
  /** SCR-2. Called when an admin clicks the "+" at the end of the headers. */
1811
1833
  onAddField?: (() => void) | undefined;
1812
1834
  /**
@@ -1867,7 +1889,7 @@ interface GridProps {
1867
1889
  footer?: FooterPlacement | undefined;
1868
1890
  className?: string | undefined;
1869
1891
  }
1870
- declare function Grid({ tableId, pageSize, filter, onOpenRecord, onAddField, onNewRecordForm, toolbarActions, editable, presentation, onPresentationChange, viewOrder, footer, className, onAskWhoChanged, }: GridProps): react.JSX.Element;
1892
+ declare function Grid({ tableId, pageSize, filter, onOpenRecord, openRecordId, onAddField, onNewRecordForm, toolbarActions, editable, presentation, onPresentationChange, viewOrder, footer, className, onAskWhoChanged, }: GridProps): react.JSX.Element;
1871
1893
 
1872
1894
  /** Answers "what is the record with this id called", for one Field's values. */
1873
1895
  type LabelLookup = (field: Field$1, id: string) => string | null;
@@ -2354,6 +2376,14 @@ interface FieldEditorControlProps {
2354
2376
  onChange: (value: unknown) => void;
2355
2377
  disabled?: boolean | undefined;
2356
2378
  id?: string | undefined;
2379
+ /**
2380
+ * A GRID CELL'S PICKER OPENS AT ONCE (merged-grid review 2026-09-26, A2). One click on a selected
2381
+ * choice, multi-choice or person cell opens its list directly — no inline Save/Cancel form that
2382
+ * grows the row. Honoured by the choice and person pickers; every other control ignores it.
2383
+ */
2384
+ autoOpen?: boolean | undefined;
2385
+ /** The picker's list closed (a pick, Escape, a click away): the cell commits what is chosen. */
2386
+ onClose?: (() => void) | undefined;
2357
2387
  }
2358
2388
  /** The label row every editor shares: one line, the label, the unit, the required mark. */
2359
2389
  declare function FieldLabel({ field, htmlFor, children }: {
@@ -2361,7 +2391,7 @@ declare function FieldLabel({ field, htmlFor, children }: {
2361
2391
  htmlFor: string;
2362
2392
  children?: ReactNode;
2363
2393
  }): react.JSX.Element;
2364
- declare function FieldControl({ field, value, onChange, disabled, id }: FieldEditorControlProps): string | number | bigint | true | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react.JSX.Element;
2394
+ declare function FieldControl({ field, value, onChange, disabled, id, autoOpen, onClose }: FieldEditorControlProps): string | number | bigint | true | Iterable<ReactNode> | Promise<string | number | bigint | boolean | react.ReactPortal | react.ReactElement<unknown, string | react.JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | react.JSX.Element;
2365
2395
  /** The chip a picked record shows as — REC-2's "a record is a chip" in one place. */
2366
2396
  declare function RecordChip({ title, onRemove, className, }: {
2367
2397
  title: string;
@@ -2711,6 +2741,8 @@ interface ViewSwitcherProps {
2711
2741
  */
2712
2742
  onViewChange?: ((patch: Partial<SavedViewSpec>) => void) | undefined;
2713
2743
  onOpenRecord?: ((recordId: Uuid) => void) | undefined;
2744
+ /** The record the host has open beside a grid, which the grid's panel then follows (`Grid.openRecordId`). */
2745
+ openRecordId?: Uuid | null | undefined;
2714
2746
  /**
2715
2747
  * Called after a card moves between columns, so the host re-reads the rows it owns. A
2716
2748
  * board that moved a card and left the row list alone would show the card in two places
@@ -2794,7 +2826,7 @@ declare function useViewRecords(view: SavedViewSpec, pageSize?: number, filter?:
2794
2826
  */
2795
2827
  membership: string;
2796
2828
  };
2797
- declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, onMoved, onNewRecordForm, onAskWhoChanged, pageSize, titleField, filter, hostLayout, onHostLayoutChange, chooser, openExport, viewId, onOrderPlaced, footer, className, }: ViewSwitcherProps): react.JSX.Element;
2829
+ declare function ViewSwitcher({ view, onLayoutChange, onViewChange, onOpenRecord, openRecordId, onMoved, onNewRecordForm, onAskWhoChanged, pageSize, titleField, filter, hostLayout, onHostLayoutChange, chooser, openExport, viewId, onOrderPlaced, footer, className, }: ViewSwitcherProps): react.JSX.Element;
2798
2830
 
2799
2831
  /** What the screen says above the list, so nobody has to be told twice that nothing was destroyed. */
2800
2832
  declare const ARCHIVE_MEANS = "Archiving never destroys anything. A record you remove from the grid waits here until somebody brings it back.";