@nxgt/material 3.88.1 → 3.91.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +8 -5
  3. package/dist/{alert-dialog-CEGs7LMN.js → alert-dialog-BFk4ye-3.js} +1 -1
  4. package/dist/{async-slice-CR0M95Jq.js → async-slice-gGyfyUw2.js} +1 -1
  5. package/dist/{button-DuTNpo28.js → button-C5OEq9bi.js} +1 -1
  6. package/dist/{calculator-DsGk_fCw.js → calculator-Dg_L6d3R.js} +16 -16
  7. package/dist/calculator-field.js +6 -6
  8. package/dist/{calculator-segment-DMFptsDf.js → calculator-segment-CvAaDqKf.js} +1 -1
  9. package/dist/calculator.js +1 -1
  10. package/dist/charts.js +3 -3
  11. package/dist/{chip-C-RAjar5.js → chip-DOxFA4BI.js} +2 -2
  12. package/dist/{code-editor-DApeIOZ3.js → code-editor-tiAjhXW-.js} +4 -4
  13. package/dist/code-editor.js +4 -4
  14. package/dist/{code-language-items-CGtYfc1l.js → code-language-items-CcdzQvLT.js} +2 -2
  15. package/dist/{components-DiEY0nrG.js → components-G0nEVa52.js} +19744 -19404
  16. package/dist/components.js +38 -37
  17. package/dist/{conversion-variant-DfQiOwYC.js → conversion-variant-4gCdFWV-.js} +3 -3
  18. package/dist/currency-field-CIqe7Wg4.js +289 -0
  19. package/dist/{dialog-Bul68q8u.js → dialog-d6Kdf5wc.js} +3 -3
  20. package/dist/dnd.js +1 -1
  21. package/dist/{dropdown-menu-xUUE5X6T.js → dropdown-menu-DO5kUvof.js} +1 -1
  22. package/dist/editor.js +139 -139
  23. package/dist/{empty-state-D7ofGDU_.js → empty-state-DQnICuun.js} +1 -1
  24. package/dist/{financial-variant-CYgRZzKT.js → financial-variant-D80C1hVD.js} +14 -14
  25. package/dist/{hooks-CpvvZCs7.js → hooks-C-yLDF39.js} +2 -2
  26. package/dist/hooks.js +2 -2
  27. package/dist/{hover-card-CZLFQwZv.js → hover-card-DU3TAME4.js} +1 -1
  28. package/dist/i18n.js +3 -3
  29. package/dist/{icon-badge-0Xo9q7Ey.js → icon-badge-BGczZ4Hc.js} +1 -1
  30. package/dist/{icon-button-DE1EcO2-.js → icon-button-D0xjBCst.js} +1 -1
  31. package/dist/input-group-CSlXMFxu.js +70 -0
  32. package/dist/lib/components/ui/actions/back-link/back-link.d.ts +10 -2
  33. package/dist/lib/components/ui/collections/data-grid/grid-cell-tone.d.ts +12 -0
  34. package/dist/lib/components/ui/collections/data-grid/partials/grid-cell-access.d.ts +4 -3
  35. package/dist/lib/components/ui/collections/data-grid/partials/grid-cell-flags.d.ts +12 -0
  36. package/dist/lib/components/ui/collections/data-grid/partials/grid-cell-value.d.ts +4 -1
  37. package/dist/lib/components/ui/collections/data-table/partials/data-table-cell-content.d.ts +11 -0
  38. package/dist/lib/components/ui/collections/data-table/table-meta.d.ts +50 -2
  39. package/dist/lib/components/ui/collections/filter/use-filter-field-config.d.ts +1 -0
  40. package/dist/lib/components/ui/collections/list-pane/list-pane-item.d.ts +3 -1
  41. package/dist/lib/components/ui/collections/list-pane/partials/list-pane-item-content.d.ts +2 -1
  42. package/dist/lib/components/ui/dates/age-field/age-field.d.ts +8 -0
  43. package/dist/lib/components/ui/dates/age-field/age-form-field.d.ts +8 -0
  44. package/dist/lib/components/ui/dates/age-field/age-reading.d.ts +11 -0
  45. package/dist/lib/components/ui/dates/age-field/age-value.d.ts +9 -0
  46. package/dist/lib/components/ui/dates/age-field/index.d.ts +4 -0
  47. package/dist/lib/components/ui/dates/age-field/types.d.ts +40 -0
  48. package/dist/lib/components/ui/dates/index.d.ts +1 -0
  49. package/dist/lib/components/ui/display/badge/badge.d.ts +4 -1
  50. package/dist/lib/components/ui/display/entity-header/entity-header.d.ts +8 -1
  51. package/dist/lib/components/ui/display/index.d.ts +2 -0
  52. package/dist/lib/components/ui/display/person-cell/index.d.ts +1 -0
  53. package/dist/lib/components/ui/display/person-cell/person-cell.d.ts +23 -0
  54. package/dist/lib/components/ui/display/tone-value/index.d.ts +2 -0
  55. package/dist/lib/components/ui/display/tone-value/tone-value.d.ts +23 -0
  56. package/dist/lib/components/ui/feedback/error-state/partials/error-state-actions.d.ts +6 -1
  57. package/dist/lib/components/ui/feedback/page-load-failed/page-load-failed.d.ts +8 -1
  58. package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
  59. package/dist/lib/components/ui/fields/number-field/number-control.d.ts +2 -2
  60. package/dist/lib/components/ui/fields/switch/partials/switch-row.d.ts +4 -2
  61. package/dist/lib/components/ui/fields/switch/switch.d.ts +7 -1
  62. package/dist/lib/components/ui/layout/app-shell/activity-content-aside-parts.d.ts +68 -0
  63. package/dist/lib/components/ui/layout/app-shell/activity-content-aside-toggle.d.ts +22 -0
  64. package/dist/lib/components/ui/layout/app-shell/activity-content-aside.d.ts +5 -2
  65. package/dist/lib/components/ui/layout/app-shell/activity-content-aside.types.d.ts +13 -0
  66. package/dist/lib/components/ui/layout/app-shell/activity-content.d.ts +3 -2
  67. package/dist/lib/components/ui/layout/app-shell/app-shell.utils.d.ts +3 -1
  68. package/dist/lib/components/ui/layout/app-shell/app-sidebar-entries.d.ts +5 -2
  69. package/dist/lib/components/ui/layout/app-shell/app-sidebar-icon-item.d.ts +2 -1
  70. package/dist/lib/components/ui/layout/app-shell/app-sidebar.d.ts +3 -1
  71. package/dist/lib/components/ui/layout/app-shell/types.d.ts +14 -5
  72. package/dist/lib/components/ui/layout/sticky-action-bar/sticky-action-bar.d.ts +3 -1
  73. package/dist/lib/components/ui/layout/sticky-action-bar/use-bar-clearance.d.ts +10 -0
  74. package/dist/lib/components/ui/primitives/tabs/partials/tabs-list.d.ts +8 -2
  75. package/dist/lib/components/ui/primitives/tabs/tabs-nav.d.ts +5 -1
  76. package/dist/lib/components/ui/primitives/tabs/tabs-overflow-classes.d.ts +3 -0
  77. package/dist/lib/components/ui/selection/sortable-list/index.d.ts +1 -1
  78. package/dist/lib/components/ui/selection/sortable-list/sortable-list-context.d.ts +3 -1
  79. package/dist/lib/components/ui/selection/sortable-list/sortable-list.d.ts +6 -2
  80. package/dist/lib/components/ui/selection/sortable-list/types.d.ts +1 -0
  81. package/dist/lib/components/ui/selection/sortable-list/use-sortable-list-labels.d.ts +3 -0
  82. package/dist/lib/i18n/resources/en/en.d.ts +11 -0
  83. package/dist/lib/i18n/resources/en/material.json.d.ts +12 -1
  84. package/dist/lib/i18n/resources/fr/fr.d.ts +11 -0
  85. package/dist/lib/i18n/resources/fr/material.json.d.ts +12 -1
  86. package/dist/lib/i18n/resources/resources.d.ts +22 -0
  87. package/dist/lib/i18n/translator.d.ts +1 -1
  88. package/dist/lib.js +1 -1
  89. package/dist/main.js +54 -53
  90. package/dist/map.js +7 -7
  91. package/dist/material.css +1 -1
  92. package/dist/pdf-reader.js +1 -1
  93. package/dist/{percent-field-BFwxazf1.js → percent-field-DyJ2yOH4.js} +1 -1
  94. package/dist/{picker-surface-DkfTv_ft.js → picker-surface-CnlOKQ3D.js} +4 -4
  95. package/dist/{popover-BM6xApJd.js → popover-BevReSHi.js} +1 -1
  96. package/dist/{pos-types-CpTpT4vE.js → pos-types-BrZBX0Zg.js} +1 -1
  97. package/dist/{pos-variant-Bs5BUB1L.js → pos-variant-G12HCSfQ.js} +4 -4
  98. package/dist/print.js +1 -1
  99. package/dist/{programmer-variant-B1eI1fd0.js → programmer-variant-WWLwTGRF.js} +2 -2
  100. package/dist/{public-CzLMnSsS.js → public-C1a6wQ6o.js} +19 -19
  101. package/dist/{rich-code-block-CdVtxD75.js → rich-code-block-Dh9J-VZ8.js} +5 -5
  102. package/dist/{scientific-variant-rfvJKxB4.js → scientific-variant-BvutlF8a.js} +2 -2
  103. package/dist/{sortable-provider-BopzsciJ.js → sortable-provider-BrA5tfJX.js} +2 -2
  104. package/dist/{spinner-DcW21pN1.js → spinner-JnLLiLIO.js} +1 -1
  105. package/dist/{tabs-CdoBFtWx.js → tabs-dXCZGShv.js} +63 -61
  106. package/dist/theme-picker.js +2 -2
  107. package/dist/{theme-toggle-Ch3GkDDU.js → theme-toggle-BmcK6mXV.js} +1 -1
  108. package/dist/{toolbar-items-CZ1lJXZZ.js → toolbar-items-B-kMYsDT.js} +4 -4
  109. package/dist/{translate-material-BrYBBZ66.js → translate-material-BmpcP1p1.js} +1 -1
  110. package/dist/{translator-DoVsjUff.js → translator-CfoXTrZd.js} +24 -2
  111. package/dist/{use-chat-composer-C9DG7O1Z.js → use-chat-composer-DFD_eF-r.js} +4197 -4246
  112. package/dist/{use-heading-spy-kyOSe9rb.js → use-heading-spy-BcNK1qvq.js} +4 -4
  113. package/dist/{use-material-translation-BdrItuwY.js → use-material-translation-DoymV6IP.js} +1 -1
  114. package/dist/{use-return-focus-Cqzvj9R0.js → use-return-focus-CLEnhfka.js} +1 -1
  115. package/docs/components/README.md +2 -1
  116. package/docs/components/buttons-and-actions.md +10 -1
  117. package/docs/components/data-display.md +80 -4
  118. package/docs/components/data-table-filter-and-lists.md +56 -3
  119. package/docs/components/date-and-time.md +53 -1
  120. package/docs/components/feedback-and-status.md +20 -3
  121. package/docs/components/form-fields.md +11 -2
  122. package/docs/components/navigation-and-layout.md +7 -2
  123. package/docs/components/primitives.md +7 -0
  124. package/docs/components/selection-and-choice.md +4 -2
  125. package/docs/guides/app-shell.md +46 -11
  126. package/docs/guides/data-grid.md +12 -0
  127. package/docs/guides/forms.md +1 -0
  128. package/docs/guides/i18n.md +1 -1
  129. package/docs/previews/data-display-personcell.webp +0 -0
  130. package/docs/previews/data-display-tonevalue.webp +0 -0
  131. package/docs/previews/date-time-agefield.webp +0 -0
  132. package/docs/roadmap.md +23 -0
  133. package/docs/troubleshooting.md +4 -0
  134. package/package.json +2 -2
  135. package/dist/currency-field-cwC7cqUE.js +0 -286
