@nxgt/material 3.58.0 → 3.59.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 (87) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +31 -0
  3. package/dist/{async-slice-C_Juki7i.js → async-slice-Cmvxrvn9.js} +2 -2
  4. package/dist/barcode.js +440 -0
  5. package/dist/{button-CCHGtPIN.js → button-Deo7l4wM.js} +1 -1
  6. package/dist/charts.js +3 -3
  7. package/dist/{chip-1XnyX4bd.js → chip-BlmIjpiD.js} +2 -2
  8. package/dist/{code-editor-1Y7ykpam.js → code-editor-BYEpBp_U.js} +11 -11
  9. package/dist/code-editor.js +5 -5
  10. package/dist/{code-language-items-Clbln9C6.js → code-language-items-Cqk4Fq61.js} +2 -2
  11. package/dist/components/components.css +1 -0
  12. package/dist/components/ui/widgets/barcode/barcode.css +112 -0
  13. package/dist/{components-Cj3lJ_uK.js → components-BsKpeqVX.js} +10568 -10517
  14. package/dist/components.js +26 -26
  15. package/dist/{dialog-Cx_xWUVR.js → dialog-1BstKBvt.js} +4 -4
  16. package/dist/dnd.js +1 -1
  17. package/dist/{dropdown-menu-Jp6pHNgP.js → dropdown-menu-hoaocAS7.js} +1 -1
  18. package/dist/editor.js +9 -9
  19. package/dist/{empty-state-D61RimJr.js → empty-state-DEG9h7xk.js} +2 -2
  20. package/dist/{escape-owner-Ch_-kDLw.js → escape-owner-BvLBo_Al.js} +22 -22
  21. package/dist/{field-blur-BJjUHsgJ.js → field-blur-BkeYPaWi.js} +1 -1
  22. package/dist/{field-blur-context-CmOF0FIN.js → field-blur-context-Ba7iZVm_.js} +1 -1
  23. package/dist/hooks.js +2 -2
  24. package/dist/{hover-card-CVmY0Z44.js → hover-card-C5DTU9P_.js} +2 -2
  25. package/dist/{i18n-provider-C0uLEv0G.js → i18n-provider-BJDI-A5w.js} +1 -1
  26. package/dist/i18n.js +5 -5
  27. package/dist/{icon-badge-bAQQK3ls.js → icon-badge-9ZE6k7cn.js} +3 -3
  28. package/dist/{icon-button-Csvj2R8S.js → icon-button-AwJtCkV5.js} +1 -1
  29. package/dist/lib/barcode/index.d.ts +15 -0
  30. package/dist/lib/components/ui/actions/index.d.ts +1 -0
  31. package/dist/lib/components/ui/actions/load-more-footer/index.d.ts +1 -0
  32. package/dist/lib/components/ui/actions/load-more-footer/load-more-footer.d.ts +24 -0
  33. package/dist/lib/components/ui/feedback/index.d.ts +1 -0
  34. package/dist/lib/components/ui/feedback/page-load-failed/index.d.ts +1 -0
  35. package/dist/lib/components/ui/feedback/page-load-failed/page-load-failed.d.ts +15 -0
  36. package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
  37. package/dist/lib/components/ui/widgets/barcode/barcode-error.d.ts +4 -0
  38. package/dist/lib/components/ui/widgets/barcode/barcode-geometry.d.ts +22 -0
  39. package/dist/lib/components/ui/widgets/barcode/barcode.d.ts +31 -0
  40. package/dist/lib/components/ui/widgets/barcode/code128.d.ts +15 -0
  41. package/dist/lib/components/ui/widgets/barcode/datamatrix-placement.d.ts +7 -0
  42. package/dist/lib/components/ui/widgets/barcode/datamatrix-rs.d.ts +2 -0
  43. package/dist/lib/components/ui/widgets/barcode/datamatrix-tables.d.ts +13 -0
  44. package/dist/lib/components/ui/widgets/barcode/datamatrix.d.ts +10 -0
  45. package/dist/lib/components/ui/widgets/barcode/encode.d.ts +12 -0
  46. package/dist/lib/components/ui/widgets/barcode/index.d.ts +6 -0
  47. package/dist/lib/components/ui/widgets/barcode/label-length.d.ts +4 -0
  48. package/dist/lib/components/ui/widgets/barcode/label-sheet.d.ts +37 -0
  49. package/dist/lib/i18n/resources/en/en.d.ts +8 -0
  50. package/dist/lib/i18n/resources/en/material.json.d.ts +8 -0
  51. package/dist/lib/i18n/resources/fr/fr.d.ts +8 -0
  52. package/dist/lib/i18n/resources/fr/material.json.d.ts +8 -0
  53. package/dist/lib/i18n/resources/resources.d.ts +16 -0
  54. package/dist/lib/i18n/translator.d.ts +1 -1
  55. package/dist/lib/print/components/print-section.d.ts +8 -1
  56. package/dist/lib.js +1 -1
  57. package/dist/main.js +43 -43
  58. package/dist/map.js +8 -8
  59. package/dist/material.css +1 -1
  60. package/dist/pdf-reader.js +1 -1
  61. package/dist/print.js +4 -3
  62. package/dist/{public-B42vT7ee.js → public-CLkf8uky.js} +15 -15
  63. package/dist/{rich-code-block-DIJ1X1AF.js → rich-code-block-hJexXOdk.js} +5 -5
  64. package/dist/{sortable-provider-Csby6WpB.js → sortable-provider-DE-MSfeJ.js} +2 -2
  65. package/dist/{spinner-DC35lhqx.js → spinner-CquTUxzt.js} +1 -1
  66. package/dist/{stored-language-BIxd7uiP.js → stored-language-BDzGeEAm.js} +16 -0
  67. package/dist/theme-picker.js +2 -2
  68. package/dist/{theme-toggle-BWdNFLvB.js → theme-toggle-DBgsBw8S.js} +1 -1
  69. package/dist/{toolbar-items-oqmVfnfO.js → toolbar-items-Bm1baIp6.js} +4 -4
  70. package/dist/{translate-material-Bgtjuh85.js → translate-material-Q67GD4co.js} +2 -2
  71. package/dist/{translator-BrZv1U54.js → translator-CBhGdYun.js} +1 -1
  72. package/dist/{use-chat-composer-Dj0TP_B1.js → use-chat-composer-Cx7Q_KOV.js} +929 -929
  73. package/dist/{use-heading-spy-1Dqxj_JQ.js → use-heading-spy-COxCWqWF.js} +1 -1
  74. package/dist/{use-material-translation-DLdQKdfw.js → use-material-translation-BZAy_ffS.js} +2 -2
  75. package/docs/README.md +1 -0
  76. package/docs/components/buttons-and-actions.md +37 -0
  77. package/docs/components/feedback-and-status.md +25 -0
  78. package/docs/components/rich-widgets.md +31 -0
  79. package/docs/guides/barcode.md +203 -0
  80. package/docs/guides/printing.md +28 -3
  81. package/docs/previews/buttons-actions-loadmorefooter.webp +0 -0
  82. package/docs/previews/feedback-status-pageloadfailed.webp +0 -0
  83. package/docs/roadmap.md +14 -7
  84. package/docs/troubleshooting.md +62 -0
  85. package/lib/components/components.css +1 -0
  86. package/lib/components/ui/widgets/barcode/barcode.css +112 -0
  87. package/package.json +10 -3
