@nxgt/material 3.42.0 → 3.43.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.
@@ -17,7 +17,7 @@ either can be used alone.
17
17
  | <img src="../previews/data-table-filter-lists-servertable.webp" width="96" alt=""> | [ServerTable](#servertable) | A server-mode `DataTable` driven by the URL, with the Filter in its top band (a title and a description, or neither): sort, cursor paging, the empty page with a scene, an action and removable chips, one count announced |
18
18
  | <img src="../previews/data-table-filter-lists-rowmenu.webp" width="96" alt=""> | [RowMenu](#rowmenu) | A row's "..." button named after its row ("Actions for Ada"), its menu aligned to the end with the destructive item last, and `rowMenuColumn` to drop both in a table |
19
19
  | <img src="../previews/data-table-filter-lists-listempty.webp" width="96" alt=""> | [ListEmpty](#listempty) | A list's empty page: nothing yet, with a scene and an action, or nothing matches and the way out |
20
- | <img src="../previews/data-table-filter-lists-pagefooter.webp" width="96" alt=""> | [PageFooter](#pagefooter) | The page footer of a feed that is not a table (cards, a news feed) under a FilterBar: the range and Previous / Next as links from the URL's cursor trail, `pageControls="icon"` |
20
+ | <img src="../previews/data-table-filter-lists-pagefooter.webp" width="96" alt=""> | [PageFooter](#pagefooter) | The page footer of a feed that is not a table (cards, a news feed) under a FilterBar: the range and Previous / Next as links from the URL's cursor trail, `pageControls="icon"`; no `totalCount` for a feed with no total (no range, Previous / Next only) |
21
21
  | <img src="../previews/data-table-filter-lists-datagrid.webp" width="96" alt=""> | [DataGrid](#datagrid) | An editable grid: spreadsheet keys, paste, a fill handle with series, undo, totals, saving with versions and conflicts (lazy ones fetch theirs on "Keep mine"), `onRefuse` for a refused paste or fill, row selection (`enableRowSelection`), and resizable, hideable and reorderable columns (`columnControls`, `columnLayout`, `DataGridColumnsMenu`) |
22
22
  | <img src="../previews/data-table-filter-lists-daygroupedlist.webp" width="96" alt=""> | [DayGroupedList](#daygroupedlist) | A list split by day under sticky Today / Yesterday / Earlier headings |
23
23
  | <img src="../previews/data-table-filter-lists-filter.webp" width="96" alt=""> | [Filter](#filter) | Schema-driven filters with chips, presets and persistence |
@@ -898,13 +898,24 @@ export default function Students() {
898
898
  Under `max` again, the button returns. On a phone an action in the bar
899
899
  shows the notice inline; one in the More menu is a disabled item whose
900
900
  notice is its description (`aria-describedby`), so it reads under its label.
901
- - `onClick(ids, selection, rows, { trigger, focusTrigger })`: `trigger` is the
902
- button the action started from (the "More" button for a More-menu item),
903
- and `focusTrigger()` gives it the focus back, found again at call time, so
904
- it works after a re-render. When the action is gone by then (its count fell
905
- to 0, or past `max`) the focus goes to the next bulk action, else "More",
906
- else the table's "Select all" (the table itself without one), never the
907
- body. A handler that takes three parameters stays valid.
901
+ - `onClick(ids, selection, rows, { actionId, trigger, element, focusTrigger })`:
902
+ `focusTrigger()` gives the focus back to the action, found again in the
903
+ DOM when it is called by the stable key every bulk button carries,
904
+ `data-bulk-action="<id>"` (the More button carries `data-bulk-more`). It
905
+ works after the bar was drawn again (a resize, a rotation, the move
906
+ between the desktop toolbar and the phone's bar): the action's new button
907
+ gets the focus, and the More button when the action now sits in the More
908
+ menu. When the action is hidden by then (its count fell to 0, or past
909
+ `max`) the focus goes to the next bulk action, else "More", else the
910
+ table's "Select all" (the table itself without one), never the body.
911
+ `trigger` (and `element()`, the same as a function) is the action's
912
+ current button, or the More button, read when you read it: do not keep it
913
+ from the click, the node is disconnected after a redraw; use
914
+ `focusTrigger()`, and `actionId` is the key. A dialog opened from
915
+ `onClick` returns the focus to the button it was opened from by default,
916
+ which a redraw has disconnected: give it
917
+ `onCloseAutoFocus={(e) => { e.preventDefault(); ctx.focusTrigger(); }}`
918
+ as below. A handler that takes three parameters stays valid.
908
919
  ```tsx
909
920
  const [confirm, setConfirm] = useState<{ ids: string[]; focus: () => void } | null>(null);
910
921
  <ServerTable<Person>
@@ -921,6 +932,8 @@ export default function Students() {
921
932
  },
922
933
  ]}
923
934
  />
935
+ {/* A ConfirmDialog (or ConfirmationDialog, CustomDialog) takes the same
936
+ prop: onCloseAutoFocus={(e) => { e.preventDefault(); confirm?.focus(); }} */}
924
937
  <Dialog open={confirm != null} onOpenChange={(open) => !open && setConfirm(null)}>
925
938
  <DialogContent
926
939
  // Without this, the focus goes back to what Radix remembers, which is
@@ -1226,11 +1239,17 @@ import { FilterBar, PageFooter } from '@nxgt/material/components';
1226
1239
 
1227
1240
  Previous exists when the URL has a trail; Next when `pageInfo` has a next page and an `endCursor`. An end with no page is a disabled button with `aria-disabled`, not a link. Previous and Next are named "Previous" / "Next", or "Previous page" / "Next page" with `pageControls="icon"` (`material.table.pagination.*` keys, as the table's).
1228
1241
 
1242
+ A cursor feed whose API answers `pageInfo` and the items but no total (a news or notifications feed) leaves `totalCount` out: the footer shows no range (never a made-up total), only the controls, usually with `pageControls="icon"`, and still renders nothing for an empty first page.
1243
+
1244
+ ```tsx
1245
+ <PageFooter pageInfo={pageInfo} params={params} pageSize={20} count={items.length} pageControls="icon" />
1246
+ ```
1247
+
1229
1248
  | Prop | Type | Default | Effect |
1230
1249
  | --- | --- | --- | --- |
1231
1250
  | `params` | `URLSearchParams` | — | the page's search params: the trail is read from them and every other param is kept in the hrefs |
1232
1251
  | `pageInfo` | `{ hasNextPage, endCursor? }` | — | the connection's page info |
1233
- | `totalCount` | `number` | — | items matching the query across every page |
1252
+ | `totalCount` | `number` | — | items matching the query across every page; leave it out for a cursor feed with no total: no range, only the Previous / Next controls (Next disabled once `pageInfo.hasNextPage` is false) |
1234
1253
  | `pageSize` | `number` | — | items per page; it places the range from the trail |
1235
1254
  | `count` | `number` | `pageSize` | items on this page, for a short last page |
1236
1255
  | `pageHref` | `(params) => string` | ``?${params}`` | a page's href |
@@ -226,6 +226,7 @@ function DeleteProject({ onDelete }: { onDelete: () => Promise<void> }) {
226
226
  | `lockOnRefusal` | `boolean` | `false` | once `onConfirm` has refused (thrown), the confirm button stays disabled while its error shows, so a retry that cannot succeed is not invited; Cancel stays enabled. The error clears when the dialog closes |
227
227
  | `confirmLabel` / `cancelLabel` | `string` | "Confirm" / "Cancel", translated (`material.confirm-dialog.confirm` / `.cancel`) | button text |
228
228
  | `cancelVariant` | `ButtonVariant` | `'tonal'` | the Cancel button's variant; `'outlined'` restores the earlier look |
229
+ | `onCloseAutoFocus` | `(event: Event) => void` | — | called as the dialog closes, before the focus goes back; `event.preventDefault()` places it yourself, e.g. `ctx.focusTrigger()` of a `ServerTable` bulk action whose button a redraw replaced |
229
230
 
230
231
  On a phone (below 640 px) the footer stacks. A `destructive` dialog puts Cancel first and the destructive action last; a default one keeps the action on top.
231
232
 
@@ -234,7 +235,9 @@ it hands the focus back to what opened it, or to the menu's trigger when
234
235
  that was a `DropdownMenuItem` (gone by then); `Dialog`, `Sheet` and
235
236
  `Drawer` do the same.
236
237
  `ConfirmationDialog` is a `Dialog` and focuses its first control (its form
237
- field, or its first button).
238
+ field, or its first button). `ConfirmDialog`, `ConfirmationDialog` and
239
+ `CustomDialog` take `onCloseAutoFocus`: opened from a `ServerTable` bulk
240
+ action, pass `onCloseAutoFocus={(e) => { e.preventDefault(); ctx.focusTrigger(); }}`.
238
241
 
239
242
  ![ConfirmationDialog](../previews/feedback-status-confirmationdialog.webp)
240
243
 
@@ -327,6 +330,7 @@ import { Atom } from 'lucide-react';
327
330
  | `title` / `description` | `ReactNode` | — | hidden when empty |
328
331
  | `footer` | `ReactNode` | — | after a separator; omitted when empty |
329
332
  | `open` / `onOpenChange` | Radix `Dialog` props | uncontrolled | control it from outside |
333
+ | `onCloseAutoFocus` | `(event: Event) => void` | — | called as it closes, before the focus goes back; `event.preventDefault()` places it yourself |
330
334
 
331
335
  **Storybook:** `Feedback & status/CustomDialog` · **Vue:** `CustomDialog`
332
336
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material",
3
- "version": "3.42.0",
3
+ "version": "3.43.0",
4
4
  "license": "MIT",
5
5
  "bugs": {
6
6
  "email": "stevegires@gmail.com"