@nxgt/material 3.14.0 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/dist/components/ui/display/chip/chip.css +12 -2
- package/dist/{components-BOwj2IuR.js → components-BOF9frx3.js} +1893 -1774
- package/dist/components.js +5 -5
- package/dist/{dialog-rOMjlKJ7.js → dialog-YHZJjDrI.js} +1 -0
- package/dist/editor.js +3 -3
- package/dist/{icon-badge-_tY1MwU9.js → icon-badge-C0TGvF9o.js} +2 -2
- package/dist/lib/components/ui/display/chip/chip.d.ts +1 -1
- package/dist/lib/components/ui/display/chip/chip.variants.d.ts +5 -2
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog-rules.d.ts +17 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog.d.ts +10 -2
- package/dist/lib/components/ui/layout/app-shell/activity-content-aside.d.ts +22 -0
- package/dist/lib/components/ui/layout/app-shell/activity-content-aside.types.d.ts +36 -0
- package/dist/lib/components/ui/layout/app-shell/activity-content.d.ts +15 -2
- package/dist/lib/components/ui/layout/app-shell/index.d.ts +1 -0
- package/dist/lib/components/ui/overlays/command/command.d.ts +7 -1
- package/dist/lib/components/ui/overlays/dialog/dialog-variants.d.ts +1 -1
- package/dist/main.js +5 -5
- package/dist/map.js +2 -2
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/{public-D_Gz7wgr.js → public-Bj3rGSfS.js} +1 -1
- package/dist/{use-chat-composer-P4GxmCHa.js → use-chat-composer-CoXmJCkr.js} +7 -7
- package/docs/components/data-display.md +2 -2
- package/docs/components/feedback-and-status.md +4 -0
- package/docs/components/navigation-and-layout.md +3 -0
- package/docs/components/overlays-and-menus.md +2 -1
- package/docs/components/primitives.md +2 -0
- package/docs/components/selection-and-choice.md +3 -1
- package/docs/guides/app-shell.md +52 -0
- package/docs/guides/theming.md +1 -0
- package/docs/troubleshooting.md +44 -0
- package/lib/components/ui/display/chip/chip.css +12 -2
- package/package.json +1 -1
package/dist/pdf-reader.js
CHANGED
|
@@ -6,7 +6,7 @@ import { c as i, i as a, l as o, n as s, o as c, s as l } from "./dist-lmHmz_bu.
|
|
|
6
6
|
import { a as u, o as d } from "./tooltip-CkttDIab.js";
|
|
7
7
|
import { i as f } from "./dist-C4wtXCjf.js";
|
|
8
8
|
import { r as p, t as m } from "./in-dialog-Vwkyp-M5.js";
|
|
9
|
-
import { C as h, D as g, E as _, O as v, S as y, T as b, _ as x, f as S, g as ee, k as te, r as ne, t as C, u as re, w } from "./dialog-
|
|
9
|
+
import { C as h, D as g, E as _, O as v, S as y, T as b, _ as x, f as S, g as ee, k as te, r as ne, t as C, u as re, w } from "./dialog-YHZJjDrI.js";
|
|
10
10
|
import { c as T, d as ie, f as E, i as ae, l as oe, n as se, o as D, t as ce, u as O } from "./dropdown-menu-BEyP-A0J.js";
|
|
11
11
|
import { S as le, a as k, d as A, g as ue, h as de, i as fe, m as pe, n as j, r as me, t as M, u as he, w as ge } from "./toolbar-items-CVQBYp4K.js";
|
|
12
12
|
import { t as N } from "./use-material-translation-DGeVp-qP.js";
|
|
@@ -7,7 +7,7 @@ import { a as o, c as s, i as c, l, n as u, o as d, r as f, s as p, t as m } fro
|
|
|
7
7
|
import { _ as h, a as g, g as _, h as v, m as y, o as b, p as x, t as S, v as C, y as w } from "./tooltip-CkttDIab.js";
|
|
8
8
|
import { a as T, i as E, n as D, r as O, t as k } from "./dist-C4wtXCjf.js";
|
|
9
9
|
import { i as A, n as ee, t as j } from "./in-dialog-Vwkyp-M5.js";
|
|
10
|
-
import { A as M, C as N, D as te, E as ne, O as P, S as re, T as ie, _ as F, b as ae, f as oe, g as se, k as I, l as ce, m as le, n as ue, o as de, r as fe, s as pe, t as me, u as he, v as ge, w as _e, x as ve, y as ye } from "./dialog-
|
|
10
|
+
import { A as M, C as N, D as te, E as ne, O as P, S as re, T as ie, _ as F, b as ae, f as oe, g as se, k as I, l as ce, m as le, n as ue, o as de, r as fe, s as pe, t as me, u as he, v as ge, w as _e, x as ve, y as ye } from "./dialog-YHZJjDrI.js";
|
|
11
11
|
import { A as be, C as xe, D as Se, E as Ce, O as we, S as Te, T as Ee, _ as De, a as Oe, b as ke, d as Ae, f as je, g as Me, h as Ne, i as Pe, k as Fe, l as Ie, m as Le, n as Re, o as ze, p as Be, r as Ve, s as He, t as Ue, u as We, v as Ge, w as Ke, x as qe, y as Je } from "./floating-ui.dom-KkZSW8_E.js";
|
|
12
12
|
import { D as Ye, E as Xe, N as Ze, O as Qe, c as $e, d as et, f as tt, i as nt, k as rt, m as it, n as at, o as ot, p as st, t as ct, u as lt } from "./dropdown-menu-BEyP-A0J.js";
|
|
13
13
|
import { a as ut, i as dt, n as ft } from "./field-blur-context-Cft9s7Hg.js";
|
|
@@ -36,7 +36,7 @@ import { n as an } from "./use-slice-reducer-BGkeGtML.js";
|
|
|
36
36
|
import { t as on } from "./browser-only-DQ94lWF8.js";
|
|
37
37
|
import { t as sn } from "./compose-refs-DZ0iIVF-.js";
|
|
38
38
|
import { n as cn, r as ln, t as un } from "./chip-BZu-bvAJ.js";
|
|
39
|
-
import { n as dn, t as fn } from "./icon-badge-
|
|
39
|
+
import { n as dn, t as fn } from "./icon-badge-C0TGvF9o.js";
|
|
40
40
|
import { r as pn } from "./use-csp-nonce-ZKs9k-ov.js";
|
|
41
41
|
import { t as mn } from "./empty-state-ZtqqpX0S.js";
|
|
42
42
|
import { t as hn } from "./skeleton-DSdtr2qc.js";
|
|
@@ -1324,7 +1324,7 @@ function xu({ className: e, ...t }) {
|
|
|
1324
1324
|
function Su({ className: e, ...t }) {
|
|
1325
1325
|
return /* @__PURE__ */ G("kbd", {
|
|
1326
1326
|
"data-slot": "kbd",
|
|
1327
|
-
className: n("pointer-events-none inline-flex h-5 min-w-5 items-center justify-center rounded border bg-muted px-1 font-mono text-[10px] font-medium text-muted-foreground", e),
|
|
1327
|
+
className: n("pointer-events-none inline-flex h-5 min-w-5 items-center justify-center rounded border bg-muted px-1 font-mono text-[10px] font-medium text-muted-foreground", "in-[[data-slot=button][data-variant=filled]]:border-current/40 in-[[data-slot=button][data-variant=filled]]:bg-current/15 in-[[data-slot=button][data-variant=filled]]:text-inherit", e),
|
|
1328
1328
|
...t
|
|
1329
1329
|
});
|
|
1330
1330
|
}
|
|
@@ -7969,7 +7969,7 @@ function Hy({ slot: e, values: t, getOptionValue: r = (e) => e.value, getOptionL
|
|
|
7969
7969
|
ref: o,
|
|
7970
7970
|
"data-slot": e ?? "autocomplete-field",
|
|
7971
7971
|
"data-mode": a.mode,
|
|
7972
|
-
className: n("grid w-full gap-1.5", a.className),
|
|
7972
|
+
className: n("grid w-full content-start gap-1.5", a.className),
|
|
7973
7973
|
children: [
|
|
7974
7974
|
/* @__PURE__ */ G(dt, {
|
|
7975
7975
|
id: c,
|
|
@@ -8432,7 +8432,7 @@ function ub({ group: e, name: t, slot: r, className: i, contentClassName: a, inl
|
|
|
8432
8432
|
return /* @__PURE__ */ K("div", {
|
|
8433
8433
|
"data-slot": r,
|
|
8434
8434
|
"data-pw": t ?? r,
|
|
8435
|
-
className: n("grid gap-1 w-full", i),
|
|
8435
|
+
className: n("grid content-start gap-1 w-full", i),
|
|
8436
8436
|
...l,
|
|
8437
8437
|
...d,
|
|
8438
8438
|
ref: u,
|
|
@@ -9542,7 +9542,7 @@ function kx({ error: e, helperText: t, label: r, className: i, placeholder: a, m
|
|
|
9542
9542
|
return /* @__PURE__ */ K("div", {
|
|
9543
9543
|
ref: m,
|
|
9544
9544
|
"data-slot": "textarea-field",
|
|
9545
|
-
className: n("grid w-full gap-1.5", i),
|
|
9545
|
+
className: n("grid w-full content-start gap-1.5", i),
|
|
9546
9546
|
children: [
|
|
9547
9547
|
r && /* @__PURE__ */ G(dt, {
|
|
9548
9548
|
withAsterisk: p.required,
|
|
@@ -12073,7 +12073,7 @@ function GC({ id: e, value: t, defaultValue: r = "", onChange: i, onValueChange:
|
|
|
12073
12073
|
return /* @__PURE__ */ K("div", {
|
|
12074
12074
|
"data-slot": "search-field",
|
|
12075
12075
|
"data-pw": m ?? "search-field",
|
|
12076
|
-
className: n("grid w-full gap-1.5", h),
|
|
12076
|
+
className: n("grid w-full content-start gap-1.5", h),
|
|
12077
12077
|
children: [
|
|
12078
12078
|
l && /* @__PURE__ */ G(dt, {
|
|
12079
12079
|
htmlFor: ee,
|
|
@@ -368,7 +368,7 @@ reads as a status (a stronger container of the tone, the label in the tone's
|
|
|
368
368
|
text colour, 4.5:1 or more on the page and in a card; the `outlined` hairline
|
|
369
369
|
and the `tag` dot still read `--chip-tone`, not the tone channel); `tag`
|
|
370
370
|
a smaller tag led by a colour dot; `dashed` a muted "add" pill. 28px tall,
|
|
371
|
-
24px with `size="sm"`. Selected (`active`, or `aria-pressed` on a toggle
|
|
371
|
+
24px with `size="sm"`, 44px with `size="lg"`. Selected (`active`, or `aria-pressed` on a toggle
|
|
372
372
|
chip) takes a primary tint and a primary edge, and a pressed toggle with
|
|
373
373
|
nothing leading draws a check. The label is truncated as one piece; the ✕
|
|
374
374
|
and `trailing` stay whole.
|
|
@@ -397,7 +397,7 @@ import { Avatar, Chip } from '@nxgt/material/components';
|
|
|
397
397
|
| `variant` | `'outlined' \| 'tonal' \| 'filled' \| 'tag' \| 'dashed'` | `'outlined'` | the look: hairline, soft (`tonal` and `filled`), tag, or a muted dashed "add" pill that turns solid once `active` or pressed. `ghost` (soft) and `link` (hairline) are still accepted, deprecated |
|
|
398
398
|
| `active` | `boolean` | — | selected: primary tint and edge |
|
|
399
399
|
| `aria-pressed` | `boolean` | — | a toggle chip: selected while pressed, with a check when nothing leads |
|
|
400
|
-
| `size` | `'default' \| 'sm'` | `'default'` | 28px or
|
|
400
|
+
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | 28px, 24px, or `lg`, a 44px box at every pointer (the touch target); every target stays at least 24px |
|
|
401
401
|
| `color` | `'default' \| 'primary' \| 'secondary' \| 'info' \| 'success' \| 'warning' \| 'error'` | neutral | tone: the edge (outlined), the fill and label (tonal, `filled`: a status look) or the dot and tint (tag). A `tag` without `color` (or with `default`) draws no dot |
|
|
402
402
|
| `onDismiss` | `() => void` | — | adds the ✕ button, named "Remove {label}" (`material.chip.dismiss-label`), `{label}` being the text `label` shows, a node's included; "Remove" when it shows none; the ✕ is drawn 18px (16px small) with a 24px target |
|
|
403
403
|
| `dismissLabel` | `string \| ((label: string) => string)` | — | names the ✕ instead of "Remove {label}": a string as is, or a function of the text the chip shows |
|
|
@@ -222,8 +222,12 @@ function DeleteProject({ onDelete }: { onDelete: () => Promise<void> }) {
|
|
|
222
222
|
| `onConfirm` | `() => void \| Promise<void>` | required | awaited with the dialog open and both buttons disabled; closes on success; on a throw or rejection the dialog stays open and shows the error's message, or the translated `material.confirm-dialog.failed` ("The action failed.") when it has none |
|
|
223
223
|
| `variant` | `'default' \| 'destructive'` | `'default'` | `destructive` paints the confirm button `error` |
|
|
224
224
|
| `loading` | `boolean` | `false` | disables both buttons and shows the translated `material.confirm-dialog.processing` label, as a pending `onConfirm` does |
|
|
225
|
+
| `confirmDisabled` | `boolean` | `false` | disables the confirm button, for example while an error of yours stands |
|
|
226
|
+
| `lockOnRefusal` | `boolean` | `false` | once `onConfirm` has refused (thrown), the confirm button stays disabled while its error shows, so a retry that cannot succeed is not invited; Cancel stays enabled. The error clears when the dialog closes |
|
|
225
227
|
| `confirmLabel` / `cancelLabel` | `string` | "Confirm" / "Cancel", translated (`material.confirm-dialog.confirm` / `.cancel`) | button text |
|
|
226
228
|
|
|
229
|
+
On a phone (below 640 px) the footer stacks. A `destructive` dialog puts Cancel first and the destructive action last; a default one keeps the action on top.
|
|
230
|
+
|
|
227
231
|
`ConfirmDialog` opens with its Cancel focused, so Enter cancels. On closing
|
|
228
232
|
it hands the focus back to what opened it, or to the menu's trigger when
|
|
229
233
|
that was a `DropdownMenuItem` (gone by then); `Dialog`, `Sheet` and
|
|
@@ -81,6 +81,9 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
81
81
|
| `menuOpen` / `onMenuOpenChange` | `boolean` / `(open: boolean) => void` | — | control the phone's menu sheet; it still closes by itself when a link in it is followed or the location changes (see the [app shell guide](../guides/app-shell.md#activitylayout)) |
|
|
82
82
|
| `titleAs` | `'h1'` … `'h6'` \| `'div'` | `'h1'` | `ActivityContent`: the title's heading level |
|
|
83
83
|
| `inset` | `boolean` | `false` | `ActivityContent`: pads the content 1rem on each side. Off, it lines up with the app bar and the cards |
|
|
84
|
+
| `width` | `'full' \| 'form' \| 'reading'` | `'full'` | `ActivityContent`: caps the main column at 48rem (`'form'`) or 72ch (`'reading'`), the side panel right beside it |
|
|
85
|
+
| `aside`, `asideLabel` / `asideLabelledBy` | `ReactNode`, `string` | — | `ActivityContent`: a side panel, a named `complementary` landmark, beside the content from 1280px (CSS only), stacked below; sticky under the app bar |
|
|
86
|
+
| `asideSide` / `asideWidth` / `asideFrom` / `asideSticky` / `asideStack` | `'start' \| 'end'` / `'sm' \| 'md' \| 'lg' \| string` / `'lg' \| 'xl' \| '2xl'` / `boolean` / `'before' \| 'after'` | `'end'` / `'md'` / `'xl'` / `true` / `'after'` | `ActivityContent`: the panel's side, width (16, 20, 24rem), breakpoint, stickiness and stacking order (see the [app shell guide](../guides/app-shell.md#a-side-panel-and-a-readable-width)) |
|
|
84
87
|
| `align` | `'start' \| 'center'` | `'start'` | `ActivityLayout`, `AppSidebar`: the collapsed rail's entries sit right under the brand, or `'center'` for centred in the height below it |
|
|
85
88
|
| `backdrop` | `ReactNode \| false` | — | `ActivityLayout`: an exception to the theme's background (drawn by default; Lavande's is the built-in gradient and blobs); `false` draws a plain page. A node is a decorative background behind the frame: a `Background` preset from `@nxgt/material/backgrounds`. Rendered in an `inert`, `aria-hidden` layer as wide as the content column and one viewport high, fixed in the viewport while the content scrolls; the chrome (app bar, breadcrumb, tabs strip) is lightly translucent and frosted over it (80%, 87% for the sidebar, breadcrumb and tabs strip, 92% and no blur under `prefers-reduced-transparency` or without `backdrop-filter`), and the lists, tables and cards stay opaque; `--nxgt-backdrop-chrome` sets the chrome's fill and `--nxgt-backdrop-surface` the lists'. See [Use a background preset behind the shell](../guides/app-shell.md#use-a-background-preset-behind-the-shell) |
|
|
86
89
|
| `routeTransition` | `boolean \| Omit<ViewTransitionProps, 'children' \| 'ref'>` | `true` | `ActivityLayout`: React's `<ViewTransition>` around the content region, so a navigation cross-fades it. `false` renders none (your app has its own boundary); an object passes `default` / `update` classes, `name`, callbacks through. See [Route transitions](../guides/app-shell.md#route-transitions) |
|
|
@@ -131,7 +131,7 @@ export function Terms() {
|
|
|
131
131
|
| `asChild` (`DialogTrigger`) | `boolean` | **`true`** | the trigger is its child — pass one element, usually a `Button` |
|
|
132
132
|
| `variant`, `color` (`DialogClose`) | as `IconButton` | `'ghost'` | a top-right X button, named "Close" (`material.buttons.close`, translated) while it shows only the icon; text children or an `aria-label` name it instead, and icon-only children leave it unnamed; children replace the X |
|
|
133
133
|
| `size` (`DialogContent`) | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | `'md'` | its width from 640 px up: 384, 512, 672, 896 px, or the screen less 2rem a side |
|
|
134
|
-
| `mobilePresentation` (`DialogContent`) | `'dialog' \| 'bottom-sheet' \| 'fullscreen'` | `'dialog'` | below 640 px: a centred dialog, a sheet that springs up from the bottom edge (rounded top corners), or the whole screen |
|
|
134
|
+
| `mobilePresentation` (`DialogContent`) | `'dialog' \| 'bottom-sheet' \| 'top' \| 'fullscreen'` | `'dialog'` | below 640 px: a centred dialog, a sheet that springs up from the bottom edge (rounded top corners), a dialog pinned under the top edge, or the whole screen |
|
|
135
135
|
| `scrim` (`DialogContent`, `SheetContent`, `DrawerContent`) | `'dim' \| 'blur'` | `'dim'` | the scrim behind; `blur` also blurs the page a little |
|
|
136
136
|
|
|
137
137
|
The look lives in the stylesheet (`nxgt-dialog`, `nxgt-scrim`), under the
|
|
@@ -678,6 +678,7 @@ children and they replace that, as before.
|
|
|
678
678
|
| `alwaysRender` (`CommandSeparator`) | `boolean` | `false` | the separator is purely visual (`role="presentation"`, hidden from assistive technology) and hidden while the user searches; `true` keeps it visible then |
|
|
679
679
|
| `shouldFilter` (`Command`) | `boolean` | `true` | `false` when you filter the items yourself (server search) |
|
|
680
680
|
| `title` / `description` (`CommandDialog`) | `string` | `"Command Palette"` / `"Search for a command to run..."` (translated) | the dialog's title and description, inside the dialog and visually hidden |
|
|
681
|
+
| `mobilePresentation` (`CommandDialog`) | `'dialog' \| 'top' \| 'fullscreen'` | `'top'` | below 640 px: pinned under the top edge (so the input and the results stay above the on-screen keyboard), the whole screen, or the centred modal it was before |
|
|
681
682
|
| `open`, `onOpenChange` (`CommandDialog`) | as `Dialog` | — | `CommandDialog` takes every `Dialog` prop |
|
|
682
683
|
|
|
683
684
|
`CommandDialog` does not bind a keyboard shortcut; listen for ⌘K yourself and
|
|
@@ -526,6 +526,8 @@ import { Kbd, KbdShortcut } from '@nxgt/material/components';
|
|
|
526
526
|
| --- | --- | --- | --- |
|
|
527
527
|
| `keys` (`KbdShortcut`) | `string[]` | — (required) | one `Kbd` per key; `mod`, `shift` and `alt` become `⌘ ⇧ ⌥` on Apple platforms, `Ctrl Shift Alt` elsewhere |
|
|
528
528
|
|
|
529
|
+
Inside a filled `Button` the key, its border and the `+` between keys take the button's own text colour, so the shortcut keeps the contrast of the fill.
|
|
530
|
+
|
|
529
531
|
`Kbd` takes the props of `<kbd>`. `shortcutGlyph(key)` gives one glyph on its own.
|
|
530
532
|
|
|
531
533
|
**Storybook:** `Primitives/Kbd` · **Vue:** `Kbd`, `KbdShortcut`
|
|
@@ -328,6 +328,8 @@ when nothing matches. Options with a `group` are listed under its header
|
|
|
328
328
|
with their count, filtered within it (a group with no match goes); a
|
|
329
329
|
`description` is a muted line under the label, in the list only.
|
|
330
330
|
|
|
331
|
+
Once an option is picked, a round "Clear" button (`material.combobox.clear`, "Effacer" in French) appears at the end of the box, also in `ComboboxFormField`. It is a real button, named and exposed to assistive technology (`getByRole('button', { name: 'Clear' })`), described by the field's label, and kept out of the tab order like the other clearable fields: the keyboard clears with Backspace or by selecting the text and deleting it.
|
|
332
|
+
|
|
331
333
|
```tsx
|
|
332
334
|
import { ComboboxField } from '@nxgt/material/components';
|
|
333
335
|
import { useState } from 'react';
|
|
@@ -785,7 +787,7 @@ export function Topics() {
|
|
|
785
787
|
| `deselectable` | `boolean` | `false` | `single` only: pressing the chosen chip clears it |
|
|
786
788
|
| `max` / `min` | `number` | — | `multiple` only: `max` blocks the other chips once reached and speaks "{count} of {max} chosen"; `min` is a hint only |
|
|
787
789
|
| `options` | `{ value; label; icon?; count?; description?; disabled?; disabledReason? }[]` | — | the chips; `count`: see [option counts](#option-counts) |
|
|
788
|
-
| `size` | `'default' \| 'sm'` | `'default'` | the chip's size
|
|
790
|
+
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | the chip's size: 28px, 24px, or `lg`, a 44px box at every pointer (a coarse pointer already gets a 44px box at any size) |
|
|
789
791
|
| `name` | `string` | — | put on every chip (`ChipGroupFormField` passes its field `name`), so `document.getElementsByName(name)` finds the group's chips in order |
|
|
790
792
|
| `label`, `helperText`, `error`, `required`, `disabled`, `id` | | — | the question and its help or error text, the required mark, disabling every chip, the group's `id` |
|
|
791
793
|
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | wrapping row or a column (each chip as wide as its label); in single mode it sets the `radiogroup`'s `aria-orientation` (ARIA allows none on a `group`), and every arrow key moves in both axes either way (the chips wrap) |
|
package/docs/guides/app-shell.md
CHANGED
|
@@ -799,6 +799,15 @@ export function BoardShell({ items }: { items: MenuItem[] }) {
|
|
|
799
799
|
| `hideHeader` | `boolean` | `false` | No header card |
|
|
800
800
|
| `titleAs` | `'h1'` … `'h6'` \| `'div'` | `'h1'` | The title's element: the screen's heading, `'h2'` under a heading of yours, `'div'` for none |
|
|
801
801
|
| `inset` | `boolean` | `false` | Pads the content 1rem on each side. Off, it lines up with the app bar and the cards |
|
|
802
|
+
| `width` | `'full'` \| `'form'` \| `'reading'` | `'full'` | Caps the main column: `'form'` 48rem, `'reading'` 72ch. The header card keeps the full width |
|
|
803
|
+
| `aside` | `ReactNode` | — | A side panel: beside the content from `asideFrom`, stacked below it |
|
|
804
|
+
| `asideLabel` / `asideLabelledBy` | `string` | — | The panel's accessible name (`aria-label`), or the id of a heading inside it (`aria-labelledby`). One of them is needed |
|
|
805
|
+
| `asideSide` | `'start'` \| `'end'` | `'end'` | Which side of the content the panel sits on, wide |
|
|
806
|
+
| `asideWidth` | `'sm'` \| `'md'` \| `'lg'` \| a CSS length | `'md'` | 16, 20 or 24rem; `'md'` reads the `--nxgt-aside-width` token (20rem) |
|
|
807
|
+
| `asideFrom` | `'lg'` \| `'xl'` \| `'2xl'` | `'xl'` | The viewport width (1024, 1280 or 1536px) from which the panel is beside the content |
|
|
808
|
+
| `asideSticky` | `boolean` | `true` | Keeps the panel under the app bar while the page scrolls; it scrolls inside itself when taller than the window |
|
|
809
|
+
| `asideStack` | `'before'` \| `'after'` | `'after'` | Where the panel goes below `asideFrom`, in the DOM and on screen |
|
|
810
|
+
| `asideClassName` | `string` | — | Classes for the panel |
|
|
802
811
|
|
|
803
812
|
```tsx
|
|
804
813
|
import { ActivityContent, Button } from '@nxgt/material/components';
|
|
@@ -819,6 +828,49 @@ export function InvoicesPage() {
|
|
|
819
828
|
The header card wraps: on a narrow screen the actions go under the title
|
|
820
829
|
instead of squeezing it, and a long title wraps rather than scrolling.
|
|
821
830
|
|
|
831
|
+
### A side panel and a readable width
|
|
832
|
+
|
|
833
|
+
A record's alerts and key facts, a dashboard's counts, a form's help: pass them
|
|
834
|
+
as `aside`. From `asideFrom` (`'xl'`, a 1280px window) the panel sits beside the
|
|
835
|
+
main column and sticks under the app bar; below it, the panel stacks after the
|
|
836
|
+
content (`asideStack="before"` puts it first). The switch is CSS only: the
|
|
837
|
+
server's HTML and a phone's first paint are already right, and no JavaScript
|
|
838
|
+
decides the layout.
|
|
839
|
+
|
|
840
|
+
```tsx
|
|
841
|
+
<ActivityContent
|
|
842
|
+
title="New visit"
|
|
843
|
+
width="form"
|
|
844
|
+
aside={<DuplicateHints />}
|
|
845
|
+
asideLabel="Possible duplicates"
|
|
846
|
+
>
|
|
847
|
+
<VisitForm />
|
|
848
|
+
</ActivityContent>
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
- **Landmark.** The panel is an `<aside>`, a `complementary` landmark: name it
|
|
852
|
+
with `asideLabel`, or with `asideLabelledBy` (the id of its heading). Its
|
|
853
|
+
content is the panel's own; give it one `h2` when it has a heading.
|
|
854
|
+
- **Sticky, in every scroll mode.** With `scroll="document"` the panel sticks
|
|
855
|
+
under the app bar (the bar's gutter, its `--nxgt-header-height` and 0.5rem);
|
|
856
|
+
with `scroll="container"` it sticks to the top of the content region. It is as
|
|
857
|
+
tall as the window at most and scrolls inside itself, and becomes focusable
|
|
858
|
+
(`tabindex="0"`) only while it does, so a keyboard reaches what overflows. A
|
|
859
|
+
`scroll="document"` page needs no `overflow` on any ancestor of the screen
|
|
860
|
+
(see [the troubleshooting entry](../troubleshooting.md#the-side-panel-does-not-stick)).
|
|
861
|
+
- **A capped column sits beside its panel.** `width="form"` or `"reading"` caps
|
|
862
|
+
the main column and the pair stays start-aligned: at 1920px the panel is 1rem
|
|
863
|
+
from the fields, not at the window's far edge. The header card above keeps
|
|
864
|
+
the full width. `width` also works without a panel.
|
|
865
|
+
- **The breakpoint is the window's.** `asideFrom` follows the viewport, not the
|
|
866
|
+
content column, which the sidebar and the gutters narrow a little: pick `'lg'`
|
|
867
|
+
for a panel on a smaller window, `'2xl'` to keep it stacked on all but the
|
|
868
|
+
widest.
|
|
869
|
+
- **A size container.** With a panel, the main column is an `@container`, so a
|
|
870
|
+
`ResponsiveGrid` inside it follows the column's width.
|
|
871
|
+
- **Width token.** `--nxgt-aside-width` changes `asideWidth="md"` (20rem) app-wide;
|
|
872
|
+
any CSS length goes in `asideWidth` for one screen.
|
|
873
|
+
|
|
822
874
|
## Breadcrumbs
|
|
823
875
|
|
|
824
876
|
`AppBreadcrumb` (rendered by `ActivityLayout`) starts with a home icon linking
|
package/docs/guides/theming.md
CHANGED
|
@@ -625,6 +625,7 @@ centre line from `md` (48rem) up:
|
|
|
625
625
|
| Token | Default | Reads |
|
|
626
626
|
| --- | --- | --- |
|
|
627
627
|
| `--nxgt-header-height` | `4rem` | the app bar (all widths) and the rail's header (`md` and up) |
|
|
628
|
+
| `--nxgt-aside-width` | `20rem` | an `ActivityContent` side panel with `asideWidth="md"` (the default) |
|
|
628
629
|
|
|
629
630
|
```css
|
|
630
631
|
:root {
|
package/docs/troubleshooting.md
CHANGED
|
@@ -1338,6 +1338,30 @@ top (`align="start"`).
|
|
|
1338
1338
|
<ActivityLayout items={items} align="center">…</ActivityLayout>
|
|
1339
1339
|
```
|
|
1340
1340
|
|
|
1341
|
+
### The side panel does not stick
|
|
1342
|
+
|
|
1343
|
+
**When:** an `ActivityContent` `aside` scrolls away with the page instead of
|
|
1344
|
+
staying under the app bar, or it is not beside the content at 1280px.
|
|
1345
|
+
**Why:** `position: sticky` sticks inside its nearest scrolling ancestor, and
|
|
1346
|
+
any ancestor with an `overflow` other than `visible` (`overflow-hidden`,
|
|
1347
|
+
`overflow-auto`, `overflow-clip`; a `PaneLayout`'s root has `overflow-hidden`)
|
|
1348
|
+
becomes that scroller, which does not scroll, so the panel never moves. A
|
|
1349
|
+
wrapper of yours around the screen, or an `ActivityLayout` with
|
|
1350
|
+
`scroll="container"` where you meant `"document"`, does the same. Below
|
|
1351
|
+
`asideFrom` (1280px by default) the panel is stacked and not sticky on
|
|
1352
|
+
purpose, and the breakpoint is the window's width, not the column's.
|
|
1353
|
+
**Fix:** take the `overflow` off every ancestor between `ActivityLayout` and
|
|
1354
|
+
`ActivityContent`, or move it onto an element inside the screen; use
|
|
1355
|
+
`scroll="document"` (the default). Find the culprit in the console:
|
|
1356
|
+
|
|
1357
|
+
```js
|
|
1358
|
+
for (let el = document.querySelector('[data-slot="activity-content-aside"]').parentElement; el; el = el.parentElement)
|
|
1359
|
+
if (getComputedStyle(el).overflowY !== 'visible') console.log(el);
|
|
1360
|
+
```
|
|
1361
|
+
|
|
1362
|
+
With `scroll="container"` the region itself is the scroller, and the panel
|
|
1363
|
+
sticks to its top: that is expected.
|
|
1364
|
+
|
|
1341
1365
|
### axe reports `target-size` on a control under a form's sticky actions
|
|
1342
1366
|
|
|
1343
1367
|
**When:** since the form's actions stick to the bottom (see above), axe's
|
|
@@ -1975,6 +1999,26 @@ a few frames after the alert has gone) and look it up by selector rather
|
|
|
1975
1999
|
than by role: the closing alert still hides the page from the accessibility
|
|
1976
2000
|
tree.
|
|
1977
2001
|
|
|
2002
|
+
### A `ConfirmDialog` keeps offering a refused action
|
|
2003
|
+
|
|
2004
|
+
When `onConfirm` throws, the dialog shows the error and the confirm button
|
|
2005
|
+
stays active, so the user can retry. If a retry cannot succeed (the item is
|
|
2006
|
+
still referenced), lock it:
|
|
2007
|
+
|
|
2008
|
+
```tsx
|
|
2009
|
+
<ConfirmDialog variant="destructive" lockOnRefusal onConfirm={remove} … />
|
|
2010
|
+
```
|
|
2011
|
+
|
|
2012
|
+
Cancel stays enabled. `confirmDisabled` is the switch for your own condition.
|
|
2013
|
+
|
|
2014
|
+
### Fields in a two-column row sit at different heights
|
|
2015
|
+
|
|
2016
|
+
A field is a grid of a label and a control. In earlier versions its rows stretched when a
|
|
2017
|
+
neighbour in the same row was taller (a helper or error text under it), so the
|
|
2018
|
+
shorter field's control sank by half the difference. Fields now keep their rows
|
|
2019
|
+
at the top (`content-start`); upgrade, and drop any `mt-*` or `items-end`
|
|
2020
|
+
workaround. A `DateField`'s tonal fill is deliberate and not a misalignment.
|
|
2021
|
+
|
|
1978
2022
|
## Navigation
|
|
1979
2023
|
|
|
1980
2024
|
### A `ListTile` link shows two chevrons
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Chip: what the three looks share (data-look hairline, soft, tag). Sizes:
|
|
2
|
-
* default 28px, sm 24px (both at least the 24px target of WCAG 2.5.8)
|
|
3
|
-
* label truncates as one piece; the ✕ is drawn small, its target is 24×24
|
|
2
|
+
* default 28px, sm 24px (both at least the 24px target of WCAG 2.5.8), lg
|
|
3
|
+
* 44px (the touch target). The label truncates as one piece; the ✕ is drawn small, its target is 24×24
|
|
4
4
|
* through ::after, and it has its own focus ring. */
|
|
5
5
|
@layer components {
|
|
6
6
|
.nxgt-chip {
|
|
@@ -50,6 +50,16 @@
|
|
|
50
50
|
--chip-gap: 0.25rem;
|
|
51
51
|
--chip-x: 1rem;
|
|
52
52
|
}
|
|
53
|
+
/* lg: a 44px box at every pointer (the touch target, #1529). */
|
|
54
|
+
.nxgt-chip[data-size="lg"] {
|
|
55
|
+
--chip-h: 2.75rem;
|
|
56
|
+
--chip-px: 1rem;
|
|
57
|
+
--chip-font: 0.9375rem;
|
|
58
|
+
--chip-icon: 1rem;
|
|
59
|
+
--chip-gap: 0.375rem;
|
|
60
|
+
--chip-x: 1.5rem;
|
|
61
|
+
min-width: 2.75rem;
|
|
62
|
+
}
|
|
53
63
|
button.nxgt-chip,
|
|
54
64
|
.nxgt-chip > button[data-slot="chip-body"] {
|
|
55
65
|
cursor: pointer;
|