@nxgt/material 3.70.0 → 3.72.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 +75 -0
- package/README.md +9 -6
- package/dist/{alert-dialog-CBkWG32g.js → alert-dialog-A8K8zai1.js} +1 -1
- package/dist/{async-slice-CNZ3N55P.js → async-slice-B8933IHY.js} +1 -1
- package/dist/{button-D3qzqDY_.js → button-C8moFnIT.js} +1 -1
- package/dist/charts.js +3 -3
- package/dist/{chip-CMTe4sQd.js → chip-B6AY1E91.js} +2 -2
- package/dist/{code-editor-Cs7mz1J-.js → code-editor-DQZIKL54.js} +5 -5
- package/dist/code-editor.js +5 -5
- package/dist/{code-language-items-D_QMgjVy.js → code-language-items-8VL0aMKG.js} +2 -2
- package/dist/components/ui/collections/server-table/server-table-phone-row.css +6 -0
- package/dist/components/ui/display/code/code-moonlight.css +37 -0
- package/dist/components/ui/fields/attributes/attribute-values-form/attribute-values-form.css +5 -0
- package/dist/{components-BbWcozcj.js → components-EHdmrvV4.js} +20862 -19924
- package/dist/components.js +28 -28
- package/dist/{dialog-B3BSdRci.js → dialog-WuLTwxkr.js} +5 -5
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-BaupOWgS.js → dropdown-menu-tadySeGH.js} +1 -1
- package/dist/editor.js +35 -35
- package/dist/{empty-state-B2Um5UpI.js → empty-state-ZYiFSSBQ.js} +2 -2
- package/dist/{field-blur-1XLyxRPr.js → field-blur-BYR0yE5L.js} +1 -1
- package/dist/{field-blur-context-XNvCDnMT.js → field-blur-context-bEhByoK-.js} +1 -1
- package/dist/{hooks-BU21Jpaa.js → hooks-GpwD8iDu.js} +2 -2
- package/dist/hooks.js +3 -3
- package/dist/{hover-card-Bvm3svX9.js → hover-card-BowaI_ra.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-wip9bfa2.js → icon-badge-B1B0Lg02.js} +3 -3
- package/dist/{icon-button-QwIeBTdw.js → icon-button-BbGU17cb.js} +1 -1
- package/dist/lib/components/ui/actions/fab/fab-dock-inset.d.ts +29 -0
- package/dist/lib/components/ui/actions/fab/fab-dock.d.ts +6 -1
- package/dist/lib/components/ui/collections/data-table/select-checkbox.d.ts +3 -2
- package/dist/lib/components/ui/collections/filter/use-filter-field-config.d.ts +3 -0
- package/dist/lib/components/ui/collections/index.d.ts +1 -0
- package/dist/lib/components/ui/collections/record-actions-menu/index.d.ts +2 -0
- package/dist/lib/components/ui/collections/record-actions-menu/record-actions-dialogs.d.ts +15 -0
- package/dist/lib/components/ui/collections/record-actions-menu/record-actions-menu.d.ts +72 -0
- package/dist/lib/components/ui/collections/record-actions-menu/record-actions-rules.d.ts +68 -0
- package/dist/lib/components/ui/collections/record-actions-menu/record-actions-words.d.ts +52 -0
- package/dist/lib/components/ui/collections/record-actions-menu/use-record-actions.d.ts +51 -0
- package/dist/lib/components/ui/collections/record-actions-menu/use-record-flight.d.ts +16 -0
- package/dist/lib/components/ui/collections/row-menu/row-menu-column.d.ts +4 -0
- package/dist/lib/components/ui/collections/row-menu/row-menu.d.ts +1 -1
- package/dist/lib/components/ui/collections/server-table/partials/server-table-band-classes.d.ts +7 -5
- package/dist/lib/components/ui/collections/server-table/partials/server-table-column-sorting.d.ts +7 -0
- package/dist/lib/components/ui/collections/server-table/types.d.ts +3 -1
- package/dist/lib/components/ui/display/markdown/markdown.d.ts +12 -1
- package/dist/lib/components/ui/display/markdown/markdown.utils.d.ts +2 -0
- package/dist/lib/components/ui/display/markdown/mermaid-csp-scope.d.ts +8 -0
- package/dist/lib/components/ui/display/markdown/mermaid-markup.d.ts +20 -0
- package/dist/lib/components/ui/display/markdown/partials/markdown-code.d.ts +5 -1
- package/dist/lib/components/ui/feedback/confirm-dialog/action-refusal.d.ts +24 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-alert.d.ts +17 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-description.d.ts +10 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog.d.ts +24 -2
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-refusal.d.ts +7 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/index.d.ts +1 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/use-confirm-action.d.ts +6 -0
- package/dist/lib/components/ui/feedback/index.d.ts +1 -0
- package/dist/lib/components/ui/feedback/stale-notice/index.d.ts +1 -0
- package/dist/lib/components/ui/feedback/stale-notice/stale-notice.d.ts +15 -0
- package/dist/lib/components/ui/fields/attributes/attribute-values-form-field-set/attribute-values-form-field-set.d.ts +5 -1
- package/dist/lib/components/ui/fields/contacts/contact-dialog.d.ts +8 -2
- package/dist/lib/components/ui/fields/contacts/contacts-dialog-form-field.d.ts +4 -1
- package/dist/lib/components/ui/fields/contacts/contacts-editor.d.ts +15 -2
- package/dist/lib/components/ui/fields/contacts/contacts-form-field.d.ts +6 -1
- package/dist/lib/components/ui/fields/contacts/logic/offered-types.d.ts +3 -0
- package/dist/lib/components/ui/fields/contacts/models.d.ts +2 -0
- package/dist/lib/components/ui/fields/contacts/partials/contact-fields.d.ts +4 -2
- package/dist/lib/components/ui/fields/contacts/partials/contact-groups.d.ts +4 -2
- package/dist/lib/components/ui/fields/contacts/partials/contact-row.d.ts +4 -2
- package/dist/lib/components/ui/fields/contacts/partials/phone-sheet.d.ts +4 -2
- package/dist/lib/components/ui/fields/contacts/partials/quick-add.d.ts +4 -2
- package/dist/lib/components/ui/fields/contacts/partials/row-actions.d.ts +3 -1
- package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
- package/dist/lib/components/ui/forms/index.d.ts +1 -0
- package/dist/lib/components/ui/forms/use-server-field-errors.d.ts +21 -0
- package/dist/lib/components/ui/layout/index.d.ts +1 -0
- package/dist/lib/components/ui/layout/record-page/index.d.ts +2 -0
- package/dist/lib/components/ui/layout/record-page/record-details.d.ts +40 -0
- package/dist/lib/components/ui/layout/record-page/record-page.d.ts +27 -0
- package/dist/lib/components/ui/media/upload/network-upload-field/network-upload-field.d.ts +5 -0
- package/dist/lib/components/ui/media/upload/network-upload-field/partials/network-upload-field-items.d.ts +2 -1
- package/dist/lib/components/ui/media/upload/network-upload-field/upload-constraints.d.ts +10 -2
- package/dist/lib/components/ui/overlays/dialog/dialog-body-overflow.d.ts +7 -0
- package/dist/lib/components/ui/overlays/dialog/dialog-body.d.ts +10 -0
- package/dist/lib/components/ui/overlays/dialog/dialog-variants.d.ts +4 -0
- package/dist/lib/components/ui/overlays/dialog/dialog.d.ts +1 -0
- package/dist/lib/components/ui/widgets/chat/chat-conversation-item.d.ts +8 -2
- package/dist/lib/components/ui/widgets/chat/chat-markdown.d.ts +4 -0
- package/dist/lib/components/ui/widgets/chat/chat-message-list.d.ts +8 -1
- package/dist/lib/components/ui/widgets/chat/index.d.ts +2 -0
- package/dist/lib/components/ui/widgets/chat/partials/chat-conversation-item-text.d.ts +2 -1
- package/dist/lib/components/ui/widgets/chat/partials/chat-empty-thread.d.ts +2 -0
- package/dist/lib/components/ui/widgets/chat/proposal/chat-action-proposal-parts.d.ts +24 -0
- package/dist/lib/components/ui/widgets/chat/proposal/chat-action-proposal.d.ts +39 -0
- package/dist/lib/components/ui/widgets/chat/proposal/chat-action-proposal.rules.d.ts +17 -0
- package/dist/lib/components/ui/widgets/chat/proposal/chat-tool-activity.d.ts +16 -0
- package/dist/lib/i18n/resources/en/en.d.ts +47 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +47 -0
- package/dist/lib/i18n/resources/fr/fr.d.ts +47 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +47 -0
- package/dist/lib/i18n/resources/resources.d.ts +94 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/print/output/to-pdf-element.d.ts +6 -0
- package/dist/lib.js +1 -1
- package/dist/main.js +42 -42
- package/dist/map.js +8 -8
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/print.js +135 -131
- package/dist/{public-BuGgOtAl.js → public-CFj2qn7l.js} +9 -9
- package/dist/{rich-code-block-DdHqzzek.js → rich-code-block-B7Teg7-e.js} +5 -5
- package/dist/{sortable-provider-DMyaSfZZ.js → sortable-provider-y5sFhRDj.js} +2 -2
- package/dist/{spinner-ByYdcukU.js → spinner-C5Kb6MWu.js} +1 -1
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-DB3aZZnO.js → theme-toggle-B1JZg7mU.js} +1 -1
- package/dist/{toolbar-items-DbzIKjo7.js → toolbar-items-m_1FTFPQ.js} +3 -3
- package/dist/{translate-material-Dh7MYkHT.js → translate-material-D1ndrd7a.js} +1 -1
- package/dist/{translator-Byhohi1S.js → translator-CtEC_AQa.js} +94 -0
- package/dist/{typography-rUf0mE5a.js → typography-prQHb3gI.js} +1 -1
- package/dist/{use-chat-composer-uUUTRB3Z.js → use-chat-composer-DBiyNXvL.js} +107 -107
- package/dist/{use-heading-spy-CVzW2NM3.js → use-heading-spy-Bzj5mIUZ.js} +179 -87
- package/dist/{use-material-translation-BAYyZOcH.js → use-material-translation-C6H0B4kE.js} +1 -1
- package/dist/{use-return-focus-CQAuk9kh.js → use-return-focus-CkUX1HFl.js} +1 -1
- package/docs/components/buttons-and-actions.md +24 -0
- package/docs/components/data-display.md +33 -0
- package/docs/components/data-table-filter-and-lists.md +117 -5
- package/docs/components/feedback-and-status.md +58 -2
- package/docs/components/form-fields.md +63 -3
- package/docs/components/media-and-files.md +4 -1
- package/docs/components/navigation-and-layout.md +58 -0
- package/docs/components/overlays-and-menus.md +20 -1
- package/docs/components/rich-widgets.md +35 -2
- package/docs/guides/cards-and-tiles.md +4 -2
- package/docs/guides/csp.md +44 -6
- package/docs/guides/forms.md +59 -7
- package/docs/guides/i18n.md +7 -6
- package/docs/guides/printing.md +17 -7
- package/docs/previews/data-table-filter-lists-recordactionsmenu.webp +0 -0
- package/docs/previews/feedback-status-stalenotice.webp +0 -0
- package/docs/previews/media-files-networkuploadfield.webp +0 -0
- package/docs/previews/media-files-s3uploadfield.webp +0 -0
- package/docs/previews/media-files-uploadfield.webp +0 -0
- package/docs/previews/navigation-layout-recordpage.webp +0 -0
- package/docs/previews/rich-widgets-chat-action-proposal.webp +0 -0
- package/docs/roadmap.md +11 -1
- package/docs/troubleshooting.md +126 -8
- package/lib/components/ui/collections/server-table/server-table-phone-row.css +6 -0
- package/lib/components/ui/display/code/code-moonlight.css +37 -0
- package/lib/components/ui/fields/attributes/attribute-values-form/attribute-values-form.css +5 -0
- package/package.json +3 -2
package/docs/guides/csp.md
CHANGED
|
@@ -28,7 +28,7 @@ Content-Security-Policy:
|
|
|
28
28
|
| `style-src-elem 'self' 'nonce-X'` | Your stylesheet and the `<style>` elements that carry the nonce. No component needs `'unsafe-inline'` here, apart from the [known gaps](#known-gaps): see [the `<style>` elements the components inject](#style-elements-the-components-inject) |
|
|
29
29
|
| `style-src-attr 'unsafe-inline'` | The `style="…"` attributes in the server-rendered HTML and those set through `setAttribute('style', …)`. CSP blocks only these two; `el.style.x = …` and `style.setProperty` are not blocked. An attribute cannot carry a nonce, so this is the only way to allow it. It does not allow `<style>` elements or scripts |
|
|
30
30
|
| `font-src 'self'` | The library ships no web font (host Inter yourself: [Inter / the sans font](#inter--the-sans-font)), and KaTeX's fonts are files, never `data:` URIs: see [KaTeX fonts](#katex-fonts) for the one Vite setting the Tailwind route needs |
|
|
31
|
-
| `img-src 'self' data: blob:` | `data:` for the cropper's output, a captured `Camera` photo,
|
|
31
|
+
| `img-src 'self' data: blob:` | `data:` for the cropper's output, a captured `Camera` photo, and the PDF reader's annotation icons (SVG `data:` images; the icons the components draw are inline `<svg>` elements and need none); `blob:` for previews of a file the user picked and for a `Markdown` mermaid diagram (an SVG image from a `blob:` URL). Drop what you do not use |
|
|
32
32
|
| `worker-src 'self'` | The pdf.js worker of `PdfReader`: the build ships it as a file, `dist/pdf.worker.min.mjs`, referenced relative to the module that loads it, so `'self'` is enough with no setting and no `data:`. `configurePdfReader({ workerSrc })` overrides it ([PDF reader guide](pdf-reader.md#content-security-policy)). Monaco: see below |
|
|
33
33
|
| `connect-src 'self'` | `PdfReader` fetches the PDF from its `url` and its pdf.js assets from `assetsUrl`: add those origins when they are not yours |
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ Components outside this table need nothing more. Measured per component:
|
|
|
38
38
|
| --- | --- | --- |
|
|
39
39
|
| `PdfReader` | `worker-src 'self'` (the worker is a file of the package), `connect-src` for the PDF's and the assets' origins, and `script-src 'wasm-unsafe-eval'` for the JPEG 2000 and ICC WebAssembly decoders (without it: a JavaScript fallback for JPEG 2000, device colours for ICC profiles) | worker checked in the same Playwright run (Vite production build under `worker-src 'self'`), the rest read from pdf.js's code: [PDF reader guide](pdf-reader.md#content-security-policy) |
|
|
40
40
|
| `CodeEditor`, `CodeDiff` (Monaco) | `@monaco-editor/react` loads Monaco from `https://cdn.jsdelivr.net/npm/monaco-editor@…/min/vs` by default: that host in `style-src-elem` (Monaco's CSS) and `font-src` (its icon font), plus `worker-src blob:` | not yet verified in a browser. Safe default: allow `https://cdn.jsdelivr.net` in `script-src`, `style-src-elem` and `font-src`, and `worker-src blob:`; or stay on `'self'` by [serving Monaco yourself](#monaco-on-self) |
|
|
41
|
-
| `Markdown`
|
|
41
|
+
| `Markdown` (maths, mermaid, code) | nothing beyond the policy above, in a client render | checked in Chromium under that policy with `style-src-attr 'none'`, no `img-src data:` and no `'unsafe-eval'`: [Markdown under a strict CSP](#markdown-katex-mermaid-and-code-under-a-strict-csp) |
|
|
42
42
|
| `RichTextViewer` (`@nxgt/material/editor/viewer`) | none, but it emits `style` attributes: a text colour, a highlight, a text alignment or a table cell's background in the saved document becomes an inline `style` (only allow-listed properties with plain values survive), so a page that shows such a document needs `style-src-attr 'unsafe-inline'`. A document with none sends no style | measured by reading the renderers; a strict `style-src-attr 'none'` page shows the document without those colours |
|
|
43
43
|
| `Camera` | none: `getUserMedia` is not governed by CSP; the captured photo is a `data:` image | `media-src` for the live preview is not yet verified. Safe default: `media-src 'self' blob:` (a `srcObject` stream usually needs nothing) |
|
|
44
44
|
| `MediaPlayer` with `hlsStreaming()` | `connect-src` and `media-src blob:` for the stream; `worker-src blob:` only with `config: { enableWorker: true }` (the adapter defaults to no worker) | see the [streaming guide](media-streaming.md#content-security-policy) |
|
|
@@ -201,8 +201,8 @@ instead.
|
|
|
201
201
|
`style-src-elem 'self' 'nonce-X'` is enough for every component below: your
|
|
202
202
|
stylesheet carries the CSS the dependencies used to inject, and under a
|
|
203
203
|
`NonceProvider` the few `<style>` elements left carry the nonce. No route
|
|
204
|
-
needs `'unsafe-inline'` in `style-src-elem` for them;
|
|
205
|
-
font fallback are the [known gaps](#known-gaps).
|
|
204
|
+
needs `'unsafe-inline'` in `style-src-elem` for them; the mindmap diagrams of
|
|
205
|
+
mermaid and pdf.js's font fallback are the [known gaps](#known-gaps).
|
|
206
206
|
|
|
207
207
|
| Component | What it needs | How |
|
|
208
208
|
| --- | --- | --- |
|
|
@@ -261,12 +261,50 @@ when the page carries it nowhere. `setCspNonce` does nothing on a server (no
|
|
|
261
261
|
`document`), so a request's nonce never lands in a module global there: see
|
|
262
262
|
[SSR](#ssr).
|
|
263
263
|
|
|
264
|
+
### Markdown (KaTeX, mermaid and code) under a strict CSP
|
|
265
|
+
|
|
266
|
+
The full `Markdown` (and `ChatMarkdown`) needs no relaxed directive: no
|
|
267
|
+
`'unsafe-inline'`, no `'unsafe-eval'`, no `style-src-attr`, no `img-src data:`.
|
|
268
|
+
Only `img-src blob:` (already in the policy above) for the diagrams. The
|
|
269
|
+
`Data display/Markdown > StrictCsp` story plays maths, two flowcharts (one with
|
|
270
|
+
an `<img>` in a label, removed before the render, since mermaid would load it in the page) and a code block in a document served
|
|
271
|
+
`script-src 'self' 'nonce-X'` (no `'unsafe-eval'`),
|
|
272
|
+
`style-src-elem 'self' 'nonce-X'`, `style-src-attr 'none'`,
|
|
273
|
+
`img-src 'self' blob:` and `font-src 'self'`, and expects not one violation.
|
|
274
|
+
What the app does: wrap the app in [`NonceProvider`](#nonceprovider) (or let
|
|
275
|
+
the page carry the nonce in its meta tag).
|
|
276
|
+
|
|
277
|
+
- **Code blocks**: the scheme's colours are classes of the stylesheet
|
|
278
|
+
(`data-scheme`), and `maxHeight` is a custom property written through the
|
|
279
|
+
CSSOM in a layout effect (a server-rendered block is full height until
|
|
280
|
+
hydration).
|
|
281
|
+
- **KaTeX**: on a client render React writes its styles through the CSSOM.
|
|
282
|
+
Its fonts are files ([KaTeX fonts](#katex-fonts)).
|
|
283
|
+
- **Mermaid**: the diagram stays an image, now from a `blob:` URL (revoked when
|
|
284
|
+
the diagram goes), so the SVG's own `<style>` and `style` attributes live in
|
|
285
|
+
the image and the page needs neither `data:` nor `style-src-attr` for them.
|
|
286
|
+
While a diagram renders, mermaid builds it in a temporary element of the
|
|
287
|
+
page and sanitises it with DOMPurify, which a strict policy reports. For
|
|
288
|
+
that render only, and only for the elements of that diagram, a short-lived
|
|
289
|
+
scope gives mermaid's `<style>` the nonce, writes its `style` attributes
|
|
290
|
+
through the CSSOM and parks the `style` attributes of the string DOMPurify
|
|
291
|
+
parses (patches of `Document.prototype.createElement`,
|
|
292
|
+
`Element.prototype.setAttribute` and `DOMParser.prototype.parseFromString`;
|
|
293
|
+
each is a pass-through once the last render ends and is restored only if
|
|
294
|
+
no other script patched it since). Unrelated calls, such as an editor
|
|
295
|
+
parsing HTML, are left alone.
|
|
296
|
+
- **Left**: a page whose HTML the server renders with math carries KaTeX's
|
|
297
|
+
`style="…"` attributes in the markup, which `style-src-attr 'none'` blocks
|
|
298
|
+
(the formula loses its spacing until `style-src-attr 'unsafe-inline'`, or a
|
|
299
|
+
client-only render of the `Markdown`). Mermaid's mindmap and architecture
|
|
300
|
+
diagrams add a `<style>` to `<head>` ([known gaps](#known-gaps)).
|
|
301
|
+
|
|
264
302
|
### Known gaps
|
|
265
303
|
|
|
266
304
|
Not fixable by configuration yet, and still reported by a strict policy:
|
|
267
305
|
|
|
268
|
-
- the `<style>` element
|
|
269
|
-
|
|
306
|
+
- the `<style>` element cytoscape adds to `<head>` for mermaid's mindmap and
|
|
307
|
+
architecture diagrams (flowcharts, sequences and the like add none);
|
|
270
308
|
- the font fallback of pdf.js, when a PDF font is not embedded;
|
|
271
309
|
- this repository's Storybook, or an app that imports react-use's barrel
|
|
272
310
|
itself on a dev server: react-use served as one module, whose `useCss`
|
package/docs/guides/forms.md
CHANGED
|
@@ -279,10 +279,11 @@ the form, on a translucent, blurred background, so Cancel and Save stay in
|
|
|
279
279
|
reach while the fields scroll under them; a form shorter than the screen
|
|
280
280
|
shows them right under its fields.
|
|
281
281
|
|
|
282
|
-
- In a `DialogContent` or a `SheetContent`, which are capped to the screen
|
|
282
|
+
- In a `DialogContent` without a `DialogBody`, or a `SheetContent`, which are capped to the screen
|
|
283
283
|
and scroll themselves, the actions stick to the dialog's or the sheet's
|
|
284
284
|
bottom. A `DrawerContent` does not scroll: a form that is its direct child
|
|
285
285
|
is the scroll box there.
|
|
286
|
+
- A long form in a `Dialog` can pin its own header and `DialogFooter` instead: put the fields in a `DialogBody` and only the body scrolls (see the Dialog section of the overlays page). Do not combine it with `form-actions`: they would stick inside the body, so use a `DialogFooter` for the buttons and `form="id"` on the submit button.
|
|
286
287
|
- In an `AlertDialogContent` (the rich text editor's edit dialogs use one),
|
|
287
288
|
the actions stick to its bottom the same way, over its padding.
|
|
288
289
|
- `ConfirmationDialog` holds a form when you pass it `formOptions` (a schema, its `defaultValues` and `renderForm`): it renders the
|
|
@@ -608,7 +609,7 @@ export function SupplierForm({ onSave }: { onSave: (values: Values) => void }) {
|
|
|
608
609
|
|
|
609
610
|
| Wrapper | Value | Notes |
|
|
610
611
|
| --- | --- | --- |
|
|
611
|
-
| `ContactsFormField` | `Contact[]` | Edited in place by default (one `primary` per type); `editing="dialog"` goes through `ContactDialog`. `ContactType` is `EMAIL`, `FAX`, `MOBILE`, `PHONE`, `WEBSITE` |
|
|
612
|
+
| `ContactsFormField` | `Contact[]` | Edited in place by default (one `primary` per type); `editing="dialog"` goes through `ContactDialog`. `ContactType` is `EMAIL`, `FAX`, `MOBILE`, `PHONE`, `WEBSITE`; `types={['EMAIL', 'PHONE']}` limits the offered types and `primary={false}` hides the star (inline only) |
|
|
612
613
|
| `OpeningHoursFormField` | `OpeningHour[]` | Edits the week inline by default (`weekStartsOn` and `locale` apply there); `editing="dialog"` keeps the list and dialog, where `showAddAllDays` adds Monday–Friday 09:00–17:00 and a closed weekend in one click |
|
|
613
614
|
| `PostalAddressFormField` | `PostalAddressFieldValue` | Deleting the address writes `{}`, not `null` |
|
|
614
615
|
| `AttributesFormField` | `Attribute[]` | A typed attribute editor, in place by default (rows that open, drag and keyboard reorder that sets `priority`, unique names); `editing="dialog"` keeps the list and dialog. Its `label` prop is required |
|
|
@@ -628,7 +629,11 @@ export function SupplierForm({ onSave }: { onSave: (values: Values) => void }) {
|
|
|
628
629
|
`form.setValue()` replace the list it shows.
|
|
629
630
|
- `AttributeValuesFormFieldSet` is the other half: given `attributes`, it
|
|
630
631
|
renders one input per attribute and writes `{ [attribute.name]: value }` to
|
|
631
|
-
its `name`.
|
|
632
|
+
its `name`. It reads the value the form already holds at `name` (and shows
|
|
633
|
+
a `reset()` or `setValue()`), so `defaultValues` is only the fallback while
|
|
634
|
+
the form value is empty (`undefined`, `null` or `{}`). Its fields fill the row, and `hideLabel` keeps
|
|
635
|
+
`label` as the group's accessible name without drawing it, under a
|
|
636
|
+
`FormSection` that already has the title.
|
|
632
637
|
|
|
633
638
|
### Rows a user adds and removes
|
|
634
639
|
|
|
@@ -786,8 +791,9 @@ form.setFocus('email'); // or without an error
|
|
|
786
791
|
|
|
787
792
|
## A form that is quick to fill
|
|
788
793
|
|
|
789
|
-
`useFastForm` and `
|
|
790
|
-
|
|
794
|
+
`useFastForm`, `useFocusOnRefusal` and `useServerFieldErrors`
|
|
795
|
+
(`@nxgt/material/components`) are the three habits of a record form, in one
|
|
796
|
+
place; the first two are below, the third under [Server errors](#server-errors). `useFastForm(props, first, active?)`
|
|
791
797
|
is `useForm` with `mode: 'onTouched'` (an error shows once the user leaves the
|
|
792
798
|
field, then follows each change; a `mode` in `props` wins) that also focuses
|
|
793
799
|
the `first` field when the form opens, so typing starts at once. It waits a
|
|
@@ -821,8 +827,24 @@ Storybook: `Guides/Forms/useFastForm`. If the focus does not move, see
|
|
|
821
827
|
|
|
822
828
|
## Server errors
|
|
823
829
|
|
|
824
|
-
|
|
825
|
-
|
|
830
|
+
`useServerFieldErrors(form, fieldErrors)` (`@nxgt/material/components`) puts an
|
|
831
|
+
action's `{ path: message }` field errors on the form, each under its field
|
|
832
|
+
with `type: 'server'`, and focuses the first. It runs when the `fieldErrors`
|
|
833
|
+
object changes, so pass the answer's own object (one per answer) and
|
|
834
|
+
`undefined` while a request runs or when the answer is not a refusal; entries
|
|
835
|
+
with an empty message are skipped, and the keys must be names of the form's
|
|
836
|
+
fields.
|
|
837
|
+
|
|
838
|
+
```tsx
|
|
839
|
+
import { useFastForm, useServerFieldErrors } from '@nxgt/material/components';
|
|
840
|
+
|
|
841
|
+
const form = useFastForm<Values>({ defaultValues }, 'email');
|
|
842
|
+
useServerFieldErrors(form, actionData?.fieldErrors); // { email: 'Already used' }
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
It pairs with `useFastForm` and `useFocusOnRefusal` above. To write the same
|
|
846
|
+
mapping by hand, or to put anything that belongs to no field under `root`,
|
|
847
|
+
shown in an `Alert`, use `setError`:
|
|
826
848
|
|
|
827
849
|
```tsx
|
|
828
850
|
import { useForm } from 'react-hook-form';
|
|
@@ -870,6 +892,36 @@ export function SignInForm() {
|
|
|
870
892
|
- `Alert` `variant` is one of `primary`, `secondary`, `error`, `success`,
|
|
871
893
|
`info`, `warning`, `foreground`.
|
|
872
894
|
|
|
895
|
+
### A save refused because another write came first
|
|
896
|
+
|
|
897
|
+
When the API refuses a versioned save (`stale-version`), keep the form as it is
|
|
898
|
+
and show a `StaleNotice` above it. It takes the focus and scrolls into view on
|
|
899
|
+
mount, and its one action reads the record again. There is no overwrite.
|
|
900
|
+
|
|
901
|
+
```tsx
|
|
902
|
+
import type { FormEventHandler } from 'react';
|
|
903
|
+
import type { UseFormReturn } from 'react-hook-form';
|
|
904
|
+
import { Button, StaleNotice, TextFormField } from '@nxgt/material/components';
|
|
905
|
+
|
|
906
|
+
type Props = {
|
|
907
|
+
form: UseFormReturn<{ title: string }>;
|
|
908
|
+
stale: boolean; // the save was refused with `stale-version`
|
|
909
|
+
reload: () => void; // read the record again
|
|
910
|
+
reloading: boolean;
|
|
911
|
+
onSubmit: FormEventHandler<HTMLFormElement>;
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
function EditNote({ form, stale, reload, reloading, onSubmit }: Props) {
|
|
915
|
+
return (
|
|
916
|
+
<form onSubmit={onSubmit} className="grid gap-4">
|
|
917
|
+
{stale && <StaleNotice onReload={reload} busy={reloading} />}
|
|
918
|
+
<TextFormField control={form.control} name="title" label="Title" />
|
|
919
|
+
<Button type="submit">Save</Button>
|
|
920
|
+
</form>
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
```
|
|
924
|
+
|
|
873
925
|
## Dialogs and steps
|
|
874
926
|
|
|
875
927
|
### A form in a dialog
|
package/docs/guides/i18n.md
CHANGED
|
@@ -236,7 +236,7 @@ the camera's button, toggle and image (`material.camera.capture`, `captured`,
|
|
|
236
236
|
barcode accessible names, the calendar's navigation tooltips
|
|
237
237
|
(`material.calendar.*`), the data table's selection checkboxes, the filter's
|
|
238
238
|
presets and `Min`/`Max` fields, the `variant="4"` spinner's label,
|
|
239
|
-
`ScrollToTop`, the network upload field's `Retry
|
|
239
|
+
`ScrollToTop`, the network upload field's `Retry` and `Remove {name}`, the map's
|
|
240
240
|
`You are here` and zoom buttons, and the icon-only close button of
|
|
241
241
|
`DialogClose`, `DrawerClose`, `SheetClose` and `SheetContent`'s
|
|
242
242
|
`withCloseButton`. A range `Slider`'s readout inputs read
|
|
@@ -320,11 +320,11 @@ those words as an optional third argument); the `aria-label` of `Breadcrumb`
|
|
|
320
320
|
and `Pagination` and the breadcrumb ellipsis's "More"
|
|
321
321
|
(`material.breadcrumb.*`, `material.pagination.label`), and `DocHeader`'s
|
|
322
322
|
breadcrumb (`material.doc.breadcrumb`); `MessageCard`'s "Unread"
|
|
323
|
-
(`material.cards.unread`); the network upload field's "Replace {name}", "Upload
|
|
323
|
+
(`material.cards.unread`); the network upload field's "Replace {name}", "Remove {name}", "Upload
|
|
324
324
|
failed", its type, size and count errors (`material.upload.replace-named`,
|
|
325
|
-
`failed`, `invalid-type`, `too-large`, `max-files`; an error keeps the language
|
|
325
|
+
`remove-named`, `failed`, `invalid-type`, `too-large`, `max-files`; an error keeps the language
|
|
326
326
|
it was raised in) and the drop zone's "Max {size}" and "Up to {count} files"
|
|
327
|
-
(`material.upload.dropzone.max-size` / `max-files`); the editor's
|
|
327
|
+
(`material.upload.dropzone.max-size` / `max-files`) and the names `accept` wildcards read as (`material.upload.accept.images`, `audio`, `video`, `text`, `application`); the editor's
|
|
328
328
|
placeholder, which follows a language change without rebuilding the editor,
|
|
329
329
|
and its emoji buttons (`material['rich-text-editor'].placeholder` /
|
|
330
330
|
`insert-emoji`); `CircularProgress`'s "Progress: {value}%"
|
|
@@ -485,8 +485,9 @@ and description and against the English ones, ignoring accents: in French,
|
|
|
485
485
|
|
|
486
486
|
Some strings are still English in every language, among them the filter's
|
|
487
487
|
validation messages, the camera's error messages, the editor's callout type
|
|
488
|
-
names and default block content.
|
|
489
|
-
the provider's language
|
|
488
|
+
names and default block content. The filter chips' dates, and file sizes outside the upload field (`2 KB`),
|
|
489
|
+
are not formatted for the provider's language; the upload field writes its
|
|
490
|
+
size in it (`10,4 ko`).
|
|
490
491
|
|
|
491
492
|
The emoji picker's category tabs are icon-only; their name, read by screen
|
|
492
493
|
readers and shown as a tooltip, comes from `material.emoji.groups.<group>`.
|
package/docs/guides/printing.md
CHANGED
|
@@ -1013,7 +1013,7 @@ it:
|
|
|
1013
1013
|
| `heading` | `heading(text, level?)` | A title, level `1`, `2` or `3` |
|
|
1014
1014
|
| `text` | `text(content, { variant?, style? })` | A paragraph: a string, or runs `{ text, bold?, italic?, color?, href? }`; `variant` is `'body'`, `'muted'`, `'caption'` or `'label'` |
|
|
1015
1015
|
| `stack` / `row` | `stack(children, { gap?, style?, keepTogether? })`, `row(children, { gap?, align?, justify?, style? })` | Children one under the other, or side by side |
|
|
1016
|
-
| `section` | `section(title, children, { keepTogether? })` | A titled block, as `PrintSection`; `keepTogether` as on a `stack
|
|
1016
|
+
| `section` | `section(title, children, { keepTogether? })` | A titled block, as `PrintSection`; `keepTogether` as on a `stack`; a block taller than a page breaks normally, see [Keeping a block on one page](#keeping-a-block-on-one-page) |
|
|
1017
1017
|
| `table` | `table({ columns, rows, footer?, labelTotals?, striped?, repeatHeader?, keepRowsWhole?, emptyText?, hideWhenEmpty? })`, or `dataTable(columns, rows, options?)` from [column definitions](#tables-from-column-definitions-datatable-and-keyvalueof) | A table of `PrintValue`s, as `PrintDataTable`; a row or a cell may carry a `style`, see [Row and cell styles](#row-and-cell-styles-in-a-table); with `labelTotals`, an empty first cell of a `footer` row reads "Total" in the document's language |
|
|
1018
1018
|
| `keyValue` | `keyValue(items, { columns?, keepEmpty? })`, or `keyValueOf(fields, data, options?)` from column definitions | Label and value pairs, as `PrintKeyValue` |
|
|
1019
1019
|
| `totals` | `totals(rows)` | Label and value rows; `emphasis` makes one bold |
|
|
@@ -1140,12 +1140,22 @@ section(group.title, rows, { keepTogether: true });
|
|
|
1140
1140
|
stack([heading('Result'), table(result)], { keepTogether: true });
|
|
1141
1141
|
```
|
|
1142
1142
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1143
|
+
A block taller than a page breaks normally. react-pdf cannot split a block
|
|
1144
|
+
that opted out of wrapping (it overflows and is cut off at the edge), and
|
|
1145
|
+
cannot measure ahead, so for a document as data `renderPdfBuffer`,
|
|
1146
|
+
`renderPdfBlob`, `renderPdfStream`, `downloadPdf`, `openPdf` and `pdfResponse`
|
|
1147
|
+
first render each `keepTogether` block alone, on a page like the one it sits
|
|
1148
|
+
on, and drop the option of those that need more than one page. A block that
|
|
1149
|
+
fits keeps it, so its output does not change; a document with no
|
|
1150
|
+
`keepTogether` pays for no extra pass. The measure is made at the page's
|
|
1151
|
+
width: a block inside a `row` is measured with that row, but a parent's padding or narrower style is not applied.
|
|
1152
|
+
|
|
1153
|
+
This holds for a document as data only. `<PrintSection keepTogether>` in a
|
|
1154
|
+
`PrintDocument` element, and a document you render yourself with
|
|
1155
|
+
`renderPdfElement`, are not measured: use `keepTogether` there on a block
|
|
1156
|
+
shorter than a page, or call `resolveKeepTogether(doc)` from
|
|
1157
|
+
`@nxgt/material-core/pdf/render` first and render its result. For a long
|
|
1158
|
+
table, `keepRowsWhole` and `repeatHeader` keep the rows apart from each other.
|
|
1149
1159
|
|
|
1150
1160
|
### Row and cell styles in a table
|
|
1151
1161
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/roadmap.md
CHANGED
|
@@ -6,6 +6,8 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
6
6
|
|
|
7
7
|
## Now
|
|
8
8
|
|
|
9
|
+
- **Calculator** — `@nxgt/material/calculator` adds `Calculator`, in six variants (Standard, Scientific, point of sale, Financial, Unit conversion, Programmer) chosen by `variant` or by its own switcher, and `CalculatorField` / `CalculatorFormField`, a number field whose button opens it in a popover, or a bottom sheet on a phone. Every variant shows the whole expression above the result, honours operator precedence and computes in exact decimals (0.1 + 0.2 = 0.3) without `eval`, so a strict CSP holds. A history tape (persisted, with an opt-out), memory keys, the physical keyboard, copy and paste, locale formatting and screen-reader announcements come with each; haptic and sound feedback are opt-in. Point of sale charges through `onCharge(total)`; currency rates come from the app. React only for now; the Vue port is tracked in [#2100](https://github.com/softistx/nxgt-material/issues/2100). Tracked in [#2101](https://github.com/softistx/nxgt-material/issues/2101).
|
|
10
|
+
|
|
9
11
|
- **Barcodes and label sheets** — `@nxgt/material/barcode` draws Code 128
|
|
10
12
|
and Data Matrix as accessible SVG (themed, dark-aware, black on white in
|
|
11
13
|
print) and lays labels out on a sheet to print: label size, columns, rows,
|
|
@@ -15,6 +17,10 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
15
17
|
- **Toast texts follow app translations** — the toast presets read the
|
|
16
18
|
app's `I18nProvider` `resources`, so an override under `material.*`
|
|
17
19
|
changes their wording ([#1855](https://github.com/softistx/nxgt-material/issues/1855)).
|
|
20
|
+
- **A record page layout** — `RecordPage` and `RecordDetails` lay out one
|
|
21
|
+
record: a header card, the content, and its facts as a side panel from `xl`
|
|
22
|
+
that stacks after or before the content, with a created / updated line
|
|
23
|
+
([#1689](https://github.com/softistx/nxgt-material/issues/1689)).
|
|
18
24
|
- **A decision bar for phone pages** — `StickyActionBar` holds a long page's
|
|
19
25
|
main decision at the bottom of a phone, above the keyboard and clear of the
|
|
20
26
|
safe areas, and inline from `md`
|
|
@@ -157,6 +163,8 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
157
163
|
|
|
158
164
|
## Next
|
|
159
165
|
|
|
166
|
+
- **Layouts: adaptive navigation, headers and page layouts** — `ActivityLayout` takes `navigation` (a bottom bar, a rail with a FAB, standard and modal drawers, a collapsible sidebar, a top navigation with dropdowns, two-level navigation or a floating toolbar, chosen for phone, tablet and desktop) and `header` (small, center-aligned, medium and large collapsing on scroll, a search bar with full-screen search on a phone, sticky tabs, hide on scroll); each piece is also exported. Page layouts grow with it: an inspector pane and URL state for `ListDetailsLayout`, phone modes for `PaneLayout`, spans and masonry for `ResponsiveGrid`, tabs for `Kanban` on a phone, and new settings, dashboard (rearranging tiles), editor and landing layouts, each with a screen recipe. Everything is opt-in; the shell's default is unchanged. Tracked in [#2103](https://github.com/softistx/nxgt-material/issues/2103).
|
|
167
|
+
|
|
160
168
|
- **FilterBar "Add a filter" stays on its row** — the button no longer wraps
|
|
161
169
|
alone onto a second row on a desktop screen ([#1752](https://github.com/softistx/nxgt-material/issues/1752)).
|
|
162
170
|
|
|
@@ -241,7 +249,6 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
241
249
|
slot; the bar's `views` slot is planned with it. They are meant to replace
|
|
242
250
|
the legacy `FilterPresets`. Tracked in [#1876](https://github.com/softistx/nxgt-material/issues/1876).
|
|
243
251
|
|
|
244
|
-
- **RecordActionsMenu** — one menu for a record's lifecycle: archive with an Undo toast (or confirm first with `archivePolicy="confirm"`), restore, and delete that always confirms and shows the backend's refusal, with `ConfirmDialog` gaining `details` and `refusal` slots.
|
|
245
252
|
- **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.
|
|
246
253
|
|
|
247
254
|
## Later
|
|
@@ -364,6 +371,9 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
364
371
|
|
|
365
372
|
## Shipped
|
|
366
373
|
|
|
374
|
+
- **RecordActionsMenu (next minor)** — one menu for a record's lifecycle: archive with an Undo toast (or confirm first with `archivePolicy="confirm"`), restore, and delete that always confirms and shows the backend's refusal, with `ConfirmDialog` gaining `details` and `refusal` slots.
|
|
375
|
+
- **Assistant approval card (next minor)** — `ChatActionProposal` shows an action an assistant proposes, with its status, deadline and Confirm / Reject, and `ChatToolActivity` is a compact row for a tool step in a chat thread ([#2134](https://github.com/softistx/nxgt-material/issues/2134)).
|
|
376
|
+
|
|
367
377
|
- **Link navigation (3.66.0)** — `TabsNav` and `SegmentedNav` render route tabs and segmented navigation as a `nav` of links with `aria-current="page"`, looking like Tabs and the segmented ToggleGroup, plus `useTabParam`, `skipTabChange` and `tabIds` to keep the current tab in the URL. Tracked in [#2029](https://github.com/softistx/nxgt-material/issues/2029).
|
|
368
378
|
|
|
369
379
|
- **Smaller, easier-to-read components (3.58.0)** — the library's own source
|
package/docs/troubleshooting.md
CHANGED
|
@@ -11,6 +11,7 @@ words in your error.
|
|
|
11
11
|
- [`Cannot find package '@react-pdf/renderer'`](#cannot-find-package-react-pdfrenderer)
|
|
12
12
|
- [`Cannot find package 'react-router'` after upgrading to 3.0](#cannot-find-package-react-router-after-upgrading-to-30) (and `ERESOLVE` on `react` or `react-router`; `Element type is invalid` for `ViewTransition` or `browser is not a function` from a stale Storybook or Vitest pre-bundle)
|
|
13
13
|
- [`Conflicting peer dependency: @react-pdf/renderer` (`ERESOLVE`)](#conflicting-peer-dependency-react-pdfrenderer)
|
|
14
|
+
- [`AttributeValuesFormFieldSet` shows empty fields, or ignores my `defaultValues`](#attributevaluesformfieldset-shows-empty-fields-or-ignores-my-defaultvalues)
|
|
14
15
|
- [`Cannot find package 'leaflet'`](#cannot-find-package-leaflet)
|
|
15
16
|
- [`hls.js could not be loaded. Install it (`npm i hls.js`) to play HLS streams in this browser.`](#hlsjs-could-not-be-loaded-install-it-npm-i-hlsjs-to-play-hls-streams-in-this-browser)
|
|
16
17
|
- [`This browser cannot play HLS streams.`](#this-browser-cannot-play-hls-streams)
|
|
@@ -66,7 +67,10 @@ words in your error.
|
|
|
66
67
|
- [A Button's label overflows a card on a phone](#a-buttons-label-overflows-a-card-on-a-phone)
|
|
67
68
|
- [A ResponsiveButton no longer shows its tooltip next to its visible label](#a-responsivebutton-no-longer-shows-its-tooltip-next-to-its-visible-label)
|
|
68
69
|
- [An input now has `placeholder=" "` in the DOM](#an-input-now-has-placeholder--in-the-dom)
|
|
70
|
+
- ["Select all" runs under the sort, "Filters" or the actions in a cards or tiles list on a phone](#select-all-runs-under-the-sort-filters-or-the-actions-in-a-cards-or-tiles-list-on-a-phone)
|
|
69
71
|
- [A dialog or sheet scrolls, or clips a child, after upgrading](#a-dialog-or-sheet-scrolls-or-clips-a-child-after-upgrading)
|
|
72
|
+
- [A `DialogBody`'s header and footer do not stay pinned](#a-dialogbodys-header-and-footer-do-not-stay-pinned)
|
|
73
|
+
- [A form's `form-actions` bar sticks inside a `DialogBody`](#a-forms-form-actions-bar-sticks-inside-a-dialogbody)
|
|
70
74
|
- [A form's fields no longer scroll in their own box after upgrading](#a-forms-fields-no-longer-scroll-in-their-own-box-after-upgrading)
|
|
71
75
|
- [My activity content lost its side padding, or the rail's entries moved up](#my-activity-content-lost-its-side-padding-or-the-rails-entries-moved-up)
|
|
72
76
|
- [axe reports `target-size` on a control under a form's sticky actions](#axe-reports-target-size-on-a-control-under-a-forms-sticky-actions)
|
|
@@ -380,6 +384,7 @@ words in your error.
|
|
|
380
384
|
- [`` [@nxgt/material] DataTable: `tableRef` stays null in the `cards` layout; use `focusRef`. ``](#nxgtmaterial-datatable-tableref-stays-null-in-the-cards-layout-use-focusref)
|
|
381
385
|
- [`defineCodeTheme: "keyword" must be a hex colour`](#definecodetheme-keyword-must-be-a-hex-colour-rgb-or-rrggbb)
|
|
382
386
|
- [A `CodeEditor` shows its code in one colour after another Monaco editor mounts](#a-codeeditor-shows-its-code-in-one-colour-after-another-monaco-editor-mounts)
|
|
387
|
+
- [`primary={false}` has no effect on a `ContactsFormField` with `editing="dialog"`](#primaryfalse-has-no-effect-on-a-contactsformfield-with-editingdialog)
|
|
383
388
|
|
|
384
389
|
**PDF reader**
|
|
385
390
|
|
|
@@ -535,7 +540,28 @@ bun add @react-pdf/renderer@^4.9
|
|
|
535
540
|
`--legacy-peer-deps` silences the error but keeps a renderer the print
|
|
536
541
|
entry is not tested against.
|
|
537
542
|
|
|
538
|
-
### `
|
|
543
|
+
### `AttributeValuesFormFieldSet` shows empty fields, or ignores my `defaultValues`
|
|
544
|
+
|
|
545
|
+
**When:** the set comes up empty although you pass `defaultValues`, or shows
|
|
546
|
+
other values than the ones you passed.
|
|
547
|
+
|
|
548
|
+
**Why:** the set reads the form value at `name` first. `defaultValues`
|
|
549
|
+
is only a fallback while that value is empty: `undefined`, `null` or an object
|
|
550
|
+
with no keys. A form that already holds `{ color: 'red' }` at `name` wins over
|
|
551
|
+
`defaultValues`; a form that holds `{ color: undefined }` has a key, so it is
|
|
552
|
+
not empty either.
|
|
553
|
+
|
|
554
|
+
**Fix:** put the values in the form, where every other field reads them:
|
|
555
|
+
|
|
556
|
+
```tsx
|
|
557
|
+
const form = useForm({ defaultValues: { attributes: { size: 'M' } } });
|
|
558
|
+
// or, for a record loaded later
|
|
559
|
+
form.reset({ attributes: record.attributes });
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Keep `defaultValues` on the set only for a fallback when the form holds nothing.
|
|
563
|
+
|
|
564
|
+
## `Cannot find package 'leaflet'`
|
|
539
565
|
|
|
540
566
|
**When:** at build or at the first import of `@nxgt/material/map`. The missing
|
|
541
567
|
name may also be `react-leaflet`, `@react-leaflet/core`,
|
|
@@ -613,6 +639,14 @@ never receives the press.
|
|
|
613
639
|
forwards `ref` and `...props` to its root. Or skip `trigger` and open it from
|
|
614
640
|
your own state with `open` / `onOpenChange`.
|
|
615
641
|
|
|
642
|
+
### A test that read the upload hint now sees "JPEG, PNG, WebP" or "Images"
|
|
643
|
+
|
|
644
|
+
**When:** after upgrading, a test or snapshot that matched the drop zone's hint
|
|
645
|
+
(`image/jpeg,image/png · Max 2 MB`, `image/*`) fails.
|
|
646
|
+
**Why:** the hint shows short names derived from `accept` instead of the raw
|
|
647
|
+
list: `JPEG, PNG · Max 2 MB`, `Images`.
|
|
648
|
+
**Fix:** match the names, or pass `acceptLabel="..."` to fix the text.
|
|
649
|
+
|
|
616
650
|
### `This browser cannot play HLS streams.`
|
|
617
651
|
|
|
618
652
|
**When:** an HLS track on a browser without Media Source Extensions where
|
|
@@ -1427,6 +1461,18 @@ your own through `className`. With `asChild`, put the label in a `<span>` (on a
|
|
|
1427
1461
|
</Button>
|
|
1428
1462
|
```
|
|
1429
1463
|
|
|
1464
|
+
### "Select all" runs under the sort, "Filters" or the actions in a cards or tiles list on a phone
|
|
1465
|
+
|
|
1466
|
+
**When:** below 768px, a `ServerTable` in `cards` or `tiles` with `filter` and
|
|
1467
|
+
`actions` draws "Select all" over the sort menu and "Filters (N)" when an action
|
|
1468
|
+
has a long label ("Add a measurement unit").
|
|
1469
|
+
**Why:** up to 3.70.0, the label of row 2 neither shrank nor truncated, and the
|
|
1470
|
+
end group (sort, "Filters (N)", actions) never gave up its width.
|
|
1471
|
+
**Fix:** update. The "Select all" text now ends in "…" (the box keeps its name,
|
|
1472
|
+
"Select all on this page"), and an end group too wide for the row wraps to its
|
|
1473
|
+
own line under it. Until then, use a `Fab` for the create action on phones, or
|
|
1474
|
+
a short or icon-only label.
|
|
1475
|
+
|
|
1430
1476
|
### A dialog or sheet scrolls, or clips a child, after upgrading
|
|
1431
1477
|
|
|
1432
1478
|
**When:** after upgrading, a tall `DialogContent`, `AlertDialogContent` or
|
|
@@ -1448,6 +1494,38 @@ with `className="max-h-none overflow-visible"`; to change the cap, pass your own
|
|
|
1448
1494
|
For a dialog that should grow taller than the screen and scroll inside the
|
|
1449
1495
|
overlay instead of capping, use `DialogScrollContent`.
|
|
1450
1496
|
|
|
1497
|
+
With a `DialogBody` as a direct child, the content is `overflow-hidden` (a
|
|
1498
|
+
clipped flex column) and only the body scrolls, so a child that sticks out of
|
|
1499
|
+
the content is clipped there too; keep popovers in a portal.
|
|
1500
|
+
|
|
1501
|
+
### A `DialogBody`'s header and footer do not stay pinned
|
|
1502
|
+
|
|
1503
|
+
**When:** a long dialog with a `DialogBody` still scrolls as a whole, or the
|
|
1504
|
+
footer sits at the end of the fields.
|
|
1505
|
+
**Why:** the content only turns into a column when the `DialogBody` is a
|
|
1506
|
+
*direct* child of `DialogContent` (`has-[>[data-slot=dialog-body]]`). Inside a
|
|
1507
|
+
`<form>` or another wrapper it is not. Another cause: a `max-h-none` or
|
|
1508
|
+
`overflow-visible` in the content's `className`.
|
|
1509
|
+
**Fix:** make the body a direct child, and put the `<form>` around the whole
|
|
1510
|
+
content, or keep the body inside and submit with `form="id"` on the footer button:
|
|
1511
|
+
|
|
1512
|
+
```tsx
|
|
1513
|
+
<DialogContent>
|
|
1514
|
+
<DialogHeader>…</DialogHeader>
|
|
1515
|
+
<DialogBody>…</DialogBody>
|
|
1516
|
+
<DialogFooter>…</DialogFooter>
|
|
1517
|
+
</DialogContent>
|
|
1518
|
+
```
|
|
1519
|
+
|
|
1520
|
+
### A form's `form-actions` bar sticks inside a `DialogBody`
|
|
1521
|
+
|
|
1522
|
+
**When:** a `.form-grid-layout` form with `form-actions` in a `DialogBody` shows
|
|
1523
|
+
its Save bar at the bottom of the body, not of the dialog.
|
|
1524
|
+
**Why:** the bar is sticky at the bottom of whatever scrolls, and the body now
|
|
1525
|
+
scrolls.
|
|
1526
|
+
**Fix:** use the `DialogFooter` for the buttons, with `form="my-form"` on the
|
|
1527
|
+
submit button and `id="my-form"` on the form, and leave `form-actions` out.
|
|
1528
|
+
|
|
1451
1529
|
### A form's fields no longer scroll in their own box after upgrading
|
|
1452
1530
|
|
|
1453
1531
|
**When:** after upgrading, a `.form-grid-layout` form grows with the page
|
|
@@ -2128,6 +2206,12 @@ settles, with `focusRef` on the table. It matches the row by id, not by label.
|
|
|
2128
2206
|
See "Handing the focus back to a row" in
|
|
2129
2207
|
[RowMenu](components/data-table-filter-and-lists.md#handing-the-focus-back-to-a-row).
|
|
2130
2208
|
|
|
2209
|
+
### A `RecordActionsMenu` or `ConfirmDialog` refusal shows no action, or its link reloads the page
|
|
2210
|
+
|
|
2211
|
+
**When:** `send` rejects (or a `ConfirmDialog`'s `onConfirm` throws) with an error that should offer a link, but the dialog or toast shows only the message; or the link of a toast action reloads the whole app.
|
|
2212
|
+
**Why:** only an `ActionRefusal` carries an action (a plain `Error`, or a rethrown copy of its message, does not). A toast's `href` is a plain navigation (`window.location.assign`), since a toast is outside your router.
|
|
2213
|
+
**Fix:** reject with `new ActionRefusal(message, { action: { label, href } })` and keep it as thrown (do not wrap it in another `Error`). For a client-side navigation use `action: { label, onAction: () => navigate(...) }`. A `ConfirmDialog` renders it by default; a `refusal` prop of your own replaces that rendering, so it must show the details and the action itself. See [ConfirmDialog](components/feedback-and-status.md#confirmdialog) and [RecordActionsMenu](components/data-table-filter-and-lists.md#recordactionsmenu).
|
|
2214
|
+
|
|
2131
2215
|
### A `ConfirmationDialog` reopened with new content blocks clicks
|
|
2132
2216
|
|
|
2133
2217
|
**When:** a `ConfirmationDialog` or a `ConfirmDialog` (or a `Dialog`,
|
|
@@ -3649,6 +3733,26 @@ fill is now left empty, and the title stays centred.
|
|
|
3649
3733
|
<PrintHeader title="Invoice" logoText="LOGO" />
|
|
3650
3734
|
```
|
|
3651
3735
|
|
|
3736
|
+
### A `keepTogether` block is cut off at the bottom of the page
|
|
3737
|
+
|
|
3738
|
+
**When:** the console shows `Node of type VIEW can't wrap between pages and
|
|
3739
|
+
it's bigger than available page height` and the rows of a `PrintSection` or
|
|
3740
|
+
a `section` / `stack` with `keepTogether` run past the page edge.
|
|
3741
|
+
**Why:** react-pdf keeps a block whole with `wrap={false}` and cannot split
|
|
3742
|
+
one taller than a page, nor measure it ahead.
|
|
3743
|
+
**Fix:** for a document as data, render it with `renderPdfBuffer`,
|
|
3744
|
+
`renderPdfBlob`, `renderPdfStream`, `downloadPdf` or `pdfResponse`: they
|
|
3745
|
+
measure each `keepTogether` block and let a too-tall one break normally. For a
|
|
3746
|
+
`<PrintSection keepTogether>` in a `PrintDocument`, or your own call to
|
|
3747
|
+
`renderPdfElement`, remove `keepTogether` from blocks that can outgrow a page,
|
|
3748
|
+
or resolve the document first:
|
|
3749
|
+
|
|
3750
|
+
```ts
|
|
3751
|
+
import { resolveKeepTogether } from '@nxgt/material-core/pdf/render';
|
|
3752
|
+
|
|
3753
|
+
const element = renderPdfElement(await resolveKeepTogether(doc));
|
|
3754
|
+
```
|
|
3755
|
+
|
|
3652
3756
|
### French amounts print with a slash, as in `1/234,50`
|
|
3653
3757
|
|
|
3654
3758
|
**When:** before 2.2.0, `formatAmount(1234.5, 'EUR', { locale: 'fr-FR' })`
|
|
@@ -5142,7 +5246,7 @@ such as `getByRole('navigation', { name: 'pagination' })`,
|
|
|
5142
5246
|
| `No filters available`, `+2 more` | `Aucun filtre disponible`, `+2 de plus` | `filter.empty`, `filter.more` |
|
|
5143
5247
|
| `Yes` / `No`, `From 5`, `Until …`, `Up to 9` (filter chips) | `Oui` / `Non`, `À partir de 5`, `Jusqu'au …`, `Jusqu'à 9` | `filter.value.*` |
|
|
5144
5248
|
| `Unread` (`MessageCard`) | `Non lu` | `cards.unread` |
|
|
5145
|
-
| `Replace {name}`, `Upload failed` | `Remplacer {name}`, `Échec de l'envoi` | `upload.replace-named`, `upload.failed` |
|
|
5249
|
+
| `Replace {name}`, `Remove {name}`, `Upload failed` | `Remplacer {name}`, `Supprimer {name}`, `Échec de l'envoi` | `upload.replace-named`, `upload.remove-named`, `upload.failed` |
|
|
5146
5250
|
| `File type not accepted`, `File exceeds maximum size of …`, `Maximum of 3 files reached` | `Type de fichier non accepté`, `Le fichier dépasse la taille maximale de …`, `Maximum de 3 fichiers atteint` | `upload.invalid-type`, `upload.too-large`, `upload.max-files` |
|
|
5147
5251
|
| `Write something …`, `Insert {name} emoji` (editor) | `Écrire quelque chose…`, `Insérer l'emoji {name}` | `rich-text-editor.placeholder`, `rich-text-editor.insert-emoji` |
|
|
5148
5252
|
| `Progress: 40%` | `Progression : 40 %` | `circular-progress.label` |
|
|
@@ -6911,6 +7015,19 @@ structural theme back when it mounts, but not while the other editor stays.
|
|
|
6911
7015
|
**Fix:** keep that editor on a page of its own, or stop it calling `setTheme`
|
|
6912
7016
|
(`CodeEditor`'s `ref` handle gives Monaco's editor for the options you need).
|
|
6913
7017
|
|
|
7018
|
+
### `primary={false}` has no effect on a `ContactsFormField` with `editing="dialog"`
|
|
7019
|
+
|
|
7020
|
+
**When:** `<ContactsFormField editing="dialog" primary={false} />` looks the
|
|
7021
|
+
same as without the prop.
|
|
7022
|
+
|
|
7023
|
+
**Why:** `primary` hides the star and the primary marks of the inline editor.
|
|
7024
|
+
The dialog edits `type`, `value` and `label` only; it has no star to hide, and
|
|
7025
|
+
the `primary` flag already in the value is left untouched. `types` does work
|
|
7026
|
+
in both modes.
|
|
7027
|
+
|
|
7028
|
+
**Fix:** use the default `editing="inline"` when the star must go, or drop the
|
|
7029
|
+
`primary` flag from the value before saving.
|
|
7030
|
+
|
|
6914
7031
|
## PDF reader
|
|
6915
7032
|
|
|
6916
7033
|
### PDF text is missing or in the wrong font in `PdfReader` (cMaps and standard fonts)
|
|
@@ -7748,8 +7865,13 @@ one. The usual sources are the components listed in
|
|
|
7748
7865
|
[the guide](guides/csp.md#style-elements-the-components-inject).
|
|
7749
7866
|
`ScrollArea`, `Select`, `RichTextEditor` and the scroll lock of dialogs and
|
|
7750
7867
|
sheets take the nonce from `NonceProvider`, and report this when the app has
|
|
7751
|
-
none (or gives it another nonce than the header's).
|
|
7752
|
-
|
|
7868
|
+
none (or gives it another nonce than the header's). Mermaid and KaTeX
|
|
7869
|
+
need none of this on a client render
|
|
7870
|
+
([Markdown under a strict CSP](guides/csp.md#markdown-katex-mermaid-and-code-under-a-strict-csp));
|
|
7871
|
+
the two left are a server-rendered page with math (KaTeX's `style` attributes
|
|
7872
|
+
need `style-src-attr 'unsafe-inline'`) and the `<style>` cytoscape adds to
|
|
7873
|
+
`<head>` for mermaid's mindmap and architecture diagrams.
|
|
7874
|
+
`Toaster`, `Drawer`,
|
|
7753
7875
|
`InputOTP`, `ImageCropper`, `Spinner` and `PdfReader` create none: their CSS is in the stylesheet, so if one of them looks unstyled, the
|
|
7754
7876
|
app imports neither `styles.css` nor `dist/material.css`.
|
|
7755
7877
|
**Fix:** for `style-src-attr`, allow attributes: they cannot be nonced.
|
|
@@ -7770,10 +7892,6 @@ import { NonceProvider } from '@nxgt/material';
|
|
|
7770
7892
|
</NonceProvider>;
|
|
7771
7893
|
```
|
|
7772
7894
|
|
|
7773
|
-
For a mermaid diagram, serve that route with `'unsafe-inline'` in
|
|
7774
|
-
`style-src-elem` instead of the nonce (a nonce in the same directive makes
|
|
7775
|
-
browsers ignore `'unsafe-inline'`).
|
|
7776
|
-
|
|
7777
7895
|
Once a page has a header-delivered CSP, browsers blank the `nonce` attribute, so React's development build may warn of an attribute mismatch when it hydrates a server-rendered `<style nonce>`. The warning is harmless.
|
|
7778
7896
|
|
|
7779
7897
|
### `Refused to load the font 'data:font/woff2…' because it violates the following Content Security Policy directive: "font-src 'self'"`
|
|
@@ -187,3 +187,9 @@
|
|
|
187
187
|
gap: 0.75rem;
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
+
/* A wide action (#2168): the actions may shrink within the row, so a long
|
|
191
|
+
* label ends in "..." (the button's own ellipsis) instead of overflowing. */
|
|
192
|
+
[data-slot="server-table-phone-row-end"] > [data-slot="server-table-actions"] {
|
|
193
|
+
flex-shrink: 1;
|
|
194
|
+
min-width: 0;
|
|
195
|
+
}
|
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
/* The scheme's colours as custom properties on the block: classes of the
|
|
2
|
+
stylesheet, never an inline `style` (strict CSP). Same values as
|
|
3
|
+
moonlight-tokens.ts; code-block-scheme.test.ts keeps them equal. */
|
|
4
|
+
[data-slot="code-block"][data-scheme="dark"] {
|
|
5
|
+
--code-bg: #222436;
|
|
6
|
+
--code-fg: #c8d3f5;
|
|
7
|
+
--code-comment: #7a88cf;
|
|
8
|
+
--code-blue: #82aaff;
|
|
9
|
+
--code-cyan: #86e1fc;
|
|
10
|
+
--code-green: #c3e88d;
|
|
11
|
+
--code-orange: #ff966c;
|
|
12
|
+
--code-yellow: #ffc777;
|
|
13
|
+
--code-red: #ff757f;
|
|
14
|
+
--code-magenta: #c099ff;
|
|
15
|
+
--code-pink: #fca7ea;
|
|
16
|
+
--code-selection: #2f334d;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
[data-slot="code-block"][data-scheme="light"] {
|
|
20
|
+
--code-bg: #f5f7ff;
|
|
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;
|
|
31
|
+
--code-selection: #d4daf0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
[data-slot="code-block"] [data-code-scroll] {
|
|
35
|
+
max-height: var(--code-max-height, none);
|
|
36
|
+
}
|
|
37
|
+
|
|
1
38
|
[data-slot="code-block"] {
|
|
2
39
|
background: var(--code-bg);
|
|
3
40
|
color: var(--code-fg);
|