@nxgt/material 3.86.2 → 3.88.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 (113) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +2 -0
  3. package/dist/{alert-dialog-DZnt8PYE.js → alert-dialog-CEGs7LMN.js} +1 -1
  4. package/dist/{async-slice-D3OdV2BS.js → async-slice-CR0M95Jq.js} +1 -1
  5. package/dist/{button-MAL5an7L.js → button-h9hXBEQB.js} +1 -1
  6. package/dist/{calculator-C3a8YMwl.js → calculator-Dn_KL0b0.js} +10 -10
  7. package/dist/calculator-field.js +6 -6
  8. package/dist/{calculator-segment-BigMpjhn.js → calculator-segment-DMFptsDf.js} +1 -1
  9. package/dist/calculator.js +1 -1
  10. package/dist/charts.js +3 -3
  11. package/dist/{chip-DKAKoNxq.js → chip-C-RAjar5.js} +2 -2
  12. package/dist/{code-editor-B6dFQSUJ.js → code-editor-DApeIOZ3.js} +4 -4
  13. package/dist/code-editor.js +4 -4
  14. package/dist/{code-language-items-CXEMiWoF.js → code-language-items-CGtYfc1l.js} +2 -2
  15. package/dist/components/components.css +2 -0
  16. package/dist/components/ui/collections/marking-list/marking-list.css +16 -0
  17. package/dist/components/ui/primitives/toggle-group/toggle-group-connected.css +1 -1
  18. package/dist/components/ui/primitives/toggle-group/toggle-group-segmented.css +1 -1
  19. package/dist/components/ui/primitives/toggle-group/toggle-group-separate.css +1 -1
  20. package/dist/components/ui/primitives/toggle-group/toggle-group.css +12 -1
  21. package/dist/{components-BTukNsPn.js → components-BIATLurA.js} +16555 -15979
  22. package/dist/components.js +37 -37
  23. package/dist/{conversion-variant-BT5n4-jm.js → conversion-variant-Cf0sEmnS.js} +3 -3
  24. package/dist/{currency-field-t41sFYg_.js → currency-field-cwC7cqUE.js} +4 -4
  25. package/dist/{dialog-BjKiRXJg.js → dialog-Bul68q8u.js} +3 -3
  26. package/dist/dnd.js +1 -1
  27. package/dist/{dropdown-menu-IRFp4CH9.js → dropdown-menu-xUUE5X6T.js} +1 -1
  28. package/dist/editor.js +7 -7
  29. package/dist/{empty-state-C70prDDr.js → empty-state-CCRfjt0n.js} +1 -1
  30. package/dist/{financial-variant-BIpzs8IL.js → financial-variant-CYgRZzKT.js} +5 -5
  31. package/dist/{hooks-CjO11MS7.js → hooks-Dzc6xz5t.js} +2 -2
  32. package/dist/hooks.js +2 -2
  33. package/dist/{hover-card-cU_aJa18.js → hover-card-CZLFQwZv.js} +1 -1
  34. package/dist/i18n.js +3 -3
  35. package/dist/{icon-badge-D3KO6uEQ.js → icon-badge-0Xo9q7Ey.js} +1 -1
  36. package/dist/{icon-button-DrqNk1Ww.js → icon-button-DE1EcO2-.js} +1 -1
  37. package/dist/lib/components/ui/collections/filter/sections/filter-sections-classes.d.ts +4 -3
  38. package/dist/lib/components/ui/collections/index.d.ts +1 -0
  39. package/dist/lib/components/ui/collections/marking-list/index.d.ts +2 -0
  40. package/dist/lib/components/ui/collections/marking-list/marking-list.d.ts +11 -0
  41. package/dist/lib/components/ui/collections/marking-list/marking-list.utils.d.ts +18 -0
  42. package/dist/lib/components/ui/collections/marking-list/partials/marking-row-item.d.ts +16 -0
  43. package/dist/lib/components/ui/collections/marking-list/partials/marking-segments.d.ts +20 -0
  44. package/dist/lib/components/ui/collections/marking-list/partials/marking-set-remaining.d.ts +11 -0
  45. package/dist/lib/components/ui/collections/marking-list/partials/marking-tally.d.ts +14 -0
  46. package/dist/lib/components/ui/collections/marking-list/types.d.ts +74 -0
  47. package/dist/lib/components/ui/collections/marking-list/use-marking-expanded.d.ts +9 -0
  48. package/dist/lib/components/ui/collections/marking-list/use-marking-focus.d.ts +22 -0
  49. package/dist/lib/components/ui/collections/marking-list/use-marking-handlers.d.ts +11 -0
  50. package/dist/lib/components/ui/collections/marking-list/use-tally-speech.d.ts +16 -0
  51. package/dist/lib/components/ui/collections/page-footer/page-footer.d.ts +5 -1
  52. package/dist/lib/components/ui/collections/server-table/index.d.ts +1 -1
  53. package/dist/lib/components/ui/collections/server-table/server-table-paging.d.ts +8 -0
  54. package/dist/lib/components/ui/collections/server-table/types-paging.d.ts +6 -0
  55. package/dist/lib/components/ui/dates/half-day-range-field/half-day-end.d.ts +21 -0
  56. package/dist/lib/components/ui/dates/half-day-range-field/half-day-ends.d.ts +11 -0
  57. package/dist/lib/components/ui/dates/half-day-range-field/half-day-range-field.d.ts +39 -0
  58. package/dist/lib/components/ui/dates/half-day-range-field/half-day-range-form-field.d.ts +3 -0
  59. package/dist/lib/components/ui/dates/half-day-range-field/half-day-range.d.ts +25 -0
  60. package/dist/lib/components/ui/dates/half-day-range-field/index.d.ts +3 -0
  61. package/dist/lib/components/ui/dates/half-day-range-field/use-half-day-draft.d.ts +30 -0
  62. package/dist/lib/components/ui/dates/index.d.ts +1 -0
  63. package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
  64. package/dist/lib/i18n/resources/en/en.d.ts +17 -0
  65. package/dist/lib/i18n/resources/en/material.json.d.ts +17 -0
  66. package/dist/lib/i18n/resources/fr/fr.d.ts +17 -0
  67. package/dist/lib/i18n/resources/fr/material.json.d.ts +17 -0
  68. package/dist/lib/i18n/resources/resources.d.ts +34 -0
  69. package/dist/lib/i18n/translator.d.ts +1 -1
  70. package/dist/lib.js +1 -1
  71. package/dist/main.js +53 -53
  72. package/dist/map.js +7 -7
  73. package/dist/material.css +1 -1
  74. package/dist/pdf-reader.js +1 -1
  75. package/dist/{percent-field-D47IlaPF.js → percent-field-BFwxazf1.js} +1 -1
  76. package/dist/{picker-surface-Cg4V4-E0.js → picker-surface-DkfTv_ft.js} +4 -4
  77. package/dist/{popover-Dzk4-l9U.js → popover-BM6xApJd.js} +1 -1
  78. package/dist/{pos-types-JOXMY4Y2.js → pos-types-CpTpT4vE.js} +1 -1
  79. package/dist/{pos-variant-B2mS8bMB.js → pos-variant-Bs5BUB1L.js} +4 -4
  80. package/dist/print.js +1 -1
  81. package/dist/{programmer-variant-C_Tl0P4_.js → programmer-variant-B1eI1fd0.js} +2 -2
  82. package/dist/{public-ByAsqBOf.js → public-CX7ik5Yg.js} +10 -10
  83. package/dist/{rich-code-block-CJAfqk6b.js → rich-code-block-CdVtxD75.js} +5 -5
  84. package/dist/{scientific-variant-CskM4MLV.js → scientific-variant-rfvJKxB4.js} +2 -2
  85. package/dist/{sortable-provider-ICS_vkLA.js → sortable-provider-BopzsciJ.js} +2 -2
  86. package/dist/{spinner-CweqX5A9.js → spinner-DcW21pN1.js} +1 -1
  87. package/dist/{tabs-95Y2LmnV.js → tabs-CdoBFtWx.js} +1 -1
  88. package/dist/theme-picker.js +2 -2
  89. package/dist/{theme-toggle-WAFkncYx.js → theme-toggle-Ch3GkDDU.js} +1 -1
  90. package/dist/{toolbar-items-BsgzHSNf.js → toolbar-items-CZ1lJXZZ.js} +4 -4
  91. package/dist/{translate-material-DTIAPmlU.js → translate-material-BrYBBZ66.js} +1 -1
  92. package/dist/{translator-BRZNhsy5.js → translator-DoVsjUff.js} +34 -0
  93. package/dist/{use-chat-composer-CleSUh_y.js → use-chat-composer-BjKhuLfT.js} +15 -15
  94. package/dist/{use-heading-spy-D8WbHJuK.js → use-heading-spy-CGTTRV2N.js} +4 -4
  95. package/dist/{use-material-translation-CCy1Me0h.js → use-material-translation-BdrItuwY.js} +1 -1
  96. package/dist/{use-return-focus-BA8G1qVU.js → use-return-focus-Cqzvj9R0.js} +1 -1
  97. package/docs/components/README.md +2 -1
  98. package/docs/components/data-table-filter-and-lists.md +103 -0
  99. package/docs/components/date-and-time.md +111 -0
  100. package/docs/components/primitives.md +4 -1
  101. package/docs/guides/forms.md +1 -0
  102. package/docs/guides/i18n.md +7 -0
  103. package/docs/guides/theming.md +1 -1
  104. package/docs/previews/data-table-filter-lists-markinglist.webp +0 -0
  105. package/docs/previews/date-time-halfdayrangefield.webp +0 -0
  106. package/docs/roadmap.md +7 -2
  107. package/lib/components/components.css +2 -0
  108. package/lib/components/ui/collections/marking-list/marking-list.css +16 -0
  109. package/lib/components/ui/primitives/toggle-group/toggle-group-connected.css +1 -1
  110. package/lib/components/ui/primitives/toggle-group/toggle-group-segmented.css +1 -1
  111. package/lib/components/ui/primitives/toggle-group/toggle-group-separate.css +1 -1
  112. package/lib/components/ui/primitives/toggle-group/toggle-group.css +12 -1
  113. package/package.json +2 -2