@@ -105,12 +105,13 @@ type MenuItem = {
105
105
  - **`footer: true` pins an entry to the footer.** Being last in the list does
106
106
  not: appending an entry never moves another one out of the footer.
107
107
  - **`display: 'icon'` draws the entry as a round, icon-only button.** Meant
108
- for the footer's "Settings": in the collapsed rail, the expanded sidebar and
109
- the phone's menu sheet alike, it is an `IconButton` (the Button family's
110
- look, ghost, tonal when it is the active entry) with the label as its
111
- tooltip and, visually hidden, its accessible name, `aria-current="page"`
112
- when active, and a 44px target on a coarse pointer. The entries without it
113
- keep their icon and label. An icon entry draws no `badge`.
108
+ for the footer's "Settings": in the collapsed rail and the expanded sidebar
109
+ it is an `IconButton` (the Button family's look, ghost, tonal when it is the
110
+ active entry) with the label as its tooltip and, visually hidden, its
111
+ accessible name, `aria-current="page"` when active, and a 44px target on a
112
+ coarse pointer. The phone's menu sheet always shows the icon and the label,
113
+ since a lone icon reads poorly there. The entries without it keep their icon
114
+ and label. An icon button draws no `badge`.
114
115
 
115
116
  ```tsx
116
117
  { label: t('menu.settings'), url: '/settings', icon: 'gear', footer: true, display: 'icon' }
@@ -400,14 +401,27 @@ value (what the props resolve to), and `navigationClasses()` the classes that
400
401
  show each preset only at the sizes it is configured for. `DEFAULT_NAVIGATION` is
401
402
  the default value (`modal-drawer`, `rail`, `rail`).
402
403
 
404
+ `childrenIn: 'sidebar'` keeps an entry's `children` in the expanded sidebar and
405
+ the top dropdown only: the phone's menu sheet lists the entry as a plain link to
406
+ its `url`, with its badge, for a parent page that already lists them. Unset
407
+ (the default), the sheet nests them like the expanded sidebar.
408
+
409
+ ```tsx
410
+ {
411
+ label: 'Services', url: '/services', icon: 'grid-2',
412
+ children: [{ label: 'Lab', url: '/lab', icon: 'flask' }],
413
+ childrenIn: 'sidebar', // the phone's sheet shows "Services" alone, as a link
414
+ }
415
+ ```
416
+
403
417
  `MenuItem` also accepts `children`, `group`, `bar` and `priority`. Each is read
404
418
  by the presets that use it, and ignored by the others: `bar` and `priority`
405
419
  choose the entries of the bottom bar **and** of the floating toolbar (the rest go
406
420
  under « More »), `group` is read by the drawer, and `children` by the top
407
- navigation, the two-level panel, the sidebar and the phone's menu sheet.
421
+ navigation, the two-level panel, the sidebar and the phone's menu sheet (which drops them for a `childrenIn: 'sidebar'` entry).
408
422
  `footer` is read by the rail, the sidebar and the drawer (its footer), `display`
409
423
  by the rail, the sidebar and the phone's menu sheet, and `badge` and `badgeLabel`
410
- by every preset that draws entries, except on a `display: 'icon'` entry.
424
+ by every preset that draws entries, except on the icon button a `display: 'icon'` entry draws in the rail and the expanded sidebar (the phone's menu sheet draws its badge).
411
425
 
412
426
  ### The bottom bar (`navigation="bar"`)
413
427
 
@@ -577,7 +591,7 @@ menu button narrows it to 5rem, labels under the icons.
577
591
  link's name.
578
592
  - On a phone the bar is not drawn, whatever the object says
579
593
  (`{ phone: 'top' }` keeps the menu sheet): the sheet lists the entries and
580
- nests each entry's `children` under it, indented. The expanded sidebar nests
594
+ nests each entry's `children` under it, indented (unless `childrenIn: 'sidebar'`). The expanded sidebar nests
581
595
  them the same way.
582
596
  - An entry with `children` is only a trigger: its `url` is never a link, so put an "all" link as the first child. (The phone's menu sheet and the expanded sidebar do list the
583
597
  parent's own `url` as a link, with its children nested under it.)
@@ -635,7 +649,7 @@ menu button narrows it to 5rem, labels under the icons.
635
649
  level of `children` is listed.
636
650
  - **On a phone** neither is drawn, whatever the object says
637
651
  (`{ phone: 'two-level' }` keeps the menu sheet, as `'top'` does): the sheet lists
638
- the entries and nests each one's `children`. A name alone leaves the phone its
652
+ the entries and nests each one's `children` (unless `childrenIn: 'sidebar'`). A name alone leaves the phone its
639
653
  default.
640
654
  - At 768px the rail and the open panel take 19rem beside the content; fold the
641
655
  panel or use `{ tablet: 'rail', desktop: 'two-level' }` when that is too much.
@@ -1051,7 +1065,8 @@ export const invoiceRoute: RouteObject = {
1051
1065
  - **A record page uses `EntityHeader` without `title`.** Its band (icon or
1052
1066
  avatar, status, metadata, actions) stays, and no second heading duplicates
1053
1067
  the bar's `h1`: `<EntityHeader icon={<User />} metadata={[…]} actions={[…]} />`.
1054
- The root is a plain `div`, not a landmark, so nothing is left unnamed.
1068
+ The root is a plain `div`, not a landmark, so nothing is left unnamed. On a page band
1069
+ (no card of its own) pass `surface="none"`.
1055
1070
  - **It labels `<main>`.** While the bar has a title, the content region gets
1056
1071
  `aria-labelledby` pointing at the `h1`'s `id` (from `useId`, so it is the same
1057
1072
  on the server and the client); without one, there is no `aria-labelledby`.
@@ -1609,6 +1624,8 @@ export function BoardShell({ items }: { items: MenuItem[] }) {
1609
1624
  | `asideFrom` | `'lg'` \| `'xl'` \| `'2xl'` | `'xl'` | The viewport width (1024, 1280 or 1536px) from which the panel is beside the content |
1610
1625
  | `asideSticky` | `boolean` | `true` | Keeps the panel under the app bar while the page scrolls; it scrolls inside itself when taller than the window |
1611
1626
  | `asideStack` | `'before'` \| `'after'` | `'after'` | Where the panel goes below `asideFrom`, in the DOM and on screen |
1627
+ | `asidePhone` | `'stack'` \| `'toggle'` | `'stack'` | Below `asideFrom`: `'stack'` puts the panel after (or before) the content; `'toggle'` puts it behind a two-button switch above the content (content and panel are swapped, the form stays mounted so nothing typed is lost). For a form with a live preview. A `'sheet'` mode is discussed on [#2604](https://github.com/softistx/nxgt-material/issues/2604). |
1628
+ | `asidePreviewLabel` / `asideMainLabel` | `string` | "Preview" / "Content" (`material.app-shell.aside-preview` / `aside-main`) | The switch's button texts; the group's label is `material.app-shell.aside-toggle` ("View") |
1612
1629
  | `asideClassName` | `string` | — | Classes for the panel |
1613
1630
 
1614
1631
  ```tsx
