@nxgt/material 3.101.0 → 3.104.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 (207) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +37 -5
  3. package/dist/{alert-dialog-DYzxchiS.js → alert-dialog-CK_47jfm.js} +1 -1
  4. package/dist/{async-slice-drJyPvuM.js → async-slice-1ZNMiwyJ.js} +1 -1
  5. package/dist/{bubble-input-CQwVSoU3.js → bubble-input-BxI1WmNs.js} +17 -5
  6. package/dist/{button-GeIsUekU.js → button-YfEMZbRO.js} +2 -2
  7. package/dist/{calculator-BS4PAm6l.js → calculator-BmYZId4c.js} +261 -460
  8. package/dist/calculator-field.js +7 -7
  9. package/dist/{calculator-segment-CEdwE24M.js → calculator-segment-DJ08DJ-n.js} +1 -1
  10. package/dist/calculator.js +1 -1
  11. package/dist/charts.js +819 -764
  12. package/dist/{chip-BdEtFQ85.js → chip-DPj6e-3v.js} +2 -2
  13. package/dist/{code-editor-BByXZOGy.js → code-editor-CZg3nLZO.js} +178 -177
  14. package/dist/code-editor.js +5 -5
  15. package/dist/{code-language-items-CSZX5zxD.js → code-language-items-Nq1iOGId.js} +14 -5
  16. package/dist/components/ui/actions/action-card/action-card-tile.css +47 -0
  17. package/dist/components/ui/display/chip/chip-soft.css +27 -2
  18. package/dist/components/ui/display/code/code-moonlight.css +10 -10
  19. package/dist/components/ui/primitives/collapsible/collapsible.css +9 -0
  20. package/dist/{components-DQ4MljCr.js → components-B2wrH_W0.js} +19302 -18583
  21. package/dist/components.js +38 -37
  22. package/dist/{conversion-variant-DTQqAyDt.js → conversion-variant-C-LPx5ek.js} +5 -5
  23. package/dist/{currency-field-Dnju7ss2.js → currency-field-DnsRIA80.js} +24 -23
  24. package/dist/{dialog-5_2__tu8.js → dialog-BKBsDGFc.js} +3 -3
  25. package/dist/dnd.js +1 -1
  26. package/dist/{dropdown-menu-FdGC5kIP.js → dropdown-menu-QMDbRidH.js} +1 -1
  27. package/dist/editor.js +9 -9
  28. package/dist/{empty-state-BoepPG5v.js → empty-state-BLIJ6znY.js} +1 -1
  29. package/dist/{field-blur-DkiZOVNV.js → field-blur-DMJwPLsg.js} +9 -9
  30. package/dist/field-blur-context-3rKussBQ.js +29 -0
  31. package/dist/{financial-variant-BhqJp5C-.js → financial-variant-Ws0Jp01H.js} +5 -5
  32. package/dist/{hooks-DjcbJZiO.js → hooks-C3vkQmzh.js} +106 -61
  33. package/dist/hooks.js +3 -3
  34. package/dist/{hover-card-CmHJeBeY.js → hover-card-Df649U6I.js} +1 -1
  35. package/dist/i18n.js +3 -3
  36. package/dist/{icon-badge-LGUPQh_c.js → icon-badge-Cma1qOOJ.js} +1 -1
  37. package/dist/{icon-button-BSVpO1jP.js → icon-button-CpKxV264.js} +1 -1
  38. package/dist/key-layout-D2goCDkD.js +31 -0
  39. package/dist/keypad.js +551 -0
  40. package/dist/label-DR-zZyGQ.js +47 -0
  41. package/dist/lib/components/ui/actions/index.d.ts +1 -0
  42. package/dist/lib/components/ui/actions/product-tile/index.d.ts +1 -0
  43. package/dist/lib/components/ui/actions/product-tile/product-tile-link.d.ts +16 -0
  44. package/dist/lib/components/ui/actions/product-tile/product-tile.d.ts +41 -0
  45. package/dist/lib/components/ui/collections/data-table/data-table-density.d.ts +1 -1
  46. package/dist/lib/components/ui/collections/data-table/partials/data-table-scroll-region.d.ts +4 -1
  47. package/dist/lib/components/ui/collections/data-table/partials/layouts/data-table-list-bar.d.ts +6 -3
  48. package/dist/lib/components/ui/collections/data-table/partials/layouts/list-bar-in-row.d.ts +2 -1
  49. package/dist/lib/components/ui/collections/data-table/types.d.ts +11 -8
  50. package/dist/lib/components/ui/collections/data-table/virtual-option.d.ts +9 -3
  51. package/dist/lib/components/ui/collections/filter/use-filter-field-config.d.ts +2 -0
  52. package/dist/lib/components/ui/collections/server-table/partials/server-table-passed-through.d.ts +1 -0
  53. package/dist/lib/components/ui/collections/server-table/types.d.ts +1 -1
  54. package/dist/lib/components/ui/display/chip/chip.variants.d.ts +5 -4
  55. package/dist/lib/components/ui/display/code/code-block-parts.d.ts +1 -1
  56. package/dist/lib/components/ui/display/code/code-block.d.ts +4 -1
  57. package/dist/lib/components/ui/display/code/languages.d.ts +2 -0
  58. package/dist/lib/components/ui/display/code/moonlight-tokens.d.ts +10 -10
  59. package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog-props.d.ts +73 -0
  60. package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog-rules.d.ts +12 -0
  61. package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog.d.ts +5 -61
  62. package/dist/lib/components/ui/feedback/confirm-dialog/confirm-footer.d.ts +12 -0
  63. package/dist/lib/components/ui/feedback/confirm-dialog/confirm-phrase.d.ts +26 -0
  64. package/dist/lib/components/ui/feedback/confirm-dialog/use-confirm-action.d.ts +1 -1
  65. package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
  66. package/dist/lib/components/ui/fields/index.d.ts +1 -0
  67. package/dist/lib/components/ui/fields/quantity-field/types.d.ts +1 -1
  68. package/dist/lib/components/ui/fields/quantity-stepper/index.d.ts +2 -0
  69. package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper-model.d.ts +9 -0
  70. package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper-value.d.ts +15 -0
  71. package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper.d.ts +9 -0
  72. package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper.types.d.ts +35 -0
  73. package/dist/lib/components/ui/fields/quantity-stepper/use-quantity-stepper.d.ts +28 -0
  74. package/dist/lib/components/ui/layout/index.d.ts +1 -0
  75. package/dist/lib/components/ui/layout/pane-layout/pane-layout.d.ts +8 -3
  76. package/dist/lib/components/ui/layout/pane-layout/partials/pane-layout-classes.d.ts +2 -2
  77. package/dist/lib/components/ui/layout/pane-layout/partials/pane-layout-context.d.ts +9 -0
  78. package/dist/lib/components/ui/layout/pane-layout/partials/use-pane-layout-state.d.ts +7 -2
  79. package/dist/lib/components/ui/layout/panel-card/index.d.ts +1 -0
  80. package/dist/lib/components/ui/layout/panel-card/panel-card.d.ts +23 -0
  81. package/dist/lib/components/ui/primitives/collapsible/collapsible.d.ts +9 -1
  82. package/dist/lib/components/ui/primitives/tabs/partials/tabs-list.d.ts +6 -1
  83. package/dist/lib/components/ui/primitives/tabs/tabs-classes.d.ts +1 -0
  84. package/dist/lib/components/ui/selection/chip-group-field/chip-group-field.d.ts +8 -0
  85. package/dist/lib/components/ui/selection/chip-group-field/chip-group-field.types.d.ts +6 -0
  86. package/dist/lib/components/ui/selection/chip-group-field/partials/chip-group-reasons.d.ts +10 -0
  87. package/dist/lib/components/ui/selection/chip-group-field/partials/use-chip-rail.d.ts +12 -0
  88. package/dist/lib/components/ui/selection/choice-group/choice-group-root.d.ts +5 -2
  89. package/dist/lib/components/ui/selection/index.d.ts +1 -0
  90. package/dist/lib/components/ui/selection/status-menu/index.d.ts +3 -0
  91. package/dist/lib/components/ui/selection/status-menu/partials/status-confirm.d.ts +12 -0
  92. package/dist/lib/components/ui/selection/status-menu/partials/status-icon.d.ts +6 -0
  93. package/dist/lib/components/ui/selection/status-menu/partials/status-items.d.ts +18 -0
  94. package/dist/lib/components/ui/selection/status-menu/partials/status-menu-list.d.ts +14 -0
  95. package/dist/lib/components/ui/selection/status-menu/partials/status-sheet-row.d.ts +11 -0
  96. package/dist/lib/components/ui/selection/status-menu/partials/status-sheet.d.ts +21 -0
  97. package/dist/lib/components/ui/selection/status-menu/partials/status-trigger.d.ts +18 -0
  98. package/dist/lib/components/ui/selection/status-menu/status-menu-data.d.ts +12 -0
  99. package/dist/lib/components/ui/selection/status-menu/status-menu-form-field.d.ts +20 -0
  100. package/dist/lib/components/ui/selection/status-menu/status-menu.d.ts +13 -0
  101. package/dist/lib/components/ui/selection/status-menu/types.d.ts +76 -0
  102. package/dist/lib/components/ui/selection/status-menu/use-status-change.d.ts +18 -0
  103. package/dist/lib/components/ui/selection/status-menu/use-status-menu.d.ts +30 -0
  104. package/dist/lib/components/ui/widgets/calculator/partials/calculator-keypad.d.ts +1 -5
  105. package/dist/lib/components/ui/widgets/calculator/variants/key-layout.d.ts +3 -49
  106. package/dist/lib/components/ui/widgets/keypad/cash-model.d.ts +16 -0
  107. package/dist/lib/components/ui/widgets/keypad/index.d.ts +5 -0
  108. package/dist/lib/components/ui/widgets/keypad/key-layout.d.ts +56 -0
  109. package/dist/lib/components/ui/widgets/keypad/keypad-binding.d.ts +32 -0
  110. package/dist/lib/components/ui/widgets/keypad/keypad-cash.d.ts +28 -0
  111. package/dist/lib/components/ui/widgets/keypad/keypad-display.d.ts +11 -0
  112. package/dist/lib/components/ui/widgets/keypad/keypad-entry.d.ts +39 -0
  113. package/dist/lib/components/ui/widgets/keypad/keypad-grid.d.ts +27 -0
  114. package/dist/lib/components/ui/widgets/{calculator/partials/calculator-key.d.ts → keypad/keypad-key.d.ts} +6 -3
  115. package/dist/lib/components/ui/widgets/keypad/keypad-layout.d.ts +19 -0
  116. package/dist/lib/components/ui/widgets/{calculator/partials → keypad}/keypad-navigation.d.ts +1 -1
  117. package/dist/lib/components/ui/widgets/keypad/keypad.d.ts +9 -0
  118. package/dist/lib/components/ui/widgets/keypad/keypad.types.d.ts +91 -0
  119. package/dist/lib/components/ui/widgets/keypad/use-keypad-cash.d.ts +14 -0
  120. package/dist/lib/components/ui/widgets/keypad/use-keypad-keyboard.d.ts +7 -0
  121. package/dist/lib/components/ui/widgets/keypad/use-keypad-number.d.ts +10 -0
  122. package/dist/lib/components/ui/widgets/keypad/use-keypad-quantity.d.ts +16 -0
  123. package/dist/lib/components/ui/widgets/keypad/use-keypad-value.d.ts +17 -0
  124. package/dist/lib/components/ui/widgets/keypad/use-keypad.d.ts +23 -0
  125. package/dist/lib/components/ui/widgets/native-charts/core/label-placement.d.ts +25 -0
  126. package/dist/lib/components/ui/widgets/native-charts/core/reference-geometry.d.ts +3 -2
  127. package/dist/lib/hooks/index.d.ts +1 -0
  128. package/dist/lib/hooks/use-barcode-scanner.d.ts +39 -0
  129. package/dist/lib/i18n/resources/en/en.d.ts +28 -0
  130. package/dist/lib/i18n/resources/en/material.json.d.ts +30 -2
  131. package/dist/lib/i18n/resources/fr/fr.d.ts +28 -0
  132. package/dist/lib/i18n/resources/fr/material.json.d.ts +30 -2
  133. package/dist/lib/i18n/resources/resources.d.ts +56 -0
  134. package/dist/lib/i18n/translator.d.ts +1 -1
  135. package/dist/lib/keypad/index.d.ts +20 -0
  136. package/dist/lib/print/output/csv.d.ts +37 -0
  137. package/dist/lib/print/output/index.d.ts +1 -0
  138. package/dist/lib.js +1 -1
  139. package/dist/main.js +54 -53
  140. package/dist/map.js +9 -9
  141. package/dist/material.css +1 -1
  142. package/dist/pdf-reader.js +1 -1
  143. package/dist/{percent-field-DGlU_xii.js → percent-field-sAq7BaxN.js} +15 -14
  144. package/dist/{picker-surface-CGhqNR8a.js → picker-surface--6E-nvpU.js} +4 -4
  145. package/dist/{popover-ZxL7ktKk.js → popover-bEB8I_-w.js} +1 -1
  146. package/dist/{pos-types-BQ0QpYMp.js → pos-types-Cpdc4E33.js} +1 -1
  147. package/dist/{pos-variant-CKy8h_5i.js → pos-variant-ClMGu59I.js} +25 -25
  148. package/dist/print.js +146 -127
  149. package/dist/{programmer-variant-CbHstE28.js → programmer-variant-ChzgyA7x.js} +30 -30
  150. package/dist/{public-D-tdOz4b.js → public-BY8N5NDT.js} +9 -9
  151. package/dist/{rich-code-block-CTPj1LuD.js → rich-code-block-D2GiWuqD.js} +18 -18
  152. package/dist/{scientific-variant-B8U9h3II.js → scientific-variant-31_93gcZ.js} +8 -8
  153. package/dist/{sortable-provider-CSoWE7Ce.js → sortable-provider-DGiVn72t.js} +2 -2
  154. package/dist/{spinner-ffuwBXHl.js → spinner-CEVomQ5K.js} +1 -1
  155. package/dist/standard-layout-DTr9yy8Z.js +48 -0
  156. package/dist/{tabs-JgRq6ZJx.js → tabs-B8MXBbPW.js} +14 -14
  157. package/dist/{text-field-DURkIP-v.js → text-field-DN88qD0Q.js} +16 -15
  158. package/dist/theme-picker.js +13 -13
  159. package/dist/{theme-toggle-n_Zg7JAA.js → theme-toggle-C7pnu1Xh.js} +1 -1
  160. package/dist/{toolbar-items-IANZdcw4.js → toolbar-items-BDfoh8QB.js} +4 -4
  161. package/dist/{translate-material-CMHUH5Ha.js → translate-material-s_lxIV43.js} +1 -1
  162. package/dist/{translator-9zA-Um3W.js → translator-Cgql9srO.js} +56 -4
  163. package/dist/use-calculator-keyboard-DysnFK8m.js +222 -0
  164. package/dist/{use-chat-composer-BoIN7RCA.js → use-chat-composer-xCcp9fVc.js} +2250 -2195
  165. package/dist/{use-heading-spy-Ciw9GBHL.js → use-heading-spy-FIZlf9qj.js} +235 -154
  166. package/dist/{use-material-translation-CLNaMhUf.js → use-material-translation-xNzbSVgS.js} +1 -1
  167. package/dist/{use-return-focus-DWhbTGVe.js → use-return-focus-CW9KkDkp.js} +1 -1
  168. package/docs/README.md +2 -1
  169. package/docs/components/README.md +2 -0
  170. package/docs/components/buttons-and-actions.md +81 -2
  171. package/docs/components/data-display.md +6 -3
  172. package/docs/components/data-table-filter-and-lists.md +19 -6
  173. package/docs/components/feedback-and-status.md +20 -0
  174. package/docs/components/form-fields.md +60 -7
  175. package/docs/components/navigation-and-layout.md +48 -2
  176. package/docs/components/primitives.md +44 -2
  177. package/docs/components/rich-widgets.md +28 -0
  178. package/docs/components/selection-and-choice.md +176 -1
  179. package/docs/guides/calculator.md +1 -1
  180. package/docs/guides/cards-and-tiles.md +31 -24
  181. package/docs/guides/csp.md +4 -3
  182. package/docs/guides/data-grid.md +5 -4
  183. package/docs/guides/field-look-refined.md +2 -2
  184. package/docs/guides/forms.md +1 -0
  185. package/docs/guides/helpers.md +49 -0
  186. package/docs/guides/keypad.md +233 -0
  187. package/docs/guides/printing.md +51 -1
  188. package/docs/previews/buttons-actions-producttile.webp +0 -0
  189. package/docs/previews/data-display-codeblock.webp +0 -0
  190. package/docs/previews/data-display-codegroup.webp +0 -0
  191. package/docs/previews/data-display-jsonviewer.webp +0 -0
  192. package/docs/previews/form-fields-quantitystepper.webp +0 -0
  193. package/docs/previews/navigation-layout-panelcard.webp +0 -0
  194. package/docs/previews/rich-widgets-keypad.webp +0 -0
  195. package/docs/previews/selection-choice-statusmenu.webp +0 -0
  196. package/docs/roadmap.md +21 -0
  197. package/docs/troubleshooting.md +68 -4
  198. package/lib/components/ui/actions/action-card/action-card-tile.css +47 -0
  199. package/lib/components/ui/display/chip/chip-soft.css +27 -2
  200. package/lib/components/ui/display/code/code-moonlight.css +10 -10
  201. package/lib/components/ui/primitives/collapsible/collapsible.css +9 -0
  202. package/lib/components/ui/widgets/calculator/calculator-keys.css +3 -0
  203. package/lib/components/ui/widgets/calculator/calculator.css +118 -0
  204. package/package.json +7 -2
  205. package/dist/field-blur-context-BLGgf_3z.js +0 -73
  206. package/dist/key-layout-cR79xfGE.js +0 -38
  207. package/dist/standard-layout-BuSRr3TY.js +0 -40