@@ -20,6 +20,7 @@ each change and, where one is left, the prop that keeps the old behaviour.
20
20
  | <img src="../previews/date-time-timefield.webp" width="96" alt=""> | [TimeField](#timefield) | A time typed in segments or picked in a `TimePicker` (a bottom sheet on a phone) |
21
21
  | <img src="../previews/date-time-timepicker.webp" width="96" alt=""> | [TimePicker](#timepicker) | Hour and minute tiles (or columns) with AM / PM |
22
22
  | <img src="../previews/date-time-durationfield.webp" width="96" alt=""> | [DurationField](#durationfield) | Hours and minutes as a total in minutes, typed on one line: "1 h 29 min" |
23
+ | <img src="../previews/date-time-halfdayrangefield.webp" width="96" alt=""> | [HalfDayRangeField](#halfdayrangefield) | A range whose two ends are each a day plus morning or afternoon, ordered on the half |
23
24
  | <img src="../previews/date-time-minimonth.webp" width="96" alt=""> | [MiniMonth](#minimonth) | Compact month navigator with event dots and a keyboard date grid |
24
25
  | <img src="../previews/date-time-nowindicator.webp" width="96" alt=""> | [NowIndicator](#nowindicator) | The current-time line of a day schedule, moving each minute |
25
26
 
@@ -900,6 +901,116 @@ With `variant="fields"` the inputs are `textbox`es with the same names.
900
901
 
901
902
  ---
902
903
 
904
+ ## HalfDayRangeField
905
+
906
+ ![HalfDayRangeField](../previews/date-time-halfdayrangefield.webp)
907
+
908
+ A range counted to the half day (a long absence, a closure): each end is a day
909
+ (a `DateField`, typed or picked) plus a morning / afternoon toggle, and the end
910
+ is ordered on the half. The morning of day D is not after the afternoon of day
911
+ D; the afternoon of D to the morning of D is refused.
912
+
913
+ ```tsx
914
+ import { useState } from 'react';
915
+ import { type HalfDayRange, HalfDayRangeField } from '@nxgt/material/components';
916
+
917
+ function Absence() {
918
+ const [value, setValue] = useState<HalfDayRange>();
919
+ return <HalfDayRangeField label="Absence" value={value} onValueChange={setValue} />;
920
+ }
921
+ // value: { start: { date: Date, half: 'morning' | 'afternoon' }, end: { … } }
922
+ ```
923
+
924
+ With React Hook Form, `HalfDayRangeFormField` takes `control` and `name`, and its
925
+ rule refuses an unordered range in the form (the message is
926
+ `material.half-day-range.order`):
927
+
928
+ ```tsx
929
+ import { useForm } from 'react-hook-form';
930
+ import { type HalfDayRange, HalfDayRangeFormField } from '@nxgt/material/components';
931
+
932
+ type Values = { absence?: HalfDayRange };
933
+
934
+ function AbsenceForm() {
935
+ const { control, handleSubmit } = useForm<Values>();
936
+ return (
937
+ <form onSubmit={handleSubmit((values) => save(values.absence))}>
938
+ <HalfDayRangeFormField control={control} name="absence" label="Absence" required />
939
+ </form>
940
+ );
941
+ }
942
+ ```
943
+
944
+ Without a resolver the field checks the order itself (a `rules.validate` of your
945
+ own is kept beside it). React Hook Form ignores `rules` when a resolver is set,
946
+ so with zod state the order in the schema, or an end before the start is
947
+ submitted:
948
+
949
+ ```tsx
950
+ import { z } from 'zod';
951
+ import { type HalfDayRange, isHalfDayRangeOrdered } from '@nxgt/material/components';
952
+
953
+ const schema = z.object({
954
+ absence: z
955
+ .custom<HalfDayRange>((v) => v != null, { message: 'Choose both days.' })
956
+ .refine(isHalfDayRangeOrdered, { message: 'The end cannot be before the start.' }),
957
+ });
958
+ ```
959
+
960
+ | Prop | Type | Default | What it does |
961
+ | --- | --- | --- | --- |
962
+ | `value` | `HalfDayRange \| undefined` | | Controlled: `{ start: { date, half }, end: { date, half } }`; `date` is the local midnight of the day |
963
+ | `defaultValue` | `HalfDayRange` | | Uncontrolled: the range the field starts with and then keeps |
964
+ | `onValueChange` | `(value?: HalfDayRange) => void` | | The range as soon as both days are set, ordered or not; `undefined` while a day is missing |
965
+ | `label`, `helperText`, `error`, `required`, `disabled`, `className`, `id` | | | As on the other fields |
966
+ | `name` | `string` | | A test hook only (`data-pw` on the root, `.start` / `.end` on each end): the date inputs have no native `name`, so nothing is posted by a plain `<form>` |
967
+ | `minDate`, `maxDate` | `Date` | | Bound both ends; the end's calendar also starts at the start's day |
968
+ | `disabledDays`, `locale` | | | As on `DateField` (`timeZone` is not forwarded: days are the local ones) |
969
+
970
+ **What the field says in each state:**
971
+
972
+ | State | `onValueChange` | On screen |
973
+ | --- | --- | --- |
974
+ | A day is missing (start or end empty, or half typed) | `undefined` | The other end is kept; no message |
975
+ | Both days set, end on or after the start on the half | the range | |
976
+ | Both days set, end before the start on the half | the range, as entered | "The end cannot be before the start." under the field, `aria-invalid` on the halves, announced politely; in a form without a resolver, `HalfDayRangeFormField` makes it a field error (with zod, use the `refine` above) |
977
+ | `value` reset to `undefined` from outside (a form `reset`) | | Both days are emptied, the halves go back to morning and afternoon, unless the emitted value was already `undefined` (a half-typed start or a cleared end stays: give the field a `key` that changes on reset) |
978
+
979
+ A new range starts on the morning (start) and the afternoon (end), so one picked
980
+ day is a full day. Each end has its own accessible names ("Start date", "Start
981
+ half-day", "End date", "End half-day": `material.dates.start` / `end` and
982
+ `material.half-day-range.*`, see the [i18n guide](../guides/i18n.md)).
983
+
984
+ The value holds `Date`s: store a day with `format(date, 'yyyy-MM-dd')` (date-fns),
985
+ not `toISOString()`, which moves it to UTC and can shift the day.
986
+
987
+ ```tsx
988
+ import {
989
+ compareHalfDays,
990
+ halfDaysIn,
991
+ type HalfDayRange,
992
+ isHalfDayRangeOrdered,
993
+ } from '@nxgt/material/components';
994
+
995
+ const range: HalfDayRange = {
996
+ start: { date: new Date(2030, 5, 3), half: 'morning' },
997
+ end: { date: new Date(2030, 5, 4), half: 'afternoon' },
998
+ };
999
+
1000
+ isHalfDayRangeOrdered(range); // true: the end is not before the start on the half
1001
+ halfDaysIn(range); // 4: Monday morning and afternoon, Tuesday morning and afternoon
1002
+ compareHalfDays(range.start, range.end); // -3: negative when the first comes first
1003
+ ```
1004
+
1005
+ `HALF_DAYS` (`['morning', 'afternoon']`, in the order of the day) is exported too.
1006
+
1007
+ **Emits:** `HalfDayRange | undefined`.
1008
+ **Form wrapper:** `HalfDayRangeFormField` — `control` and `name`, see
1009
+ [forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
1010
+ **Storybook:** `Date & time/HalfDayRangeField` and `Date & time/HalfDayRangeField/Form field` · **Vue:** not yet (#2555 tracks the twin)
1011
+
1012
+ ---
1013
+
903
1014
  ## MiniMonth
904
1015
 
905
1016
  ![MiniMonth](../previews/date-time-minimonth.webp)
@@ -1142,7 +1142,10 @@ page's: Radix writes `dir="ltr"` on the group unless told. A `segmented`
1142
1142
  group of 9 items or more has no sliding pill: the checked item paints its
1143
1143
  own.
1144
1144
 
1145
- **Storybook:** `Primitives/ToggleGroup` · **Vue:** `ToggleGroup`, `ToggleGroupItem`
1145
+ Under a coarse pointer (a touch screen) every item is at least 44×44 px, in
1146
+ every variant, size and orientation, icon-only included.
1147
+
1148
+ **Storybook:** `Primitives/ToggleGroup`, `Guides/Accessibility/Touch targets/Toggle group` · **Vue:** `ToggleGroup`, `ToggleGroupItem`
1146
1149
 
1147
1150
  ---
1148
1151
 
@@ -523,6 +523,7 @@ or fails validation.
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
525
  | `DurationField` · `DurationFormField` | `number \| null`: total minutes (hours × 60 + minutes) | `null` |
526
+ | `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` |
526
527
 
527
528
  ### Selection & choice
528
529
 
@@ -397,6 +397,13 @@ dates", "Saisir les dates") on `DateRangeField`. Its hero says
397
397
  `material.dates.select-date` ("Select date", "Choisir la date") while the date
398
398
  is empty, `material.dates.select-dates` on `DateRangeField`, and the Cancel of
399
399
  a date-time picker reads `material.buttons.cancel` ("Cancel", "Annuler").
400
+ `HalfDayRangeField` names its ends `material.dates.start` / `end` and its two
401
+ halves `material.half-day-range.start-half` ("Start half-day", "Demi-journée de
402
+ début") and `end-half` ("End half-day", "Demi-journée de fin"); the toggles read
403
+ `material.half-day-range.morning` ("Morning", "Matin") and `afternoon`
404
+ ("Afternoon", "Après-midi"), and an end before the start says
405
+ `material.half-day-range.order` ("The end cannot be before the start.", "La fin
406
+ ne peut pas précéder le début.").
400
407
  `DateRangeField`'s preset list is a group named by
401
408
  `material.date-range-field.presets` ("Presets", "Plages prédéfinies"); its
402
409
  built-in presets read `material.date-range-field.preset.today`,
@@ -1167,7 +1167,7 @@ are, without growing what is painted (nothing changes for a mouse):
1167
1167
  `trailing` button of a `TextField`), an autocomplete chip's remove button
1168
1168
  (padding the background is clipped out of, with a negative margin, so the
1169
1169
  chip and the drawn disc keep their size), and a table's select-all and
1170
- row-select checkboxes, which sit in a 44x44 `<label>` that toggles them.
1170
+ row-select checkboxes, which sit in a 44x44 `<label>` that toggles them, `AppBreadcrumb` and `BreadcrumbLink` crumbs (at every width), the `FilterBar` / `ServerTable` search input (at both densities), and the move up / move down buttons of `RepeatableGroup` and `ChoiceListFormField` (`reorderable`).
1171
1171
  - **The label or row that toggles it is 44px tall** (and at least 44px wide):
1172
1172
  a `Checkbox` with a `label`, a `Switch` with a `placeholder` (its row), and
1173
1173
  a `RadioGroup` option. The box itself stays 20px or 32px: a finger anywhere
package/docs/roadmap.md CHANGED
@@ -167,6 +167,7 @@ version they shipped in, and planned entries may name the major they target.
167
167
  pill. What changes on upgrade:
168
168
  [The field look (2.x)](guides/field-look-2-x.md)
169
169
  - **`TotalsList`** — the on-screen `PrintTotals` (#2490): React only for now; Vue twin is #2492.
170
+ - **`MarkingList`** — people in rows with a shared segmented choice, a detail slot, locked rows, "set remaining to" and a live tally (#2547): React only for now; Vue twin is #2549.
170
171
 
171
172
  ## Next
172
173
 
@@ -254,8 +255,6 @@ version they shipped in, and planned entries may name the major they target.
254
255
  slot; the bar's `views` slot is planned with it. They are meant to replace
255
256
  the legacy `FilterPresets`. Tracked in [#1876](https://github.com/softistx/nxgt-material/issues/1876).
256
257
 
257
- - **Cursor trail cap** — `ServerTable` and `PageFooter` keep cursor URLs short: the trail is encoded once and capped by `maxTrail` (20 by default) with an exact page count, and `totalCount` becomes optional for APIs that do not count.
258
-
259
258
  ## Later
260
259
 
261
260
  - **A lighter first load for the app shell** — `ActivityLayout` pulls about
@@ -380,6 +379,12 @@ version they shipped in, and planned entries may name the major they target.
380
379
 
381
380
  ## Shipped
382
381
 
382
+ - **Cursor trail cap** — `ServerTable` and `PageFooter` keep cursor URLs short:
383
+ each trail entry is encoded once and the trail is capped by `maxTrail` (20 by
384
+ default, a prop on each) behind a counted cut mark, so the "21–40 of N" range
385
+ stays exact; Previous from the cut goes back to the first page. (`ServerTable`'s
386
+ `totalCount` was already optional, with no range then; shipped earlier.)
387
+
383
388
  - **App shell header refresh: the TopAppBar family (next minor)** — `TopAppBar`
384
389
  takes four sizes (`small`, `medium-flexible`, `large-flexible`, `search`), an
385
390
  `align`, a `title` that is the page's `h1` with a `subtitle`, and a collapse
@@ -148,6 +148,8 @@
148
148
  @import "./ui/display/vote-tally/vote-tally.css";
149
149
  /* TubeCap: a specimen tube chip, cap colours fixed by the tube convention. */
150
150
  @import "./ui/display/tube-cap/tube-cap.css";
151
+ /* MarkingList: forced-colours rules for the chosen segment. */
152
+ @import "./ui/collections/marking-list/marking-list.css";
151
153
  /* SecretCard: a face that is not rendered until it is revealed. */
152
154
  @import "./ui/display/secret-card/secret-card.css";
153
155
  /* Alert and Banner span the row in the form grids (#1497). */
@@ -0,0 +1,16 @@
1
+ /* MarkingList: the chosen segment is also a check and a border; in forced
2
+ * colours it takes the system's selected colours, as the toggle group does. */
3
+ @media (forced-colors: active) {
4
+ .nxgt-marking-segment {
5
+ border: 1px solid ButtonText;
6
+ }
7
+ .nxgt-marking-segment[aria-pressed="true"] {
8
+ forced-color-adjust: none;
9
+ background: SelectedItem;
10
+ color: SelectedItemText;
11
+ }
12
+ .nxgt-marking-segment:focus-visible {
13
+ outline: 2px solid Highlight;
14
+ outline-offset: 2px;
15
+ }
16
+ }
@@ -43,7 +43,7 @@
43
43
  box-shadow 160ms ease-out;
44
44
  }
45
45
  [data-variant="connected"] > .nxgt-toggle-group-item[data-icon-only] {
46
- width: calc(var(--tg-iw) + 2 * var(--sq));
46
+ width: max(var(--tg-touch), calc(var(--tg-iw) + 2 * var(--sq)));
47
47
  padding-inline: 0;
48
48
  }
49
49
  /* Outer corners: the row's start and end, a column's top and bottom; a
@@ -55,7 +55,7 @@
55
55
  border-radius: var(--tg-inner);
56
56
  }
57
57
  [data-variant="segmented"] > .nxgt-toggle-group-item[data-icon-only] {
58
- min-width: calc(var(--tg-h) + 0.25rem);
58
+ min-width: max(var(--tg-touch), calc(var(--tg-h) + 0.25rem));
59
59
  }
60
60
  [data-variant="segmented"]
61
61
  > .nxgt-toggle-group-item:hover:not(
@@ -65,7 +65,7 @@
65
65
  scale var(--tg-fast) var(--tg-spring-fast);
66
66
  }
67
67
  [data-variant="separate"] > .nxgt-toggle-group-item[data-icon-only] {
68
- width: calc(var(--tg-h) + 0.25rem);
68
+ width: max(var(--tg-touch), calc(var(--tg-h) + 0.25rem));
69
69
  padding-inline-start: 0.25rem;
70
70
  }
71
71
  [data-variant="separate"] > .nxgt-toggle-group-item:hover:not(:disabled) {
@@ -13,6 +13,7 @@
13
13
  --tg-spring: var(--nxgt-spring);
14
14
  --tg-fast: var(--nxgt-spring-fast-duration);
15
15
  --tg-slow: var(--nxgt-spring-duration);
16
+ --tg-touch: 0px;
16
17
  --tg-h: 2.25rem;
17
18
  --tg-pad: 1rem;
18
19
  --tg-font: 0.875rem;
@@ -63,7 +64,8 @@
63
64
  justify-content: center;
64
65
  gap: var(--tg-lead-gap);
65
66
  box-sizing: border-box;
66
- min-width: var(--tg-h);
67
+ min-width: max(var(--tg-touch), var(--tg-h));
68
+ min-height: var(--tg-touch);
67
69
  height: var(--tg-h);
68
70
  padding-inline: var(--tg-pad);
69
71
  border: 0;
@@ -104,6 +106,15 @@
104
106
  cursor: not-allowed;
105
107
  opacity: 0.45;
106
108
  }
109
+ /* Touch: every item is a 44x44 box on a coarse pointer (a size sm item is
110
+ * 32px, an icon-only one about 36 to 42px wide). --tg-touch is the floor
111
+ * the item's min-width and min-height, and the variants' icon-only widths,
112
+ * take through max(); a fine pointer keeps the sizes above. */
113
+ @media (pointer: coarse) {
114
+ .nxgt-toggle-group {
115
+ --tg-touch: 2.75rem;
116
+ }
117
+ }
107
118
 
108
119
  /* The check a selected label-only item draws in (connected only). At rest
109
120
  * it takes no room: no width, and a margin that cancels the gap. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material",
3
- "version": "3.86.2",
3
+ "version": "3.88.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.51.0",
393
+ "@nxgt/material-core": "^0.52.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",