@@ -1639,6 +1656,23 @@ content (`asideStack="before"` puts it first). The switch is CSS only: the
1639
1656
  server's HTML and a phone's first paint are already right, and no JavaScript
1640
1657
  decides the layout.
1641
1658
 
1659
+ `asidePhone="toggle"` is for a form with a live preview: below `asideFrom` a
1660
+ two-button switch ("Content" / "Preview"; French: "Contenu" / "Aperçu") sits above the content and shows one of
1661
+ the two (the other stays mounted, hidden). The save bar, when placed in the content, is out of view while the preview shows, back on the switch (a footer slot outside both panes is tracked in #2609). The first paint is
1662
+ still the content.
1663
+
1664
+ ```tsx
1665
+ <ActivityContent
1666
+ title="Edit question"
1667
+ width="form"
1668
+ aside={<QuestionPreview />}
1669
+ asideLabel="Preview"
1670
+ asidePhone="toggle"
1671
+ >
1672
+ <QuestionForm />
1673
+ </ActivityContent>
1674
+ ```
1675
+
1642
1676
  ```tsx
1643
1677
  <ActivityContent
1644
1678
  title="New visit"
@@ -1750,6 +1784,7 @@ in the plugin) assembles them for any record: a patient, a student, a project.
1750
1784
  ```tsx
1751
1785
  <ActivityContent hideHeader>
1752
1786
  <EntityHeader title={record.name} stats={stats} metadata={facts} />
1787
+ {/* on a page band, no card of its own: surface="none" */}
1753
1788
  <DescriptionList meta="Added on … · Modified on …">
1754
1789
  {fields.map((f) => <Description key={f.label} label={f.label} value={f.value} />)}
1755
1790
  </DescriptionList>
@@ -59,6 +59,18 @@ Columns are DataTable's TanStack v9 column definitions. A column with no
59
59
  | `headerWrap` | `true` lets the header's title wrap onto up to two lines, the rest cut; the header row keeps one height. Without it a title that does not fit is cut to one line. Either way, a title that is cut shows in full in a tooltip on hover and when the header has the focus (the header's accessible name is always the whole title) |