@@ -1,4 +1,4 @@
1
- import { r as e } from "./i18n-provider-C0uLEv0G.js";
1
+ import { r as e } from "./i18n-provider-BJDI-A5w.js";
2
2
  import { _ as t, g as n, h as r, n as i, v as a, y as o } from "./number.utils-BG_eEt5g.js";
3
3
  import { useCallback as s, useEffect as c, useMemo as l, useRef as u, useState as d } from "react";
4
4
  import { busySettler as f, busySettler as p, createSubmitGuard as m, createSubmitGuard as h } from "@nxgt/material-core/submit-guard";
@@ -1,5 +1,5 @@
1
- import { r as e } from "./i18n-provider-C0uLEv0G.js";
2
- import { t } from "./translate-material-Bgtjuh85.js";
1
+ import { r as e } from "./i18n-provider-BJDI-A5w.js";
2
+ import { t } from "./translate-material-Q67GD4co.js";
3
3
  import { useCallback as n } from "react";
4
4
  //#region lib/i18n/use-material-translation.ts
5
5
  function r() {
package/docs/README.md CHANGED
@@ -9,6 +9,7 @@ detail: every option, the second example, and the traps.
9
9
  | [Components](components/README.md) | You are looking for a component — one preview per category, then one page per category |
10
10
  | [Forms](guides/forms.md) | You are building a form: the field / `*FormField` pair, React Hook Form + Zod, what each field emits, server errors, multi-step forms |
11
11
  | [Themes for your app](guides/themes.md) | You define your own themes: `defineTheme` modules, the Vite plugin's `themes` option, the `nxgt-theme` command and its CI check, the tone warnings |
12
+ | [Barcodes and label sheets](guides/barcode.md) | You draw a Code 128 or Data Matrix code and print a sheet of labels with `@nxgt/material/barcode`: `Barcode`, `LabelSheet` and `Label`, label size, columns, rows, gaps and a start offset, dark mode, print and accessibility |
12
13
  | [Charts](guides/charts.md) | You draw charts with `@nxgt/material/charts`: each chart, the shared props, reference bands, thresholds and point tones, zoom and phone scroll, the keyboard, accessibility, theming the `--chart-1..5` tokens, and moving off the Nivo charts, removed in 3.0 |
13
14
  | [Cards and tiles](guides/cards-and-tiles.md) | You want a `DataTable` as a list of cards or tiles: `layout`, `renderCard`, `renderTile`, the card's one link, selection, the sort control, columns by the list's width |
14
15
  | [DataGrid](guides/data-grid.md) | You are building an editable grid: columns and editors, save modes and conflicts, resizing, hiding and reordering columns, paste, fill and series, undo, totals, long lists, phones, custom editors, CSP |
@@ -18,6 +18,7 @@ and language switches. Everything is imported from `@nxgt/material/components`.
18
18
  | <img src="../previews/buttons-actions-copybutton.webp" width="96" alt=""> | [CopyButton](#copybutton) | Copies a value, shows a check mark |
19
19
  | <img src="../previews/buttons-actions-highlighttext.webp" width="96" alt=""> | [HighlightText](#highlighttext) | Marks matches of a query inside a string |
20
20
  | <img src="../previews/buttons-actions-loadmorebutton.webp" width="96" alt=""> | [LoadMoreButton](#loadmorebutton) | "Load more" for cursor pagination |
21
+ | <img src="../previews/buttons-actions-loadmorefooter.webp" width="96" alt=""> | [LoadMoreFooter](#loadmorefooter) | Shown-of-total line, load button and a failed state with retry, for a paged list |
21
22
  | <img src="../previews/buttons-actions-scrolltotop.webp" width="96" alt=""> | [ScrollToTop](#scrolltotop) | Appears past a scroll threshold |
22
23
  | <img src="../previews/buttons-actions-scrolltolatestbutton.webp" width="96" alt=""> | [ScrollToLatestButton](#scrolltolatestbutton) | "Jump to latest" over a scroll box, counting what arrived |
23
24
  | <img src="../previews/buttons-actions-actioncard.webp" width="96" alt=""> | [ActionCard](#actioncard) | Clickable card, a button (`aria-pressed`) when it chooses or reacts, with an active indicator; `status`, `meta`, a trailing `action` and a `tile` variant |
@@ -969,6 +970,42 @@ buttons.
969
970
 
970
971
  ---
971
972
 
973
+
974
+ ## LoadMoreFooter
975
+
976
+ ![LoadMoreFooter](../previews/buttons-actions-loadmorefooter.webp)
977
+
978
+ The footer of a paged list: a "Showing N of M" line (a polite live region), the load button and, when the next page failed, an error alert whose "Try again" calls `onLoad`. The rows already shown stay on screen. It renders nothing when there is no next page.
979
+
980
+ ```tsx
981
+ import { LoadMoreFooter } from '@nxgt/material/components';
982
+
983
+ <LoadMoreFooter
984
+ shown={items.length}
985
+ total={totalCount}
986
+ hasMore={hasNextPage}
987
+ loading={isFetchingNextPage}
988
+ failed={isFetchNextPageError}
989
+ onLoad={() => fetchNextPage()}
990
+ />;
991
+ ```
992
+
993
+ | Prop | Type | Default | Effect |
994
+ | --- | --- | --- | --- |
995
+ | `shown` | `number` | — | items shown now |
996
+ | `total` | `number` | — | items in all; leave it out when unknown, the line then reads "20 shown" |
997
+ | `hasMore` | `boolean` | — | `false` renders nothing, not even the count |
998
+ | `loading` | `boolean` | `false` | the button (or the retry) spins and ignores clicks |
999
+ | `failed` | `boolean` | `false` | an error alert with a tonal "Try again" replaces the button |
1000
+ | `label` | `string` | `"Load more"` (translated) | the load button's text; the button is expanded here |
1001
+ | `onLoad` | `() => void` | — | loads the next page; the retry calls it too |
1002
+
1003
+ Texts are `material.load-more.shown`, `.shown-count`, `.failed` and `.retry` (English and French). It composes [`LoadMoreButton`](#loadmorebutton); keep that one for a pagination bar. For a page opened at a cursor that failed, see [`PageLoadFailed`](./feedback-and-status.md#pageloadfailed).
1004
+
1005
+ **Storybook:** `Buttons & actions/LoadMoreFooter` · **Vue:** not yet (tracked as a `vue` issue)
1006
+
1007
+ ---
1008
+
972
1009
  ## ScrollToTop
973
1010
 
974
1011
  ![ScrollToTop](../previews/buttons-actions-scrolltotop.webp)
@@ -13,6 +13,7 @@ centre.
13
13
  | <img src="../previews/feedback-status-customdialog.webp" width="96" alt=""> | [CustomDialog](#customdialog) | Ready-made dialog with trigger, icon, title, body and footer |
14
14
  | <img src="../previews/feedback-status-stepupdialog.webp" width="96" alt=""> | [StepUpDialog](#stepupdialog) | "Confirm it's you": a dialog that asks for a one-time code, driven by props |
15
15
  | <img src="../previews/feedback-status-emptystate.webp" width="96" alt=""> | [EmptyState](#emptystate) | Illustration, title, description and action, by kind (empty, search, not-found, unauthorized) |
16
+ | <img src="../previews/feedback-status-pageloadfailed.webp" width="96" alt=""> | [PageLoadFailed](#pageloadfailed) | An empty state for a page opened at a cursor that failed, with a link back to the start |
16
17
  | <img src="../previews/feedback-status-errorstate.webp" width="96" alt=""> | [ErrorState](#errorstate) | Failure block with Retry and technical details |
17
18
  | <img src="../previews/feedback-status-progress.webp" width="96" alt=""> | [Progress](#progress) | Material 3 Expressive linear bar, circular ring and labelled progress field, flat or wavy |
18
19
  | <img src="../previews/feedback-status-scrollprogress.webp" width="96" alt=""> | [ScrollProgress](#scrollprogress) | Hairline tracking the window or a scrollable element |
@@ -502,6 +503,30 @@ The default description is the companion of its title: with your own `title` and
502
503
 
503
504
  ---
504
505
 
506
+ ## PageLoadFailed
507
+
508
+ ![PageLoadFailed](../previews/feedback-status-pageloadfailed.webp)
509
+
510
+ An `EmptyState` for a list page opened at a cursor that failed to load (a stale link, a lost page): a title and a tonal "Back to the start" link. The link is rendered by the `LinkComponentProvider`'s component. For the next page of a list that is already shown, use [`LoadMoreFooter`](./buttons-and-actions.md#loadmorefooter), which keeps the rows.
511
+
512
+ ```tsx
513
+ import { PageLoadFailed } from '@nxgt/material/components';
514
+
515
+ <PageLoadFailed href="/orders" />;
516
+ ```
517
+
518
+ | Prop | Type | Default | Effect |
519
+ | --- | --- | --- | --- |
520
+ | `href` | `string` | — | where "Back to the start" goes: the list without its cursor |
521
+ | `linkLabel` | `string` | `"Back to the start"` (translated) | the link's text |
522
+ | `title` | `ReactNode` | `"This page could not be loaded"` (translated) | the title |
523
+
524
+ It takes the other `EmptyState` props (`size`, `illustration`, `description`, `headingLevel`) except `action`. Texts are `material.load-more.page-failed-title` and `.back-to-start`.
525
+
526
+ **Storybook:** `Feedback & status/PageLoadFailed` · **Vue:** not yet (tracked as a `vue` issue)
527
+
528
+ ---
529
+
505
530
  ## ErrorState
506
531
 
507
532
  ![ErrorState](../previews/feedback-status-errorstate.webp)
@@ -11,6 +11,7 @@ the app owns the data.
11
11
  | <img src="../previews/rich-widgets-eventcalendar.webp" width="96" alt=""> | [EventCalendar](#eventcalendar) | Month / week / day / agenda calendar, a day view split by resource, a named time zone, phone to desktop |
12
12
  | <img src="../previews/rich-widgets-native-charts-linechart.webp" width="96" alt=""> | [Charts](#charts) | Native SVG charts (line, area, bar, pie, donut, gauge, radial, funnel, heat map, radar, scatter, combo) with keyboard, summary and View data; the Nivo ones were removed in 3.0 |
13
13
  | <img src="../previews/rich-widgets-drawing.webp" width="96" alt=""> | [Drawing](#drawing) | Vector drawing from a scene of plain data or JSX primitives: theme colours, accessible, focusable nodes, strict-CSP safe |
14
+ | | [Barcode](#barcode) | Code 128 and Data Matrix as accessible SVG, themed and dark-aware, black on white in print, with `LabelSheet` for a printed sheet of labels |
14
15
  | <img src="../previews/rich-widgets-emojipicker.webp" width="96" alt=""> | [EmojiPicker](#emojipicker) | Shared emoji picker and list |
15
16
  | <img src="../previews/rich-widgets-mentionlist.webp" width="96" alt=""> | [MentionList](#mentionlist) | `@` mention list with local search |
16
17
  | <img src="../previews/rich-widgets-documenteditor.webp" width="96" alt=""> | [DocumentEditor](#documenteditor) | Rich text editor for long-form writing, built on a headless core |
@@ -615,6 +616,36 @@ How to use it in an app (server rendering, CSP, colours, events, PNG, PDF, e-mai
615
616
 
616
617
  ---
617
618
 
619
+ ## Barcode
620
+
621
+ Code 128 and Data Matrix from `@nxgt/material/barcode`, drawn as SVG by the
622
+ package (no runtime dependency). The symbol follows the theme and dark mode on
623
+ screen (`appearance="paper"` keeps it dark on light), is black on white in
624
+ print, is named by `label` (or its value) and writes no inline `style`. Its own
625
+ entry, server-safe.
626
+
627
+ ```tsx
628
+ import { Barcode, Label, LabelSheet } from '@nxgt/material/barcode';
629
+
630
+ <Barcode value="TUBE-0042" label="Tube 0042" showText />
631
+ <Barcode value="TUBE-0042" symbology="datamatrix" />
632
+
633
+ <LabelSheet label={{ width: '50mm', height: '25mm' }} columns={3} rows={8} gap="2mm" offset={{ x: 5, y: 8 }}>
634
+ {items.map((i) => (
635
+ <Label key={i.id}>
636
+ <Barcode value={i.code} decorative />
637
+ {i.code}
638
+ </Label>
639
+ ))}
640
+ </LabelSheet>
641
+ ```
642
+
643
+ `LabelSheet` takes `label` (`{ width, height }`), `columns`, `rows` (labels per
644
+ page), `gap`, `offset` (margin before the first label) and `skip` (blank labels
645
+ first); lengths are millimetres or a string with a unit. A value a symbology cannot hold throws a `BarcodeError`; `barcodeGeometry(value, options)` returns the `viewBox` size and path for an SVG of your own. See the
646
+ [barcode guide](../guides/barcode.md) for printing a sheet and for the limits
647
+ of each symbology.
648
+
618
649
  ## EmojiPicker
619
650
 
620
651
  ![EmojiPicker](../previews/rich-widgets-emojipicker.webp)
@@ -0,0 +1,203 @@
1
+ # Barcodes and label sheets
2
+
3
+ `@nxgt/material/barcode` draws a scannable code and lays labels out on a sheet
4
+ to print. `Barcode` is a Code 128 or Data Matrix symbol as SVG: it follows the
5
+ theme and dark mode on screen, is black on white in print, has an accessible
6
+ name and writes no inline `style` (a strict CSP is satisfied). `LabelSheet`
7
+ and `Label` set the label size, the columns, rows, gaps and a start offset.
8
+
9
+ The encoders are in the package: no runtime dependency comes with it. Its
10
+ tests read the symbols back with a real decoder (zxing-cpp) and compare the
11
+ Data Matrix modules with bwip-js.
12
+
13
+ - [Set up](#set-up)
14
+ - [A barcode](#a-barcode)
15
+ - [Code 128 or Data Matrix](#code-128-or-data-matrix)
16
+ - [Colours, dark mode and print](#colours-dark-mode-and-print)
17
+ - [Accessibility](#accessibility)
18
+ - [A sheet of labels](#a-sheet-of-labels)
19
+ - [Printing a sheet](#printing-a-sheet)
20
+ - [Without React](#without-react)
21
+ - [Troubleshooting](#troubleshooting)
22
+
23
+ ## Set up
24
+
25
+ ```tsx
26
+ import { Barcode, Label, LabelSheet } from '@nxgt/material/barcode';
27
+ ```
28
+
29
+ The entry is its own: it is not in `@nxgt/material` or
30
+ `@nxgt/material/components`. The colours come from `styles.css`, which your app
31
+ already imports. `Barcode` has no hooks and renders on the server; `LabelSheet`
32
+ sets its lengths once mounted (see [Printing a sheet](#printing-a-sheet)).
33
+
34
+ ## A barcode
35
+
36
+ ```tsx
37
+ <Barcode value="TUBE-0042" showText />
38
+ ```
39
+
40
+ | Prop | Default | |
41
+ | --- | --- | --- |
42
+ | `value` | | The text to encode (required). |
43
+ | `symbology` | `'code128'` | `'code128'` or `'datamatrix'`. |
44
+ | `label` | the value | The accessible name. |
45
+ | `decorative` | `false` | Hide the symbol from assistive technology. |
46
+ | `showText` | `false` | Print the value under a Code 128, inside the SVG (a Data Matrix ignores it). |
47
+ | `appearance` | `'themed'` | `'themed'` or `'paper'` ([Colours](#colours-dark-mode-and-print)). |
48
+ | `moduleSize` | | Pixels per module: sets a fixed `width` and `height`. Without it the SVG fills its box and keeps its ratio. |
49
+ | `quietZone` | 10 (Code 128), 2 (Data Matrix) | The blank margin, in modules. Scanners need it: keep at least 10 and 1. |
50
+ | `barHeight` | 40 | The bar height of a Code 128, in modules. |
51
+
52
+ Other SVG props (`className`, `data-*`...) pass to the `<svg>`. An invalid
53
+ value throws a `BarcodeError` while rendering, with a message that names the
54
+ limit.
55
+
56
+ ## Code 128 or Data Matrix
57
+
58
+ | | Code 128 | Data Matrix |
59
+ | --- | --- | --- |
60
+ | Shape | bars, as wide as the text is long | a square |
61
+ | Holds | ASCII, characters 0 to 127 | any text (UTF-8), up to about 3 100 digits |
62
+ | Best for | an identifier a handheld laser or camera reads at 5 to 10 cm | a small label (a tube, a part) read by a camera, error correction included |
63
+ | Digits | two per symbol character (code set C) | two per codeword |
64
+
65
+ Code 128 switches between its three code sets to keep the symbol short. Data
66
+ Matrix uses the ASCII encodation, with the smallest square symbol (10x10 to
67
+ 144x144) that holds the text; upper-case text may come out one size larger than
68
+ a symbol made with the denser C40 encodation. Text outside ASCII goes through
69
+ `symbology="datamatrix"` as UTF-8: tell your scanner's decoder to read UTF-8.
70
+
71
+ ## Colours, dark mode and print
72
+
73
+ On screen the symbol is drawn with the theme: `--foreground` on `--background`,
74
+ so it follows light and dark. Many scanners read **dark on light only**, so in
75
+ dark mode a themed symbol may not scan from the screen: use
76
+ `appearance="paper"` for a symbol that must be scanned from the screen, which
77
+ stays black on white in dark mode too. In print every symbol is black on white,
78
+ whatever the theme or `appearance`.
79
+
80
+ ## Accessibility
81
+
82
+ A symbol is `role="img"` named by `label`, or by the value when there is no
83
+ `label`, with the same text in a `<title>`. A code is not meant to be read aloud:
84
+ name it with what it identifies (`label="Tube 0042"`). When the value is written
85
+ beside the symbol, pass `decorative`: the symbol is then hidden, and the text
86
+ carries the meaning. `showText` writes the value under a Code 128 in a
87
+ `<text>` that is hidden from assistive technology (the image already carries
88
+ the name).
89
+
90
+ ## A sheet of labels
91
+
92
+ ```tsx
93
+ <LabelSheet
94
+ label={{ width: '50mm', height: '25mm' }}
95
+ columns={3}
96
+ rows={8}
97
+ gap="2mm"
98
+ offset={{ x: '5mm', y: '8mm' }}
99
+ >
100
+ {items.map((i) => (
101
+ <Label key={i.id}>
102
+ <Barcode value={i.code} decorative barHeight={30} />
103
+ {i.code}
104
+ </Label>
105
+ ))}
106
+ </LabelSheet>
107
+ ```
108
+
109
+ | Prop | Default | |
110
+ | --- | --- | --- |
111
+ | `label` | | `{ width, height }` of one label (required). |
112
+ | `columns` | | Labels per row (required). |
113
+ | `rows` | all on one page | Rows per page: every `columns` x `rows` labels start a new page. |
114
+ | `gap` | 0 | One length, or `{ x, y }`. |
115
+ | `offset` | 0 | `{ x, y }`: the margin before the first label, for a printer's unprintable edge or a sheet's own margin. |
116
+ | `skip` | 0 | Blank labels first: start on the fifth label of a sheet that is partly used. |
117
+
118
+ `columns` and `rows` are whole numbers of at least 1, `skip` a whole number of at least 0. A length is a number of millimetres or a string with a unit (`'2.5cm'`,
119
+ `'0.5in'`, `'8pt'`, `'12px'`, `'1rem'`); anything else throws a `BarcodeError`.
120
+ A `Label` clips to the label size and never splits across two pages. A symbol
121
+ inside a `Label` scales to fit it, leaving room for the text under it.
122
+
123
+ ## Printing a sheet
124
+
125
+ A browser prints a page size that the page's CSS names. Add it to your print
126
+ stylesheet, with the size of your sheet and no margin of its own (the sheet's
127
+ `offset` is the margin):
128
+
129
+ ```css
130
+ @media print {
131
+ @page {
132
+ size: A4;
133
+ margin: 0;
134
+ }
135
+ }
136
+ ```
137
+
138
+ Then call `window.print()`. The preview on screen draws a dashed outline around
139
+ each page and a dotted one around each label; both disappear in print, and the
140
+ symbols turn black on white. To print the sheet alone from a page with other
141
+ content, use `printOnly` from `@nxgt/material/lib`.
142
+
143
+ `LabelSheet` writes its lengths as custom properties (`--nxgt-label-w`...) from
144
+ an effect, because a strict CSP blocks a `style` attribute in server markup.
145
+ Print after the page has hydrated; the server's markup alone has no label size, so a sheet printed from it comes out wrong.
146
+ Check the printed size once on your own printer: browsers scale a page to fit
147
+ unless the print dialog says "100%" or "Actual size".
148
+
149
+ ## Without React
150
+
151
+ `barcodeGeometry(value, options)` returns the `viewBox` size and one SVG path of
152
+ the dark modules, in module units, for an SVG you assemble yourself (an e-mail,
153
+ a PDF drawing). Options: `symbology`, `quietZone`, `barHeight` and
154
+ `textHeight` (room under a Code 128 for text). The result is
155
+ `{ symbology, width, height, path, textY }`; `textY` is where a Code 128's text
156
+ baseline sits.
157
+
158
+ ```tsx
159
+ const g = barcodeGeometry('TUBE-0042', { symbology: 'code128', barHeight: 30 });
160
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${g.width} ${g.height}">
161
+ <rect width="${g.width}" height="${g.height}" fill="#fff"/><path d="${g.path}"/></svg>`;
162
+ ```
163
+
164
+ The types `BarcodeProps`, `BarcodeSymbology`, `BarcodeGeometry`,
165
+ `BarcodeGeometryOptions`, `LabelSheetProps` and `LabelLength` are exported too.
166
+
167
+ ## Troubleshooting
168
+
169
+ ### `Code 128 holds ASCII only (0-127)`
170
+
171
+ The value has a character such as `é` or `€`. Use `symbology="datamatrix"`, or
172
+ remove the character.
173
+
174
+ ### `Code 128 needs a value` / `Data Matrix needs a value`
175
+
176
+ The value is an empty string. Render the symbol only once there is a code.
177
+
178
+ ### `<name> must be a whole number`
179
+
180
+ `columns`, `rows` or `skip` is zero, negative or fractional.
181
+
182
+ ### `Data Matrix holds at most 1558 codewords`
183
+
184
+ The value is longer than the largest symbol (about 3 100 digits, fewer
185
+ characters otherwise). Encode an identifier and look the rest up.
186
+
187
+ ### `<name> must be a number (millimetres) or a length such as "50mm"`
188
+
189
+ A size or gap is a bare string (`'50'`) or an unsupported unit. Pass a number
190
+ (millimetres) or a string like `'50mm'`.
191
+
192
+ ### `Unknown symbology "x"`
193
+
194
+ `symbology` is neither `"code128"` nor `"datamatrix"`; TypeScript catches it, plain JS does not.
195
+
196
+ ### The code does not scan from the screen in dark mode
197
+
198
+ Use `appearance="paper"`, and keep the quiet zone.
199
+
200
+ ### The printed labels are a little off
201
+
202
+ The print dialog scaled the page. Choose "100%" / "Actual size", set the
203
+ `@page` size to the sheet's, and set `offset` to move the first label.
@@ -195,7 +195,7 @@ print English strings ("Total", "No rows") and `en` amounts and dates.
195
195
  | Component | Props | Renders |
196
196
  | --- | --- | --- |
197
197
  | `PrintHeader` | `title: string`, `subtitle?: string`, `reference?: string`, `referencePrefix?: string` (`'#'`), `logo?: string \| { src, width?, height? }`, `logoText?: string`, `qrDataUrl?: string \| null` | The title block: the logo (a PNG or JPEG URL or data URL, 72×48 pt unless you size it) or `logoText` in a box on the left, the title, subtitle and `#reference` centred, the QR image on the right. A side you leave empty draws nothing, and the title stays centred |
198
- | `PrintSection` | `title: string`, `className?: string`, `children` | A titled block (`className` is a `tw` string, default `mb-3.5`) |
198
+ | `PrintSection` | `title: string`, `className?: string`, `keepTogether?: boolean`, `children` | A titled block (`className` is a `tw` string, default `mb-3.5`); `keepTogether` keeps its title and rows on one page, see [Keeping a block on one page](#keeping-a-block-on-one-page) |
199
199
  | `PrintInfoRow` | `label: string`, `value?: ReactNode` | A label / value line. A `string` or `number` value is wrapped in `Text`; anything else is rendered as-is |
200
200
  | `PrintCard` | `className?: string`, `children` | A bordered row of fields |
201
201
  | `PrintCardField` | `label: string`, `value?: string \| null` | A label over a value. **Renders nothing when `value` is empty** |
@@ -1012,8 +1012,8 @@ it:
1012
1012
  | --- | --- | --- |
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
- | `stack` / `row` | `stack(children, { gap?, style? })`, `row(children, { gap?, align?, justify?, style? })` | Children one under the other, or side by side |
1016
- | `section` | `section(title, children)` | A titled block, as `PrintSection` |
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` |
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 |
@@ -1122,6 +1122,31 @@ without the footer row, put `pageNumber({ scope: 'group', format: 'page' })`
1122
1122
  in the page's `footer` ("Page 2"). The scope is typed `PdfPageNumberScope`
1123
1123
  (`'document' | 'group'`), exported from `@nxgt/material-core/pdf/model`.
1124
1124
 
1125
+ ### Keeping a block on one page
1126
+
1127
+ `keepTogether` on a `PrintSection`, or on a `section` or `stack` node of a
1128
+ document as data, moves the block to the next page whole when it would
1129
+ straddle a page break, so a group's title is never left apart from its rows.
1130
+ It maps to react-pdf's `wrap={false}`, and is off by default.
1131
+
1132
+ ```tsx
1133
+ <PrintSection title={group.title} keepTogether>
1134
+ {group.rows.map((row) => <PrintInfoRow key={row.id} label={row.label} value={row.value} />)}
1135
+ </PrintSection>
1136
+ ```
1137
+
1138
+ ```ts
1139
+ section(group.title, rows, { keepTogether: true });
1140
+ stack([heading('Result'), table(result)], { keepTogether: true });
1141
+ ```
1142
+
1143
+ Use it on a block you know is shorter than a page. react-pdf cannot split a
1144
+ block that opted out of wrapping: a block taller than the page area is not
1145
+ broken, it overflows the page and is cut off at the edge (react-pdf logs
1146
+ "can't wrap between pages and it's bigger than available page height").
1147
+ Leave `keepTogether` off for a block that may grow past a page, and use
1148
+ `keepRowsWhole` and `repeatHeader` on a long table instead.
1149
+
1125
1150
  ### Row and cell styles in a table
1126
1151
 
1127
1152
  A `rows` or `footer` entry is a list of values, or `{ cells, style? }`; a
package/docs/roadmap.md CHANGED
@@ -6,6 +6,12 @@ version they shipped in, and planned entries may name the major they target.
6
6
 
7
7
  ## Now
8
8
 
9
+ - **Barcodes and label sheets** — `@nxgt/material/barcode` draws Code 128
10
+ and Data Matrix as accessible SVG (themed, dark-aware, black on white in
11
+ print) and lays labels out on a sheet to print: label size, columns, rows,
12
+ gaps and a start offset
13
+ ([#1963](https://github.com/softistx/nxgt-material/issues/1963),
14
+ [guide](guides/barcode.md)).
9
15
  - **Toast texts follow app translations** — the toast presets read the
10
16
  app's `I18nProvider` `resources`, so an override under `material.*`
11
17
  changes their wording ([#1855](https://github.com/softistx/nxgt-material/issues/1855)).
@@ -13,13 +19,6 @@ version they shipped in, and planned entries may name the major they target.
13
19
  main decision at the bottom of a phone, above the keyboard and clear of the
14
20
  safe areas, and inline from `md`
15
21
  ([#1964](https://github.com/softistx/nxgt-material/issues/1964)).
16
- - **Smaller, easier-to-read components** — the library's own source is kept
17
- to files under 200 lines and functions under 80, so a component is quicker to
18
- read and to fix. No API or behaviour change: smaller modules that are easier
19
- to review, and fewer regressions
20
- ([#1865](https://github.com/softistx/nxgt-material/issues/1865), slices
21
- [#1915](https://github.com/softistx/nxgt-material/issues/1915) to
22
- [#1925](https://github.com/softistx/nxgt-material/issues/1925)).
23
22
  - **DataGrid: one column, several kinds of row** — `meta.rowEditor(row)` picks
24
23
  the built-in editor from the row, and `meta.options` and
25
24
  `meta.number.decimals` take the row, so a result column can be a bounded
@@ -317,6 +316,9 @@ version they shipped in, and planned entries may name the major they target.
317
316
  `@nxgt/material/print` are removed in favour of `definePdfDocument`, once the
318
317
  framework-free engine (see Next) ships. They work, and are not deprecated,
319
318
  today.
319
+ - **Imports from the declaring file** — after the size split, internal
320
+ imports name the file that declares each moved name instead of going through
321
+ a re-export ([#1980](https://github.com/softistx/nxgt-material/issues/1980)).
320
322
  - **Ready-made documents** — invoice, quote, receipt, report, certificate and
321
323
  label-sheet templates that take your data and return a document, their labels
322
324
  translated.
@@ -356,6 +358,11 @@ version they shipped in, and planned entries may name the major they target.
356
358
 
357
359
  ## Shipped
358
360
 
361
+ - **Smaller, easier-to-read components (3.58.0)** — the library's own source
362
+ is kept to files under 200 lines and functions under 80, enforced by a
363
+ check, so a component is quicker to read and to fix. No API or behaviour
364
+ change ([#1865](https://github.com/softistx/nxgt-material/issues/1865)).
365
+
359
366
  - **Dark cards that separate from the page ground (3.54.0)** — in dark mode, cards,
360
367
  popovers and dialogs lift off the page ground (lighter surfaces and a `--card-edge`
361
368
  hairline), light unchanged ([#1676](https://github.com/softistx/nxgt-material/issues/1676)).
@@ -457,6 +457,15 @@ words in your error.
457
457
 
458
458
  - [Reporting a bug](#reporting-a-bug)
459
459
 
460
+ **Barcodes**
461
+
462
+ - [`Code 128 holds ASCII only (0-127)`](#code-128-holds-ascii-only-0-127)
463
+ - [`Code 128 needs a value` / `Data Matrix needs a value`](#code-128-needs-a-value--data-matrix-needs-a-value)
464
+ - [`Data Matrix holds at most 1558 codewords`](#data-matrix-holds-at-most-1558-codewords)
465
+ - [`<name> must be a whole number`](#name-must-be-a-whole-number)
466
+ - [`<name> must be a number (millimetres) or a length such as "50mm"`](#name-must-be-a-number-millimetres-or-a-length-such-as-50mm)
467
+ - [`Unknown symbology "x": use "code128" or "datamatrix"`](#unknown-symbology-x-use-code128-or-datamatrix)
468
+
460
469
  ---
461
470
 
462
471
  ## Install
@@ -7820,6 +7829,59 @@ Before upgrading, precaching the illustration chunks in the service worker narro
7820
7829
 
7821
7830
  ---
7822
7831
 
7832
+ ## Barcodes
7833
+
7834
+ ### `Code 128 holds ASCII only (0-127)`
7835
+
7836
+ Raised by `@nxgt/material/barcode` while rendering. The value has a character above 127 (`é`, `€`). Use `symbology="datamatrix"`, which encodes UTF-8, or remove the character.
7837
+
7838
+ ```tsx
7839
+ <Barcode value="CAFE-42" />
7840
+ <Barcode value="Café" symbology="datamatrix" />
7841
+ ```
7842
+
7843
+ ### `Code 128 needs a value` / `Data Matrix needs a value`
7844
+
7845
+ Raised by `@nxgt/material/barcode` while rendering. `Barcode` or `barcodeGeometry` got an empty string. Render the symbol only when there is a code.
7846
+
7847
+ ```tsx
7848
+ {code ? <Barcode value={code} /> : null}
7849
+ ```
7850
+
7851
+ ### `Data Matrix holds at most 1558 codewords`
7852
+
7853
+ Raised by `@nxgt/material/barcode` while rendering. The value is longer than the largest 144x144 symbol (about 3 100 digits, fewer other characters). Encode an identifier and look the rest up.
7854
+
7855
+ ```tsx
7856
+ <Barcode value={record.id} symbology="datamatrix" />
7857
+ ```
7858
+
7859
+ ### `<name> must be a whole number`
7860
+
7861
+ Raised by `@nxgt/material/barcode` while rendering. `columns`, `rows` or `skip` of a `LabelSheet` is zero, negative or fractional (`columns` and `rows` need at least 1, `skip` at least 0).
7862
+
7863
+ ```tsx
7864
+ <LabelSheet label={{ width: 50, height: 25 }} columns={3} rows={8}>
7865
+ ```
7866
+
7867
+ ### `<name> must be a number (millimetres) or a length such as "50mm"`
7868
+
7869
+ Raised by `@nxgt/material/barcode` while rendering. A `LabelSheet` size, gap or offset is a bare string (`'50'`) or has an unsupported unit. Use a number (millimetres) or `'50mm'`, `'2cm'`, `'0.5in'`, `'8pt'`, `'12px'`, `'1rem'`.
7870
+
7871
+ ```tsx
7872
+ <LabelSheet label={{ width: '50mm', height: 25 }} columns={3} gap="2mm">
7873
+ ```
7874
+
7875
+ ### `Unknown symbology "x": use "code128" or "datamatrix"`
7876
+
7877
+ Raised by `@nxgt/material/barcode` while rendering. `symbology` is neither value (reachable from plain JS or an untyped string).
7878
+
7879
+ ```tsx
7880
+ <Barcode value="42" symbology="datamatrix" />
7881
+ ```
7882
+
7883
+ ---
7884
+
7823
7885
  ## Reporting a bug
7824
7886
 
7825
7887
  If an error comes from inside this package and none of the entries above
@@ -206,3 +206,4 @@
206
206
  @import "./background-shared.css";
207
207
  @import "./ui/widgets/drawing/drawing.css"; /* @nxgt/material/draw: role colours, focus ring */
208
208
  @import "./ui/widgets/drawing/drawing-animate.css";
209
+ @import "./ui/widgets/barcode/barcode.css"; /* @nxgt/material/barcode: symbol colours, label sheet */