@@ -0,0 +1,233 @@
1
+ # Keypad
2
+
3
+ `@nxgt/material/keypad` is a numeric keypad you can use outside the Calculator
4
+ widget: a touch and keyboard entry for a quantity, a price, a percentage, or an
5
+ amount tendered in cash with the change worked out. The value is a canonical
6
+ decimal string (`'12.5'`), the arithmetic is exact (`0.1 + 0.2` is `0.3`), and
7
+ nothing is evaluated as code. The maths is `@nxgt/material-core/calculator`, the
8
+ same engine as the [calculator](calculator.md).
9
+
10
+ - [Set up](#set-up)
11
+ - [Standard entry](#standard-entry)
12
+ - [Decimal or minor-unit entry](#decimal-or-minor-unit-entry)
13
+ - [Calculations](#calculations)
14
+ - [Limits](#limits)
15
+ - [Keyboard](#keyboard)
16
+ - [Bind it to a field](#bind-it-to-a-field)
17
+ - [Cash](#cash)
18
+ - [Props](#props)
19
+ - [Sizing and styles](#sizing-and-styles)
20
+ - [Accessibility](#accessibility)
21
+ - [Troubleshooting](#troubleshooting)
22
+
23
+ ## Set up
24
+
25
+ ```tsx
26
+ import { Keypad } from '@nxgt/material/keypad';
27
+ ```
28
+
29
+ The entry is its own: it is not in `@nxgt/material` or
30
+ `@nxgt/material/components`, and it renders on the server. **There is no keypad
31
+ stylesheet**: the Calculator's `calculator.css` styles it, so import it once,
32
+ after `styles.css`:
33
+
34
+ ```css
35
+ @import "@nxgt/material/styles.css";
36
+ @import "@nxgt/material/calculator.css";
37
+ ```
38
+
39
+ (or `import '@nxgt/material/calculator.css'` in your entry module).
40
+
41
+ ## Standard entry
42
+
43
+ ```tsx
44
+ const [qty, setQty] = useState('');
45
+
46
+ <Keypad value={qty} onValueChange={setQty} onCommit={(value) => addLine(value)} />
47
+ ```
48
+
49
+ `value` is controlled with `onValueChange`; `defaultValue` leaves the keypad
50
+ uncontrolled. `onValueChange` receives the value text each time it changes
51
+ (`''` when emptied), but not while a calculation waits for `=`. The commit key (a check mark; "Enter" to a screen reader,
52
+ or your `commitLabel`) and the Enter key call `onCommit(value, expression)`:
53
+ the value held between `min` and `max`, and the expression that gave it. An
54
+ empty keypad commits nothing. The keypad keeps its value after a commit; empty
55
+ it from your side with a controlled `value`.
56
+
57
+ ## Decimal or minor-unit entry
58
+
59
+ `entry="decimal"` (the default) types the number as written: `1`, `2`, `.`, `5`
60
+ is `12.5`.
61
+
62
+ `entry="minor"` types it the way a till does: `1`, `2`, `5` is `1.25`, `00`
63
+ adds two zeros, backspace drops the last digit. It has no decimal key and no
64
+ operations. The decimals come from `currency` (EUR 2, JPY 0, KWD 3) or
65
+ `fractionDigits` (2 by default).
66
+
67
+ ```tsx
68
+ <Keypad entry="minor" currency="EUR" value={price} onValueChange={setPrice} />
69
+ ```
70
+
71
+ `currency` also draws the symbol after the amount. `locale` sets the decimal
72
+ separator, the grouping and the decimal key's glyph. `fractionDigits` limits the
73
+ decimals typed (a further digit is refused) and rounds a calculation half-up to
74
+ them.
75
+
76
+ ## Calculations
77
+
78
+ Pass `operations` to add calculation keys: `true` for add, subtract, multiply
79
+ and divide, or a list such as `['add', 'multiply']`. The commit key then reads
80
+ "=" while a calculation waits, and a second press commits the result:
81
+
82
+ ```tsx
83
+ <Keypad operations onCommit={(value, expression) => save(value, expression)} />
84
+ // 2 + 3 = then the commit key: onCommit('5', '2+3')
85
+ ```
86
+
87
+ An operator pressed after "=" continues from the result. `minor` entry and the
88
+ `cash` variant take no operations.
89
+
90
+ ## Limits
91
+
92
+ - `max`: a digit that would pass it is refused while typing.
93
+ - `min`: never stops typing (`5` on the way to `50` is fine); the commit key
94
+ holds the value at `min`, and tells `onValueChange` when it moved it.
95
+ - `fractionDigits`: see above; `0` removes the decimal key.
96
+
97
+ An operand past `max` inside a calculation is not caught while typing: only the
98
+ commit clamp holds the result.
99
+
100
+ ## Keyboard
101
+
102
+ `keyboard` says where the physical keyboard works:
103
+
104
+ | Value | |
105
+ | --- | --- |
106
+ | `'focus'` (default) | When focus is inside the keypad (after a tap or a Tab to a key) |
107
+ | `'global'` | Anywhere on the page, except in a field. Use it for a screen that is the keypad, such as a till |
108
+ | `false` | Never |
109
+
110
+ Digits, `.` or `,` (by locale), the operators you offer, Backspace, Escape (clear) and
111
+ Enter work. Enter on a focused key presses that key, as a button does; Enter
112
+ elsewhere commits. The keys form one tab stop with arrow keys over the grid,
113
+ Home and End along a row.
114
+
115
+ ## Bind it to a field
116
+
117
+ `useKeypadNumber` and `useKeypadQuantity` bind the keypad to a field you already
118
+ have. Pass the result as `binding`: the keypad reads the field's number, writes
119
+ each change back, takes its decimals, `min` and `max`, and **hides its own
120
+ display** (the field shows the number; pass `display` to show both). With
121
+ `operations`, the display stays on, since the field keeps its number while a
122
+ calculation is typed and `onValueChange` is called with the result at `=`.
123
+
124
+ ```tsx
125
+ import { NumberField } from '@nxgt/material/components';
126
+ import { Keypad, useKeypadNumber } from '@nxgt/material/keypad';
127
+
128
+ const [qty, setQty] = useState<number | null>(1);
129
+ const binding = useKeypadNumber({ value: qty, onChange: setQty, min: 1, max: 99 });
130
+
131
+ <NumberField label="Quantity" value={qty} onChange={setQty} />
132
+ <Keypad binding={binding} />
133
+ ```
134
+
135
+ | Field | Hook |
136
+ | --- | --- |
137
+ | `NumberField`, `PercentField` | `useKeypadNumber({ value, onChange, min, max, fractionDigits })` |
138
+ | `CurrencyField` | `useKeypadNumber({ ..., currency })`; with `minorUnits`, add `minorUnits: true`: the field holds cents (`1250`), the keypad types euros (`12.5`), and `min` / `max` stay in cents |
139
+ | `QuantityField` | `useKeypadQuantity({ value, onChange })`: the keypad types the amount, the unit stays as the field holds it |
140
+
141
+ An explicit prop on `Keypad` wins over the binding's, and both `onValueChange`
142
+ handlers are called. A cleared keypad writes `null`. While a calculation is
143
+ typed (`2+3`) the field keeps its number and nothing is written; the result is
144
+ written at `=`.
145
+
146
+ ## Cash
147
+
148
+ `variant="cash"` takes the amount a customer hands over and works out the change:
149
+
150
+ ```tsx
151
+ <Keypad
152
+ variant="cash"
153
+ total="17.35"
154
+ denominations={[5, 10, 20, 50]}
155
+ currency="EUR"
156
+ onTender={(tendered, settlement) => {
157
+ if (settlement.status === 'paid') charge(tendered, settlement.change);
158
+ else askForMore(settlement.due);
159
+ }}
160
+ />
161
+ ```
162
+
163
+ - The total is shown, then the change or what is **still due** as soon as an
164
+ amount is entered, and a live region says it ("Change 2.65", "Still due 7.35").
165
+ - `denominations` add amount buttons: the exact total, then the next round
166
+ amount at or above it for each bill or coin (`17.35` with `[5, 10, 20, 50]`
167
+ gives `17.35`, `20`, `50`), four buttons at most, none above `max`. A button
168
+ fills the amount; the commit key (named "Tender") reports it.
169
+ - `onTender(tendered, settlement)` is the only callback of the variant (`onCommit`
170
+ is not called). `settlement` is `{ status: 'paid', change }` (change may be
171
+ `'0'`) or `{ status: 'short', due }`, as exact decimal text. A short payment is a
172
+ settlement, not an error. **The app owns the charge**: the keypad only reports.
173
+ - The settlement row carries a tone: **success** while the payment covers the
174
+ total (change may be `0`), **warning** while an amount is still due, with the
175
+ figure emphasised (AA text on a tinted band, light and dark).
176
+ - The keys follow one scheme: digits neutral, the editing keys (C, backspace)
177
+ one tone, the commit key filled primary, the quick amounts tonal chips in a
178
+ two-column grid (an odd last chip takes the full row; amounts never
179
+ truncate). Without a decimal key the 0 key spans nine of the twelve columns,
180
+ the commit key the other three.
181
+ - `total` must be a valid, non-negative amount (a number or exact decimal text);
182
+ without one the commit key does nothing.
183
+
184
+ Use `entry="minor"` for a till that types cents, and `size="large"` for a
185
+ counter's touch screen.
186
+
187
+ ## Props
188
+
189
+ | Prop | Default | |
190
+ | --- | --- | --- |
191
+ | `value` / `defaultValue` / `onValueChange` | none | The value as decimal text, controlled or not |
192
+ | `onCommit` | none | `(value, expression)` on the commit key or Enter; standard variant |
193
+ | `binding` | none | From `useKeypadNumber` / `useKeypadQuantity` |
194
+ | `entry` | `'decimal'` | `'decimal'` or `'minor'` |
195
+ | `operations` | off | `true` or a list of `'add' \| 'subtract' \| 'multiply' \| 'divide'` |
196
+ | `locale` | the page's | Separator, grouping, decimal key glyph |
197
+ | `currency` | none | ISO 4217 code: the symbol and the currency's decimals |
198
+ | `fractionDigits` | the currency's, else any | Decimals kept |
199
+ | `min`, `max` | none | See [Limits](#limits) |
200
+ | `keyboard` | `'focus'` | `'focus'`, `'global'` or `false` |
201
+ | `size` | `'comfortable'` | `'comfortable'` or `'large'` |
202
+ | `display` | `true`, `false` with a `binding` and no `operations` | Shows the amount above the keys |
203
+ | `commitLabel` | translated "Enter" ("Tender" in cash) | The commit key's name and text |
204
+ | `disabled` | `false` | Keys do nothing and say so (`aria-disabled`) |
205
+ | `label` | "Keypad" | The group's accessible name |
206
+ | `variant`, `total`, `denominations`, `onTender` | `'standard'` | See [Cash](#cash) |
207
+ | `className` | | On the root |
208
+
209
+ Types: `KeypadProps`, `KeypadEntry`, `KeypadOperation`, `KeypadVariant`, `KeypadSettlement`, `KeypadBinding`.
210
+
211
+ ## Sizing and styles
212
+
213
+ The keypad sizes by its own width with the calculator's bands (compact under
214
+ 360px, wider keys from 600px) and is at most 24rem wide. `size="large"` gives
215
+ taller keys. Colours and shapes follow the theme and dark mode through
216
+ `styles.css`; the key rules are shared with the calculator in `calculator.css`.
217
+
218
+ ## Accessibility
219
+
220
+ - The keys are native buttons in one `role="group"` (one tab stop, arrow keys
221
+ over the grid), at least 44px on a coarse pointer at 360px and 320px.
222
+ - Each key's glyph is decoration; its name is spoken (Plus, Minus, "00, double zero",
223
+ Clear all, Enter, "Amount €20.00" in English, "Amount 20,00 €" in French). `commitLabel` replaces the commit key's name.
224
+ - Results and, in cash, the change or amount still due are announced in a
225
+ polite live region; the display itself is not live, so typing is not read digit by digit.
226
+ - Wording is the `material.keypad.*` and `material.calculator.keys.*` strings
227
+ (English and French shipped), overridable through `I18nProvider` `resources`.
228
+
229
+ ## Troubleshooting
230
+
231
+ See [the keypad entries](../troubleshooting.md#keypad): unstyled keys, a keyboard
232
+ that types nothing, a Tender
233
+ key that does nothing, and a digit that is refused.
@@ -2,7 +2,9 @@
2
2
 
3
3
  This page covers building PDF documents with `@nxgt/material/print`, turning
4
4
  them into a download, a new tab or a server response in one call, and
5
- shipping them from a browser or from a server bundle that must not crash.
5
+ shipping them from a browser or from a server bundle that must not crash. It
6
+ also covers [`toCsv` and `csvResponse`](#a-csv-export-tocsv-and-csvresponse),
7
+ the CSV export beside `pdfResponse`.
6
8
 
7
9
  ```bash
8
10
  bun add @react-pdf/renderer@^4.9 react-pdf-tailwind
@@ -619,6 +621,8 @@ export function DownloadInvoice({ invoice }: { invoice: Invoice }) {
619
621
  | `openPdf(doc \| blob, options?)` | `Promise<void>`: the file opens in a new tab | browser |
620
622
  | `usePdfFile(doc \| null, options?)` | `{ blob, url, loading, error, refresh }` | browser (a React hook) |
621
623
  | `pdfResponse(doc, options?)` | `Promise<Response>` with the PDF and its headers | Node 18+, Bun |
624
+ | `toCsv(rows, columns, options?)` | `string`: the CSV text | everywhere |
625
+ | `csvResponse(text, fileName?, options?)` | `Response` with the CSV and its headers | Node 18+, Bun |
622
626
  | `renderPdfBuffer(doc, options?)` | `Promise<Uint8Array>` | browser, Node 18+, Bun |
623
627
  | `renderPdfBlob(doc, options?)` | `Promise<Blob>` of type `application/pdf` | browser, Node 18+, Bun |
624
628
  | `renderPdfStream(doc, options?)` | `Promise<ReadableStream<Uint8Array>>` (a Web stream) | Node 18+, Bun |
@@ -825,6 +829,50 @@ of your own. A `PdfReader` in the browser opens the route by its URL, with
825
829
  <PdfReader file={{ url: `/invoices/${id}/pdf`, httpHeaders: { Authorization: `Bearer ${token}` } }} fileName={`${number}.pdf`} />
826
830
  ```
827
831
 
832
+ ### A CSV export: `toCsv` and `csvResponse`
833
+
834
+ A list exported to a spreadsheet is the same job as a PDF: text, then a
835
+ download response. `toCsv` writes the text (RFC 4180) and `csvResponse` wraps
836
+ it, with the same `Content-Disposition` helper as `pdfResponse`. Both are pure
837
+ and server-safe, and add nothing to a client bundle.
838
+
839
+ ```ts
840
+ import { csvResponse, toCsv } from '@nxgt/material/print';
841
+
842
+ export async function loader() {
843
+ const items = await getItems();
844
+ const text = toCsv(
845
+ items,
846
+ [
847
+ { header: 'Name', value: (item) => item.name },
848
+ { header: 'Amount', value: (item) => item.amount },
849
+ ],
850
+ { separator: ';', bom: true },
851
+ );
852
+ return csvResponse(text, 'items.csv');
853
+ }
854
+ ```
855
+
856
+ | `toCsv` option | Type | Default | Effect |
857
+ | --- | --- | --- | --- |
858
+ | `separator` | `','` \| `';'` \| `'\t'` | `','` | `';'` is what spreadsheets in comma-decimal locales (French, German) expect |
859
+ | `bom` | `boolean` | `false` | Prefixes a UTF-8 byte order mark, which Excel needs to read accents |
860
+
861
+ - The first line is the headers (`columns[].header`); each row then gives one
862
+ line from `columns[].value(row)`. Lines end with CRLF, with none after the last.
863
+ - A field holding the separator, a quote, CR or LF is quoted and its quotes
864
+ doubled. `null` and `undefined` are empty fields; numbers and booleans are
865
+ written with `String()` and a `Date` as ISO 8601. Format amounts and dates in
866
+ `value` (see `formatAmount`) when the sheet should show them by locale.
867
+ - A cell starting with `=`, `+`, `-` or `@` is written as it is: a
868
+ spreadsheet may read it as a formula, so prefix user-typed text yourself when
869
+ the file is opened by someone other than its author.
870
+
871
+ `csvResponse(text, fileName?, options?)` answers with `Content-Type: text/csv;
872
+ charset=utf-8`, an attachment `Content-Disposition` (an ASCII `filename` and a
873
+ UTF-8 `filename*`, `.csv` added when missing) and `Cache-Control: private,
874
+ no-store`, which `options.headers` can override.
875
+
828
876
  ### Bytes and streams: `renderPdfBuffer`, `renderPdfStream`
829
877
 
830
878
  For anything that is not a response (an email attachment, an upload to object
@@ -896,6 +944,8 @@ function renderPdfStream(input: PdfInput, options?: PdfRenderInputOptions): Prom
896
944
  function downloadPdf(input: PdfInput | Blob, options?: PdfOutputOptions): Promise<void>;
897
945
  function openPdf(input: PdfInput | Blob, options?: PdfOutputOptions): Promise<void>;
898
946
  function pdfResponse(input: PdfInput, options?: PdfResponseOptions & PdfRenderInputOptions): Promise<Response>;
947
+ function toCsv<Row>(rows: readonly Row[], columns: readonly CsvColumn<Row>[], options?: CsvOptions): string;
948
+ function csvResponse(text: string, fileName?: string, options?: { headers?: HeadersInit }): Response;
899
949
  function contentDisposition(disposition: 'inline' | 'attachment', fileName: string): string;
900
950
  function usePdfFile(input: PdfInput | null, options?: PdfRenderInputOptions): PdfFileState;
901
951
 
package/docs/roadmap.md CHANGED
@@ -257,6 +257,9 @@ version they shipped in, and planned entries may name the major they target.
257
257
 
258
258
  ## Later
259
259
 
260
+ - **Keypad for Vue** — the `Keypad`, its field bindings and the cash variant on
261
+ `@nxgt/material-vue`, on the same engine and stylesheet
262
+ ([#2830](https://github.com/softistx/nxgt-material/issues/2830)).
260
263
  - **A lighter first load for the app shell** — `ActivityLayout` pulls about
261
264
  831 KB gzipped through the library's single shared chunk; splitting it so the
262
265
  shell costs tens of KB at first load, the rest loaded where a page uses it
@@ -379,6 +382,24 @@ version they shipped in, and planned entries may name the major they target.
379
382
 
380
383
  ## Shipped
381
384
 
385
+ - **Keypad (3.103.0)** — `@nxgt/material/keypad` is a touch and keyboard numeric keypad
386
+ you can use outside the Calculator widget, for quantity, price, percentage
387
+ and cash-tendered entry with change. Entry is a decimal or minor units, the
388
+ display is optional, and the keyboard is on `'focus'` by default (opt in to
389
+ `'global'`). `useKeypadNumber` and `useKeypadQuantity` bind it to
390
+ `NumberField`, `PercentField` and `CurrencyField`. A `cash` variant reports
391
+ `onTender(tendered, settlement)` and leaves the charge to your app. It is
392
+ styled by `calculator.css`, with no separate stylesheet
393
+ ([guide](guides/keypad.md)).
394
+ - **A status chip that opens its choices (3.103.0)** — `StatusMenu` shows a record's
395
+ status as a tone-and-icon chip and changes it in one tap: a menu on a wide
396
+ screen, a bottom sheet on a phone. Statuses not reachable from the current
397
+ one are greyed with the reason, a locked or read-only record explains why,
398
+ a slow save keeps the old status with a spinner and announces a failure,
399
+ and a choice can ask for confirmation. `StatusMenuFormField` binds it to a
400
+ form, and `@nxgt/material-core/status-menu` holds the model; the status
401
+ board recipe's detail sheet uses it
402
+ ([#1653](https://github.com/softistx/nxgt-material/issues/1653)).
382
403
  - **Archived status filter (3.99.0)** — `archivedStatusField()` adds an active /
383
404
  archived / all choice to a table's filter model as a segmented chip group
384
405
  that is always set; `parseArchivedStatus` reads it in a loader and
@@ -392,7 +392,7 @@ words in your error.
392
392
  - [An empty gap under the `ServerTable` band while nothing is selected](#an-empty-gap-under-the-servertable-band-while-nothing-is-selected)
393
393
  - [The focus falls to the page after a `DataTable` bulk action](#the-focus-falls-to-the-page-after-a-datatable-bulk-action)
394
394
  - [A test's `getByText('No data available')` finds two elements after a change](#a-tests-getbytextno-data-available-finds-two-elements-after-a-change)
395
- - [`` [@nxgt/material] DataTable: `virtual` needs `maxHeight`; the rows are not virtualized. ``](#nxgtmaterial-datatable-virtual-needs-maxheight-the-rows-are-not-virtualized)
395
+ - [`` [@nxgt/material] DataTable: `virtual` needs `maxHeight` or `maxHeightClassName`; the rows are not virtualized. ``](#nxgtmaterial-datatable-virtual-needs-maxheight-or-maxheightclassname-the-rows-are-not-virtualized)
396
396
  - [`` [@nxgt/material] DataTable: `renderExpandedRow` is ignored while `virtual` is on. ``](#nxgtmaterial-datatable-renderexpandedrow-is-ignored-while-virtual-is-on)
397
397
  - [`` [@nxgt/material] DataTable: `virtual` does not apply to the `cards` layout and is ignored. ``](#nxgtmaterial-datatable-virtual-does-not-apply-to-the-cards-layout-and-is-ignored)
398
398
  - [`` [@nxgt/material] DataTable: `layout="cards"` needs `renderCard`; each card shows its first column. ``](#nxgtmaterial-datatable-layoutcards-needs-rendercard-each-card-shows-its-first-column)
@@ -495,6 +495,13 @@ words in your error.
495
495
  - [`The point of sale's Charge button stays off`](#the-point-of-sales-charge-button-stays-off)
496
496
  - [`The Financial calculator shows no currency`](#the-financial-calculator-shows-no-currency)
497
497
 
498
+ **Keypad**
499
+
500
+ - [`The keypad is unstyled`](#the-keypad-is-unstyled)
501
+ - [`A keypad key types nothing on the page`](#a-keypad-key-types-nothing-on-the-page)
502
+ - [`A digit is refused by the keypad`](#a-digit-is-refused-by-the-keypad)
503
+ - [`The cash keypad's Tender key does nothing`](#the-cash-keypads-tender-key-does-nothing)
504
+
498
505
  **Barcodes**
499
506
 
500
507
  - [`Code 128 holds ASCII only (0-127)`](#code-128-holds-ascii-only-0-127)
@@ -504,6 +511,10 @@ words in your error.
504
511
  - [`<name> must be a number (millimetres) or a length such as "50mm"`](#name-must-be-a-number-millimetres-or-a-length-such-as-50mm)
505
512
  - [`Unknown symbology "x": use "code128" or "datamatrix"`](#unknown-symbology-x-use-code128-or-datamatrix)
506
513
 
514
+ **Status menu**
515
+
516
+ - [`[status-menu] the value "…" matches no choice`](#status-menu-the-value--matches-no-choice)
517
+
507
518
  ---
508
519
 
509
520
  ## Install
@@ -7215,14 +7226,15 @@ expect(within(empty).getByText('No data available')).toBeVisible();
7215
7226
  const status = canvasElement.querySelector<HTMLElement>('[data-slot="data-table-status"]')!;
7216
7227
  expect(status).toHaveTextContent('No data available');
7217
7228
  ```
7218
- ### `` [@nxgt/material] DataTable: `virtual` needs `maxHeight`; the rows are not virtualized. ``
7229
+ ### `` [@nxgt/material] DataTable: `virtual` needs `maxHeight` or `maxHeightClassName`; the rows are not virtualized. ``
7219
7230
 
7220
- **When:** in development, a `DataTable` with `virtual` and no `maxHeight` logs this once, and renders its rows paged, as without `virtual`.
7231
+ **When:** in development, a `DataTable` with `virtual` and neither `maxHeight` nor `maxHeightClassName` logs this once, and renders its rows paged, as without `virtual`.
7221
7232
  **Why:** virtual rows scroll inside the table's own box; with no height cap there is no box to scroll, and the page would hold every row.
7222
- **Fix:** give the table a height, in pixels or any CSS length:
7233
+ **Fix:** give the table a height, in pixels or any CSS length, or a Tailwind class (no inline `style`, for a strict CSP):
7223
7234
 
7224
7235
  ```tsx
7225
7236
  <DataTable data={people} columns={columns} virtual maxHeight="70vh" />
7237
+ <DataTable data={people} columns={columns} virtual maxHeightClassName="max-h-[28rem]" />
7226
7238
  ```
7227
7239
 
7228
7240
  ### `` [@nxgt/material] DataTable: `renderExpandedRow` is ignored while `virtual` is on. ``
@@ -8422,6 +8434,39 @@ threw (offline, a 500). **Try again** calls it again. Pass the `signal` to your
8422
8434
 
8423
8435
  ---
8424
8436
 
8437
+ ## Keypad
8438
+
8439
+ ### `The keypad is unstyled`
8440
+
8441
+ The keys are plain buttons in a column with no shape.
8442
+
8443
+ **Why:** the keypad has no stylesheet of its own; the Calculator's `calculator.css` styles it, and `styles.css` does not import that.
8444
+ **Fix:** import it once, after `styles.css`: `@import "@nxgt/material/calculator.css";` (or `import '@nxgt/material/calculator.css'`).
8445
+
8446
+ ### `A keypad key types nothing on the page`
8447
+
8448
+ **When:** you press a digit on the physical keyboard and nothing is entered.
8449
+ **Why:** `keyboard` is `'focus'` by default: the keys work when focus is inside the keypad (tap a key or Tab to it first), and never in `false`. A key the keypad does not offer (`+` without `operations`, `.` at `fractionDigits={0}` or in `minor` entry) is left to the page.
8450
+ **Fix:** for a screen that is the keypad (a till), set `keyboard="global"`; it still never takes a key typed in a field.
8451
+
8452
+ ```tsx
8453
+ <Keypad keyboard="global" />
8454
+ ```
8455
+
8456
+ ### `A digit is refused by the keypad`
8457
+
8458
+ Not an error. A digit that would pass `max`, or add a decimal past `fractionDigits` (or the `currency`'s decimals), is ignored; `min` never refuses a digit, it holds the value when the commit key is pressed.
8459
+
8460
+ ### `The cash keypad's Tender key does nothing`
8461
+
8462
+ **When:** `variant="cash"` and the commit key (Tender) or Enter calls nothing.
8463
+ **Why:** `onTender` is called only for a valid, non-negative `total` and a non-empty amount. A missing, negative or non-numeric `total` does nothing; `onCommit` is never called in this variant.
8464
+ **Fix:** pass `total` as a number or exact decimal text and use `onTender`.
8465
+
8466
+ ```tsx
8467
+ <Keypad variant="cash" total="17.35" onTender={(tendered, settlement) => pay(tendered, settlement)} />
8468
+ ```
8469
+
8425
8470
  ## Barcodes
8426
8471
 
8427
8472
  ### `Code 128 holds ASCII only (0-127)`
@@ -8475,6 +8520,25 @@ Raised by `@nxgt/material/barcode` while rendering. `symbology` is neither value
8475
8520
 
8476
8521
  ---
8477
8522
 
8523
+ ## Status menu
8524
+
8525
+ ### `[status-menu] the value "…" matches no choice`
8526
+
8527
+ A development warning from `StatusMenu` and `StatusMenuFormField`. `value` is not among `choices`: an empty string (a form with no initial status) or a status the server sent that the app did not list. The chip then shows an empty value.
8528
+
8529
+ Give an initial status, or add the choice.
8530
+
8531
+ ```tsx
8532
+ const form = useForm({ defaultValues: { status: 'open' } });
8533
+
8534
+ const statuses: StatusChoice<'open' | 'archived'>[] = [
8535
+ { value: 'open', label: 'Open' },
8536
+ { value: 'archived', label: 'Archived' }, // a status the server can send
8537
+ ];
8538
+ ```
8539
+
8540
+ ---
8541
+
8478
8542
  ## Reporting a bug
8479
8543
 
8480
8544
  If an error comes from inside this package and none of the entries above
@@ -133,6 +133,53 @@
133
133
  align-self: start;
134
134
  }
135
135
 
136
+ /* A tile with an action wraps instead of overflowing: a flex line holds the
137
+ * icon, the title and the action; the title may shrink to --ac-title-min
138
+ * (4.5rem), so an action that does not fit beside it (a StatusMenu chip in
139
+ * a narrow tile) drops under the title, where an icon button still fits
140
+ * beside it. The status, description, meta and footer are full-width lines.
141
+ * No query, no measuring. */
142
+ .nxgt-action-card[data-size="tile"][data-has-action] {
143
+ --ac-icon-offset: 0px;
144
+ display: flex;
145
+ flex-wrap: wrap;
146
+ align-items: flex-start;
147
+ align-content: flex-start;
148
+ }
149
+ .nxgt-action-card[data-size="tile"][data-has-action][data-icon] {
150
+ --ac-icon-offset: 44px;
151
+ }
152
+ .nxgt-action-card[data-size="tile"][data-has-action]
153
+ > .nxgt-action-card-icon {
154
+ flex: none;
155
+ }
156
+ .nxgt-action-card[data-size="tile"][data-has-action] .nxgt-action-card-title {
157
+ order: 1;
158
+ flex: 1 1 var(--ac-title-min, 4.5rem);
159
+ }
160
+ .nxgt-action-card[data-size="tile"][data-has-action]
161
+ > .nxgt-action-card-action {
162
+ order: 2;
163
+ flex: none;
164
+ max-inline-size: 100%;
165
+ }
166
+ .nxgt-action-card[data-size="tile"][data-has-action]
167
+ > .nxgt-action-card-indicator {
168
+ order: 3;
169
+ flex: none;
170
+ margin-inline-start: auto;
171
+ }
172
+ .nxgt-action-card[data-size="tile"][data-has-action] .nxgt-action-card-status,
173
+ .nxgt-action-card[data-size="tile"][data-has-action]
174
+ .nxgt-action-card-description,
175
+ .nxgt-action-card[data-size="tile"][data-has-action] .nxgt-action-card-meta,
176
+ .nxgt-action-card[data-size="tile"][data-has-action]
177
+ .nxgt-action-card-footer {
178
+ order: 4;
179
+ flex: 0 0 calc(100% - var(--ac-icon-offset));
180
+ margin-inline-start: var(--ac-icon-offset);
181
+ }
182
+
136
183
  @media (forced-colors: active) {
137
184
  .nxgt-action-card:has(.nxgt-action-card-button:focus-visible) {
138
185
  outline: 3px solid CanvasText;
@@ -1,5 +1,5 @@
1
1
  /* Chip `tonal` and `filled`, "soft": no outline at all. A low tonal fill that reads as a
2
- * chip on any surface; selected = the library's primary tint, a hairline
2
+ * chip on any surface; selected = the library's primary tint (a toggle chip of a tone: the filled tone, last block), a hairline
3
3
  * ring in primary-text and a check (the ≥3:1 cue), the label unchanged. In
4
4
  * dark the tint is mixed from --primary-text, the light end of the primary,
5
5
  * 56 % at rest and 58 % on hover, and the label is white: in all 14 themes
@@ -91,7 +91,8 @@
91
91
  * (--nxgt-tone-text, 4.5:1 or more on it). It reads --nxgt-tint-status, not
92
92
  * --nxgt-tint-container: the container of a calm zone (calm.css) fades to
93
93
  * the surface through its flags, and a status chip keeps its tint there. Selected
94
- * keeps the primary tint and the foreground label above. */
94
+ * keeps the primary tint and the foreground label above, except a toggle chip of a
95
+ * tone (the last block). */
95
96
  @layer components {
96
97
  .nxgt-chip[data-look="soft"]:not([data-tone="neutral"]) {
97
98
  --chip-status-base: var(--nxgt-tone);
@@ -121,3 +122,27 @@
121
122
  color: var(--nxgt-tone-text);
122
123
  }
123
124
  }
125
+
126
+ /* A coloured toggle chip, selected: the filled tone (--nxgt-tone) under the
127
+ * tone's on-colour (--nxgt-tone-on, 4.5:1 or more, light and dark), the ring
128
+ * in the on-colour so the edge holds on the page. The check stays the
129
+ * non-colour cue. A status chip (`active`, not a toggle) keeps the tint. */
130
+ @layer components {
131
+ .nxgt-chip[data-look="soft"]:not([data-tone="neutral"])[data-selected]:is(
132
+ [aria-checked="true"],
133
+ [aria-pressed="true"]
134
+ ) {
135
+ --chip-on: var(--nxgt-tone);
136
+ --chip-on-hover: color-mix(
137
+ in oklab,
138
+ var(--nxgt-tone-on) 10%,
139
+ var(--nxgt-tone)
140
+ );
141
+ --chip-on-ring: color-mix(
142
+ in oklab,
143
+ var(--nxgt-tone-on) 55%,
144
+ var(--nxgt-tone)
145
+ );
146
+ --chip-on-text: var(--nxgt-tone-on);
147
+ }
148
+ }
@@ -4,7 +4,7 @@
4
4
  [data-slot="code-block"][data-scheme="dark"] {
5
5
  --code-bg: #222436;
6
6
  --code-fg: #c8d3f5;
7
- --code-comment: #7a88cf;
7
+ --code-comment: #8f9ad6;
8
8
  --code-blue: #82aaff;
9
9
  --code-cyan: #86e1fc;
10
10
  --code-green: #c3e88d;
@@ -19,15 +19,15 @@
19
19
  [data-slot="code-block"][data-scheme="light"] {
20
20
  --code-bg: #f5f7ff;
21
21
  --code-fg: #383a51;
22
- --code-comment: #7a88cf;
23
- --code-blue: #3e68d7;
24
- --code-cyan: #0b8ec2;
25
- --code-green: #4db380;
26
- --code-orange: #c44c22;
27
- --code-yellow: #ac8300;
28
- --code-red: #c53b40;
29
- --code-magenta: #7a5af5;
30
- --code-pink: #b04d9a;
22
+ --code-comment: #4356ba;
23
+ --code-blue: #2a56ca;
24
+ --code-cyan: #08658a;
25
+ --code-green: #2d6a4c;
26
+ --code-orange: #a33f1c;
27
+ --code-yellow: #765a00;
28
+ --code-red: #ac3337;
29
+ --code-magenta: #5d36f3;
30
+ --code-pink: #924080;
31
31
  --code-selection: #d4daf0;
32
32
  }
33
33
 
@@ -73,6 +73,15 @@
73
73
  .nxgt-collapsible-trigger[data-variant="tonal"][data-state="open"] {
74
74
  border-radius: 0.75rem;
75
75
  }
76
+ /* `flush` (text only): label at the left edge, the parent's font, so the
77
+ * trigger can sit in a heading. */
78
+ .nxgt-collapsible-trigger[data-variant="text"][data-flush] {
79
+ min-height: 1.5rem;
80
+ padding: 0.25rem 0.25rem 0.25rem 0;
81
+ border-radius: 0.5rem;
82
+ font: inherit;
83
+ letter-spacing: inherit;
84
+ }
76
85
  .nxgt-collapsible-trigger:hover:not(:disabled) {
77
86
  background-color: color-mix(in oklab, var(--ct-fg) 8%, var(--ct-bg));
78
87
  }
@@ -32,6 +32,9 @@
32
32
  .nxgt-calc-key[data-span="3"] {
33
33
  grid-column: span 3;
34
34
  }
35
+ .nxgt-calc-key[data-span="9"] {
36
+ grid-column: span 9;
37
+ }
35
38
  .nxgt-calc-key[data-span="6"] {
36
39
  grid-column: span 6;
37
40
  }