60
60
  | `headerMenu` | Items for the header's menu (a `size-6` button, Alt+ArrowDown or Shift+F10), made of `DataGridMenuItem`s. The built-in column items come first, then a separator, then yours |
61
61
  | `className` | Classes for the column's cells. In a [virtual grid](#long-grids-virtual) give the width as `w-*`, the fallback for a column with no width of its own |
62
+ | `cellClassName(value, row)` | Classes for a body cell computed from the value it shows now, so they follow an edit, a save and an undo; return `undefined` for none. Merged after `className`, header untouched, none over a value kept as an error ([cell classes](../components/data-table-filter-and-lists.md#cell-classes-from-the-value)) |
63
+ | `tone(value, row)`, `toneLabel(value, row)` | A tone for a body cell (or `undefined`) from the value it shows now, so it follows an edit, a pending save, a refused one put back and an undo. The cell gets `data-tone` and a `ToneValue` (shape, tone text colour, optional hidden words from `toneLabel`); while the cell is edited the editor's ring stays the focus indicator, and a value kept as an error is not toned. On a phone the list summary and the row panel's read-only values show it too ([cells toned by their value](../components/data-table-filter-and-lists.md#cells-toned-by-their-value)) |
64
+
65
+ ```tsx
66
+ helper.accessor('score', {
67
+ meta: {
68
+ editor: 'number',
69
+ tone: (v) => (typeof v === 'number' && v < 8 ? 'error' : undefined),
70
+ toneLabel: (v) => (typeof v === 'number' && v < 8 ? 'Low' : undefined),
71
+ },
72
+ });
73
+ ```
62
74
 
63
75
  ```tsx
64
76
  import { createColumnHelper } from '@tanstack/react-table';
@@ -522,6 +522,7 @@ or fails validation.
522
522
  | `DateField` · `DateFormField` | `Date \| undefined` (not an ISO string). With `timeZone`, still the true instant (the field shows and edits that zone's wall clock); with `showTimePicker`, `undefined` until the date and the time are both complete | `undefined` |
523
523
  | `DateRangeField` · `DateRangeFormField` | `DateRange \| undefined`: `{ from: Date \| undefined; to?: Date }` | `undefined` |
524
524
  | `TimeField` · `TimeFormField` | `Date \| undefined` (with `timeZone`, the true instant). With `asString`: `'HH:mm'`. While empty it shows its `placeholder` | `undefined` / `''` |
525
+ | `AgeField` · `AgeFormField` | `{ value: number \| null, unit: 'day' \| 'week' \| 'month' \| 'year' }` | `{ value: null, unit: <first unit> }` |
525
526
  | `DurationField` · `DurationFormField` | `number \| null`: total minutes (hours × 60 + minutes) | `null` |
526
527
  | `HalfDayRangeField` · `HalfDayRangeFormField` | `HalfDayRange \| undefined`: `{ start: { date, half }, end: { date, half } }`, `half` `'morning' \| 'afternoon'`; `undefined` while a day is missing; an end before the start is the value, refused by the wrapper's rule (`material.half-day-range.order`) | `undefined` |
527
528
 
@@ -345,7 +345,7 @@ separator's name (`material['list-details'].back` / `inspector-back` /
345
345
  `new-tab`; French "Menu", "Pied de page" and "s'ouvre dans un nouvel onglet");
346
346
  `JsonViewer`'s "Empty JSON" (`material['json-viewer'].empty`);
347
347
  `TimePicker`'s AM / PM (`material['time-picker'].am` / `pm`, "a.m." / "p.m." in
348
- French); and the names of `DurationField`'s inputs, "Hours" and "Minutes"
348
+ French); `AgeField`'s unit names, plural-aware (`material['age-field']['unit-day']`, `unit-week`, `unit-month`, `unit-year`: "2 months", « 2 mois »), its unit picker's name (`unit`, "Age unit") and the age its polite status reads (`reading`); and the names of `DurationField`'s inputs, "Hours" and "Minutes"
349
349
  (`material['duration-field']['hours-label']` / `minutes-label`) and the
350
350
  names of its −/+ buttons, "Increase hours" / "Decrease hours" /
351
351
  "Increase minutes" / "Decrease minutes"
package/docs/roadmap.md CHANGED
@@ -6,6 +6,16 @@ version they shipped in, and planned entries may name the major they target.
6
6
 
7
7
  ## Now
8
8
 
9
+ - **Fields Refined** — every text-like field (text, select, combobox, search,
10
+ phone, date and time boxes) gets a crisper, more legible look by default: a
11
+ border of at least 3:1 contrast, the card surface, a clear focus ring, and no
12
+ filled tint. `NumberField` is right-aligned with its steppers inside the box,
13
+ and gains a compact `score` variant with a caller-set tone and a locked state
14
+ with its reason. New `--success-text` and `--warning-text` tokens join
15
+ `--error-text` in every theme preset. One minor release, no API removed;
16
+ every `--nxgt-field-*` token override still applies
17
+ ([#2644](https://github.com/softistx/nxgt-material/issues/2644)).
18
+
9
19
  - **Calculator** — `@nxgt/material/calculator` is an exact-decimal calculator
10
20
  (Standard, Scientific, point of sale, Financial, Conversion, Programmer;
11
21
  every variant but Standard loads on demand)
@@ -379,6 +389,19 @@ version they shipped in, and planned entries may name the major they target.
379
389
 
380
390
  ## Shipped
381
391
 
392
+ - **Cells driven by their value** — in `DataTable`, `ServerTable` and
393
+ `DataGrid`, `meta.cellClassName(value, row)` computes a class per cell from
394
+ its value; `meta.tone(value, row)` colours a cell as coloured text after a
395
+ shaped mark (`ToneValue`: ◆ error, ▲ warning, ● success, ■ any other tone),
396
+ so colour is never the only cue, with an optional screen-reader label
397
+ (`meta.toneLabel`) and the band helper `toneBands` from
398
+ `@nxgt/material-core/cell-tone`; in `DataGrid` both follow edits, pending
399
+ saves and undo, and the phone cards and panels show the tone too; and
400
+ `PersonCell` shows a decorative initials avatar (photo when given) beside a
401
+ truncated name
402
+ ([#1117](https://github.com/softistx/nxgt-material/issues/1117),
403
+ [#1032](https://github.com/softistx/nxgt-material/issues/1032),
404
+ [#1112](https://github.com/softistx/nxgt-material/issues/1112)).
382
405
  - **Cursor trail cap** — `ServerTable` and `PageFooter` keep cursor URLs short:
383
406
  each trail entry is encoded once and the trail is capped by `maxTrail` (20 by
384
407
  default, a prop on each) behind a counted cut mark, so the "21–40 of N" range
@@ -2115,6 +2115,10 @@ offset's `top` (80 by default, so toasts start below that bar):
2115
2115
  <Toaster position="bottom-center" /> // bottom-center on every screen
2116
2116
  ```
2117
2117
 
2118
+ A mounted `StickyActionBar` is cleared automatically (`--nxgt-sticky-bar`). An
2119
+ explicit `offset` or `mobileOffset` drops that, so add
2120
+ `var(--nxgt-sticky-bar, 0px)` to its `bottom` yourself.
2121
+
2118
2122
  ### A toast never goes away after a tap
2119
2123
 
2120
2124
  **When:** on a phone (or any touch screen), a toast that should time out stays
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material",
3
- "version": "3.88.1",
3
+ "version": "3.91.0",
4
4
  "license": "MIT",
5
5
  "bugs": {
6
6
  "email": "stevegires@gmail.com"
@@ -390,7 +390,7 @@
390
390
  "docs:previews": "rm -rf node_modules/.cache/nxgt-preview-free/missing && NXGT_PREVIEW_FREE=1 vitest run --project previews && bun .storybook/preview-free-report.ts"
391
391
  },
392
392
  "dependencies": {
393
- "@nxgt/material-core": "^0.52.0",
393
+ "@nxgt/material-core": "^0.53.0",
394
394
  "@base-ui/react": "^1.6.0",
395
395
  "@dnd-kit/core": "^6.3.1",
396
396
  "@dnd-kit/sortable": "^10.0.0",
@@ -1,286 +0,0 @@
1
- import { t as e } from "./utils-BLfntITJ.js";
2
- import { n as t } from "./spinner-DcW21pN1.js";
3
- import { a as n, i as r, n as i } from "./field-blur-context-BLGgf_3z.js";
4
- import { i as a } from "./translate-material-BrYBBZ66.js";
5
- import { t as o } from "./use-material-translation-BdrItuwY.js";
6
- import { t as s } from "./icon-button-DE1EcO2-.js";
7
- import { t as c } from "./input-CC2joTjB.js";
8
- import { t as l } from "./text-field-BAdY-kpO.js";
9
- import { t as u } from "./number.utils-CSueQZcv.js";
10
- import { useCallback as d, useEffect as f, useId as p, useMemo as m, useRef as h, useState as g } from "react";
11
- import { jsx as _, jsxs as v } from "react/jsx-runtime";
12
- import { Minus as y, Plus as b } from "lucide-react";
13
- import { clampNumber as x, parseNumberInput as S, stepNumber as C } from "@nxgt/material-core/parse-number";
14
- import { currencyFractionDigits as w } from "@nxgt/material-core/currency-digits";
15
- //#region lib/components/ui/fields/number-field/number-stepper.tsx
16
- function T({ hovered: e, focused: t, focusVisible: n }) {
17
- return e || !t || n;
18
- }
19
- function E(e) {
20
- try {
21
- return e.matches(":focus-visible");
22
- } catch {
23
- return !0;
24
- }
25
- }
26
- function D({ direction: e, label: n, disabled: r, onStep: i }) {
27
- let a = h(null), o = h(!1), [c, l] = g(!1);
28
- return /* @__PURE__ */ _(t, {
29
- content: n,
30
- open: c,
31
- onOpenChange: (e) => {
32
- let t = a.current;
33
- l(e && T({
34
- hovered: o.current,
35
- focused: t != null && document.activeElement === t,
36
- focusVisible: t != null && E(t)
37
- }));
38
- },
39
- children: /* @__PURE__ */ _(s, {
40
- ref: a,
41
- variant: "outlined",
42
- color: "default",
43
- className: "size-9 shrink-0 rounded-field",
44
- disabled: r,
45
- "aria-label": n,
46
- onPointerEnter: () => {
47
- o.current = !0;
48
- },
49
- onPointerLeave: () => {
50
- o.current = !1;
51
- },
52
- onClick: () => i(e),
53
- children: _(e < 0 ? y : b, {})
54
- })
55
- });
56
- }
57
- function O(e) {
58
- let { t } = o();
59
- return e ? {
60
- decreaseLabel: t("material.number-field.decrease-named", "Decrease {label}", { label: e }),
61
- increaseLabel: t("material.number-field.increase-named", "Increase {label}", { label: e })
62
- } : {
63
- decreaseLabel: t("material.number-field.decrease", "Decrease"),
64
- increaseLabel: t("material.number-field.increase", "Increase")
65
- };
66
- }
67
- //#endregion
68
- //#region lib/components/ui/fields/number-field/format-number-input.ts
69
- function k(e, t = "en") {
70
- let n = String(e === 0 ? 0 : e);
71
- try {
72
- let r = u(e === 0 ? 0 : e, {
73
- language: t,
74
- useGrouping: !1,
75
- maximumFractionDigits: 20,
76
- numberingSystem: "latn"
77
- }), i = S(r);
78
- return e !== 0 && i !== e ? n : r;
79
- } catch {
80
- return n;
81
- }
82
- }
83
- //#endregion
84
- //#region lib/components/ui/fields/number-field/use-number-input.ts
85
- function A({ value: e, defaultValue: t, onChange: n, min: r, max: i, step: a, language: o }) {
86
- let s = (e) => k(e, o), c = e !== void 0, [l, u] = g(t), d = c ? e : l, [p, m] = g(() => d == null ? "" : s(d));
87
- f(() => {
88
- c && m((t) => e == null ? t.trim() === "" ? t : "" : S(t) === e ? t : k(e, o));
89
- }, [
90
- c,
91
- e,
92
- o
93
- ]);
94
- let _ = h(o);
95
- f(() => {
96
- _.current !== o && (_.current = o, m((e) => d != null && S(e) === d ? k(d, o) : e));
97
- }, [o, d]);
98
- let v = (e) => {
99
- c || u(e), n?.(e);
100
- };
101
- return {
102
- draft: p,
103
- amount: d,
104
- type: (e) => {
105
- if (m(e), e.trim() === "") v(null);
106
- else {
107
- let t = S(e);
108
- t != null && v(t);
109
- }
110
- },
111
- commit: (e) => {
112
- let t = S(e), n = t == null ? null : x(t, r, i);
113
- m(n == null ? "" : s(n)), v(n);
114
- },
115
- stepBy: (e) => {
116
- let t = C(d, e, a, r, i);
117
- m(s(t)), v(t);
118
- }
119
- };
120
- }
121
- //#endregion
122
- //#region lib/components/ui/fields/number-field/number-control.tsx
123
- function j({ error: e, disabled: t, value: n, defaultValue: r = null, onChange: i, onBlur: a, min: s, max: l, step: u = 1, hideSteppers: d = !1, suffix: f, stepperName: p, ...m }) {
124
- let { language: h } = o(), { draft: g, amount: y, type: b, commit: x, stepBy: S } = A({
125
- value: n,
126
- defaultValue: r,
127
- onChange: i,
128
- min: s,
129
- max: l,
130
- step: u,
131
- language: h
132
- }), { decreaseLabel: C, increaseLabel: w } = O(p), T = s != null && y != null && y <= s, E = l != null && y != null && y >= l;
133
- return /* @__PURE__ */ v("div", {
134
- className: "flex items-center gap-1",
135
- children: [
136
- !d && /* @__PURE__ */ _(D, {
137
- direction: -1,
138
- label: C,
139
- disabled: t || T,
140
- onStep: S
141
- }),
142
- /* @__PURE__ */ _(c, {
143
- ...m,
144
- inputMode: "decimal",
145
- autoComplete: "off",
146
- disabled: t,
147
- "aria-invalid": e || void 0,
148
- value: g,
149
- className: "text-center",
150
- onChange: (e) => b(e.target.value),
151
- onBlur: (e) => {
152
- x(e.target.value), a?.(e);
153
- }
154
- }),
155
- !d && /* @__PURE__ */ _(D, {
156
- direction: 1,
157
- label: w,
158
- disabled: t || E,
159
- onStep: S
160
- }),
161
- f != null && /* @__PURE__ */ _("span", {
162
- className: "px-1 font-medium text-muted-foreground text-sm",
163
- children: f
164
- })
165
- ]
166
- });
167
- }
168
- //#endregion
169
- //#region lib/components/ui/fields/number-field/number-field.tsx
170
- function M({ label: t, helperText: a, error: o, required: s, className: c, name: l, id: u, "aria-describedby": d, ...f }) {
171
- let m = i(), h = p(), g = u ?? h, y = p();
172
- return /* @__PURE__ */ v("div", {
173
- ref: m,
174
- "data-slot": "number-field",
175
- "data-pw": l ?? "number-field",
176
- className: e("grid w-full content-start gap-1.5", c),
177
- children: [
178
- t && /* @__PURE__ */ _(r, {
179
- htmlFor: g,
180
- withAsterisk: s,
181
- children: t
182
- }),
183
- /* @__PURE__ */ _(j, {
184
- ...f,
185
- id: g,
186
- name: l,
187
- stepperName: typeof t == "string" && t ? t : f["aria-label"] || void 0,
188
- error: o,
189
- required: s,
190
- "aria-describedby": [a ? y : void 0, d].filter(Boolean).join(" ") || void 0
191
- }),
192
- a && /* @__PURE__ */ _(n, {
193
- id: y,
194
- error: o,
195
- children: a
196
- })
197
- ]
198
- });
199
- }
200
- //#endregion
201
- //#region lib/components/ui/fields/currency-field/parse-currency.ts
202
- function N(e) {
203
- let t = e.trim().replace(/[\s\u00a0]/g, "");
204
- if (!t) return null;
205
- let n = t.lastIndexOf(",") > t.lastIndexOf(".") ? t.replace(/\./g, "").replace(",", ".") : t.replace(/,/g, ""), r = Number(n);
206
- return Number.isFinite(r) ? r : null;
207
- }
208
- //#endregion
209
- //#region lib/components/ui/fields/currency-field/use-currency-amount.ts
210
- function P(e, t, n) {
211
- return u(e, {
212
- language: n,
213
- minimumFractionDigits: t,
214
- maximumFractionDigits: t
215
- });
216
- }
217
- function F({ value: e, defaultValue: t, onChange: n, currency: r, fractionDigits: i }) {
218
- let o = m(() => w(r, i), [r, i]), s = a()?.language, c = e !== void 0, [l, u] = g(t), p = c ? e : l, _ = h(null), v = d((e) => {
219
- let t = P(e, o, s);
220
- return _.current = {
221
- text: t,
222
- value: e
223
- }, t;
224
- }, [o, s]), [y, b] = g(() => p == null ? "" : v(p));
225
- f(() => {
226
- c && b((t) => e == null ? "" : N(t) === e || _.current?.text === t && _.current.value === e ? t : v(e));
227
- }, [
228
- c,
229
- e,
230
- v
231
- ]);
232
- let x = h(p);
233
- x.current = p, f(() => {
234
- let e = x.current;
235
- b(e == null ? "" : v(e));
236
- }, [v]);
237
- let S = (e) => {
238
- c || u(e), n?.(e);
239
- };
240
- return {
241
- draft: y,
242
- change: (e) => {
243
- b(e);
244
- let t = N(e);
245
- e.trim() === "" ? S(null) : t != null && S(t);
246
- },
247
- commit: (e) => {
248
- let t = e.trim(), n = _.current?.text === t ? _.current.value : N(e);
249
- b(n == null ? t : v(n)), S(n);
250
- }
251
- };
252
- }
253
- //#endregion
254
- //#region lib/components/ui/fields/currency-field/currency-field.tsx
255
- function I({ value: t, defaultValue: n = null, onChange: r, currency: i = "EUR", fractionDigits: a, trailingAction: o, className: s, ...c }) {
256
- let { draft: u, change: d, commit: f } = F({
257
- value: t,
258
- defaultValue: n,
259
- onChange: r,
260
- currency: i,
261
- fractionDigits: a
262
- });
263
- return /* @__PURE__ */ _(l, {
264
- ...c,
265
- className: e(o && "[&_input]:pr-28!", s),
266
- inputMode: "decimal",
267
- autoComplete: "off",
268
- spellCheck: !1,
269
- value: u,
270
- trailing: o ? /* @__PURE__ */ v("span", {
271
- "data-slot": "currency-trailing",
272
- className: "flex h-8! w-auto! items-center gap-1 bg-transparent! pointer-coarse:h-11! ps-1 pe-1.5 text-xs font-medium text-muted-foreground",
273
- children: [i, o]
274
- }) : /* @__PURE__ */ _("span", {
275
- "data-slot": "currency-code",
276
- className: "flex h-8! w-auto! items-center justify-center bg-transparent! px-1 text-xs leading-none font-medium text-muted-foreground pointer-coarse:h-11!",
277
- children: i
278
- }),
279
- onChange: (e) => d(e.target.value),
280
- onBlur: (e) => {
281
- f(e.target.value), c.onBlur?.(e);
282
- }
283
- });
284
- }
285
- //#endregion
286
- export { x as a, j as i, N as n, S as o, M as r, C as s, I as t };