@nxgt/material-core 0.12.0 → 0.13.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 +30 -0
- package/README.md +268 -24
- package/dist/backgrounds/activity.d.ts +11 -0
- package/dist/backgrounds/activity.d.ts.map +1 -0
- package/dist/backgrounds/build.d.ts +9 -0
- package/dist/backgrounds/build.d.ts.map +1 -0
- package/dist/backgrounds/id-prefix.d.ts +8 -0
- package/dist/backgrounds/id-prefix.d.ts.map +1 -0
- package/dist/backgrounds/index.d.ts +20 -0
- package/dist/backgrounds/index.d.ts.map +1 -0
- package/dist/backgrounds/index.js +467 -0
- package/dist/backgrounds/index.js.map +20 -0
- package/dist/backgrounds/noise.d.ts +7 -0
- package/dist/backgrounds/noise.d.ts.map +1 -0
- package/dist/backgrounds/presets/grain.d.ts +9 -0
- package/dist/backgrounds/presets/grain.d.ts.map +1 -0
- package/dist/backgrounds/presets.d.ts +16 -0
- package/dist/backgrounds/presets.d.ts.map +1 -0
- package/dist/backgrounds/primitives.d.ts +22 -0
- package/dist/backgrounds/primitives.d.ts.map +1 -0
- package/dist/backgrounds/rng.d.ts +12 -0
- package/dist/backgrounds/rng.d.ts.map +1 -0
- package/dist/backgrounds/scatter.d.ts +25 -0
- package/dist/backgrounds/scatter.d.ts.map +1 -0
- package/dist/backgrounds/scene.d.ts +21 -0
- package/dist/backgrounds/scene.d.ts.map +1 -0
- package/dist/backgrounds/tones.d.ts +4 -0
- package/dist/backgrounds/tones.d.ts.map +1 -0
- package/dist/backgrounds/types.d.ts +53 -0
- package/dist/backgrounds/types.d.ts.map +1 -0
- package/dist/chunks/index-mw5fa8qa.js +119 -0
- package/dist/chunks/index-mw5fa8qa.js.map +12 -0
- package/dist/csp/index.d.ts +10 -0
- package/dist/csp/index.d.ts.map +1 -0
- package/dist/csp/index.js +21 -0
- package/dist/csp/index.js.map +9 -0
- package/dist/csp/nonce.d.ts +35 -0
- package/dist/csp/nonce.d.ts.map +1 -0
- package/dist/csp/policy.d.ts +33 -0
- package/dist/csp/policy.d.ts.map +1 -0
- package/dist/csp/zod-jitless.d.ts +4 -0
- package/dist/csp/zod-jitless.d.ts.map +1 -0
- package/dist/lib/drawer-room.d.ts +8 -0
- package/dist/lib/drawer-room.d.ts.map +1 -0
- package/dist/lib/drawer-room.js +17 -0
- package/dist/lib/drawer-room.js.map +10 -0
- package/dist/lib/escape-forward.d.ts +14 -0
- package/dist/lib/escape-forward.d.ts.map +1 -1
- package/dist/lib/escape-forward.js +22 -2
- package/dist/lib/escape-forward.js.map +3 -3
- package/dist/pdf-viewer/index.d.ts +1 -0
- package/dist/pdf-viewer/index.d.ts.map +1 -1
- package/dist/pdf-viewer/index.js +68 -38
- package/dist/pdf-viewer/index.js.map +6 -5
- package/dist/pdf-viewer/pdf-reveal-hit.d.ts +7 -0
- package/dist/pdf-viewer/pdf-reveal-hit.d.ts.map +1 -0
- package/dist/pdf-viewer/pdf-scroll-spy.d.ts +24 -8
- package/dist/pdf-viewer/pdf-scroll-spy.d.ts.map +1 -1
- package/dist/states/copy.d.ts +46 -0
- package/dist/states/copy.d.ts.map +1 -0
- package/dist/states/describe-error.d.ts +18 -0
- package/dist/states/describe-error.d.ts.map +1 -0
- package/dist/states/illustrations.d.ts +24 -0
- package/dist/states/illustrations.d.ts.map +1 -0
- package/dist/states/index.d.ts +11 -0
- package/dist/states/index.d.ts.map +1 -0
- package/dist/states/index.js +253 -0
- package/dist/states/index.js.map +13 -0
- package/dist/states/kinds.d.ts +28 -0
- package/dist/states/kinds.d.ts.map +1 -0
- package/dist/vite/csp-dev-header.d.ts +11 -0
- package/dist/vite/csp-dev-header.d.ts.map +1 -0
- package/dist/vite/csp-hashes.d.ts +23 -0
- package/dist/vite/csp-hashes.d.ts.map +1 -0
- package/dist/vite/csp-header.d.ts +10 -0
- package/dist/vite/csp-header.d.ts.map +1 -0
- package/dist/vite/csp-plugin.d.ts +12 -0
- package/dist/vite/csp-plugin.d.ts.map +1 -0
- package/dist/vite/css-chain.d.ts +13 -0
- package/dist/vite/css-chain.d.ts.map +1 -0
- package/dist/vite/dependency-closure.d.ts +8 -0
- package/dist/vite/dependency-closure.d.ts.map +1 -0
- package/dist/vite/fonts-inter.d.ts +15 -0
- package/dist/vite/fonts-inter.d.ts.map +1 -0
- package/dist/vite/fonts-plugin.d.ts +12 -0
- package/dist/vite/fonts-plugin.d.ts.map +1 -0
- package/dist/vite/index.d.ts +17 -81
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +478 -2
- package/dist/vite/index.js.map +18 -4
- package/dist/vite/libraries.d.ts +13 -0
- package/dist/vite/libraries.d.ts.map +1 -0
- package/dist/vite/nxgt-material.d.ts +12 -0
- package/dist/vite/nxgt-material.d.ts.map +1 -0
- package/dist/vite/options.d.ts +56 -0
- package/dist/vite/options.d.ts.map +1 -0
- package/dist/vite/pdf-plugin.d.ts +19 -0
- package/dist/vite/pdf-plugin.d.ts.map +1 -0
- package/dist/vite/pdfkit-standard-fonts.d.ts +74 -0
- package/dist/vite/pdfkit-standard-fonts.d.ts.map +1 -0
- package/dist/vite/styles-plugin.d.ts +27 -0
- package/dist/vite/styles-plugin.d.ts.map +1 -0
- package/dist/vite/styles-scan.d.ts +14 -0
- package/dist/vite/styles-scan.d.ts.map +1 -0
- package/docs/README.md +4 -0
- package/docs/backgrounds.md +95 -0
- package/docs/roadmap.md +34 -23
- package/docs/states.md +85 -0
- package/docs/troubleshooting.md +174 -3
- package/package.json +34 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @nxgt/material-core
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#640](https://github.com/softistx/nxgt-material/pull/640) [`f39e0b7`](https://github.com/softistx/nxgt-material/commit/f39e0b7321c6e753896f48119f354bd316cdd909) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New `@nxgt/material-core/backgrounds` subpath: the engine behind the coming background presets, framework-free and with no DOM at import. It holds the types (`BackgroundPreset`, `BackgroundTone`, `BackgroundIntensity`, `MotifOptions`, `SceneNode`...), the `BACKGROUND_PRESETS` table, a seeded generator (`mulberry32`), `valueNoise2`, `scatter`, the motif shapes (`motifShape`, `place`, `MOTIF_SHAPES`, `MOTIF_DEFAULTS`), a plain SVG scene format with `assertSafeScene` (no `style`, `on*`, `href` or external URL, so it is safe under a strict Content Security Policy), `svgIdPrefix` for ids inside `url(#...)`, and `observeBackgroundActivity`, which pauses an animation when the page is hidden, the element is off screen or the user prefers reduced motion.
|
|
8
|
+
|
|
9
|
+
- [#690](https://github.com/softistx/nxgt-material/pull/690) [`9b656e8`](https://github.com/softistx/nxgt-material/commit/9b656e80aef7afb299151e9772c7271c0e3fa377) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material-core/backgrounds` gets its first scene builder: `buildGrain` (also `buildBackgroundScene('grain', ...)`), a film-noise overlay drawn by an `feTurbulence` filter on one rect, safe under `assertSafeScene`, with ids from the `idPrefix` and the `seed` moving the noise. It also exports `SHIPPED_BACKGROUND_PRESETS`, the `ShippedBackgroundPreset` type (`'gradient' | 'grain'`, widened as presets land) and `resolveShippedPreset`, which falls back to `'gradient'` with a one-time warning outside production.
|
|
10
|
+
|
|
11
|
+
- [#642](https://github.com/softistx/nxgt-material/pull/642) [`2087342`](https://github.com/softistx/nxgt-material/commit/2087342f0b5cd3d987ff0903c0e90e5889c629d4) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New subpath `@nxgt/material-core/csp`, the server and build side of the Content Security Policy (it reads no DOM, so it runs on Node, Bun, Deno, workers and browsers; `/csp-nonce` stays the browser's side). `createCspNonce()` makes a per-request nonce; `cspPolicy({ nonce, dev, hashes, directives })` returns the recommended policy as directives and a `header` (without a nonce, `style-src-elem 'self' 'unsafe-inline'` for the `<style>` elements the libraries create at runtime, unless `hashes.style` is given); `cspHash(source)` gives the `'sha256-…'` source of an inline script or style; `applyCspNonce(html, nonce)` fills the `CSP_NONCE_PLACEHOLDER` a build leaves; `ZOD_JITLESS_SCRIPT` and `zodJitlessTag(nonce)` switch zod's `new Function` probe off from an inline script. `cspPolicy` and `applyCspNonce` throw when given the public placeholder as the nonce, and `applyCspNonce` throws when the HTML holds no placeholder (a hash-mode build, or a custom placeholder not passed as its third argument).
|
|
12
|
+
|
|
13
|
+
- [#649](https://github.com/softistx/nxgt-material/pull/649) [`0fd651b`](https://github.com/softistx/nxgt-material/commit/0fd651b98403966aee8ecd4059048eabbe8c91f6) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add `@nxgt/material-core/drawer-room`: `drawerRoom(element, top)`, the height an open floating list may take under `top` inside a Drawer without covering its footer (`null` outside a Drawer). `Autocomplete` uses it in both libraries ([#582](https://github.com/softistx/nxgt-material/issues/582)).
|
|
14
|
+
|
|
15
|
+
- [#687](https://github.com/softistx/nxgt-material/pull/687) [`970cf63`](https://github.com/softistx/nxgt-material/commit/970cf63a763be215c4d27cca58a5698d915d6f46) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `nxgtMaterial()` in `@nxgt/material-core/vite` takes a `pdf` option. It is on by default and adds `pdfkitStandardFonts()` (a no-op unless pdfkit goes through the bundle). `pdf: { bundle: true }` also adds the library, `@nxgt/material-core`, `pdfkit`, `@react-pdf/*` and every package the renderer, pdfkit and core need at runtime (their transitive dependencies, read from your installed packages) to `ssr.noExternal` (merged with yours; `noExternal: true` is left alone), for an SSR server whose runtime image has no `node_modules`. `pdf: false` turns it off.
|
|
16
|
+
|
|
17
|
+
- [#651](https://github.com/softistx/nxgt-material/pull/651) [`0da112a`](https://github.com/softistx/nxgt-material/commit/0da112a9353e4bf369a58a8ef62e5ff3b9238f6a) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material-core/vite` gains `nxgtMaterial(options, library?)`, one Vite plugin for the build-time work of a strict Content Security Policy. Default `csp.mode: 'hash'`: zod's jitless script is injected into `index.html`, the sha256 of every inline script and style is written to `dist/.vite/nxgt-csp.json` (`scriptHashes`, `styleHashes`, `header`) and the header logged at the end of the build: scripts by hash, `style-src-elem 'self' 'unsafe-inline'` with no style hash (the components create `<style>` elements at runtime, and a hash would void `'unsafe-inline'`; nonce mode is the strict option for styles), and no placeholder ever lands in the HTML. `csp: { mode: 'nonce' }` opts into Vite's `html.cspNonce` placeholder for a server that calls `applyCspNonce` per request. It also keeps fonts out of `data:` URIs (composing your `build.assetsInlineLimit`), loads self-hosted Inter and sets `--font-sans` only when the optional peer `@fontsource-variable/inter` is installed (otherwise one warning, in dev and in build, and the system font stays; the build never fails), sends a Report-Only header on the dev server (in hash mode it carries the jitless script's hash), and, given a library name, fails `vite build` with the exact line to add when the stylesheet is missing (neither `styles.css` nor the compiled stylesheet through `@import "<library>";`) or the React `@source` is (`styles.css` only). On Vite 5, which has no build environments, every check runs as for the client. `/vite` now reads stylesheets and installed `package.json` files with `node:fs/promises`, `node:fs` and `node:path` (build-time code, like `/pdfkit-fonts`); it also re-exports `CSP_NONCE_PLACEHOLDER` and `ZOD_JITLESS_SCRIPT`.
|
|
18
|
+
|
|
19
|
+
- [#639](https://github.com/softistx/nxgt-material/pull/639) [`009f7af`](https://github.com/softistx/nxgt-material/commit/009f7afaf99d44cdc960f9666cdb546fd4fc13f7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material-core/states`: the kinds of `EmptyState` and `ErrorState` (`EMPTY_STATE_KINDS`, `ERROR_STATE_KINDS`, `resolveEmptyStateKind`, `isErrorKind`), their i18n keys and English fallbacks (`stateCopy`, `STATE_ACTION_KEYS`), `describeError` (an error's name, message and cause chain, never its stack) and the geometry of the illustrations (`STATE_ILLUSTRATIONS`, `STATE_ACCENT`, `STATE_ICON`).
|
|
20
|
+
|
|
21
|
+
`@nxgt/material` and `@nxgt/material-vue` ship new i18n keys in `en` and `fr`, for the components that follow: `material.error-state.*` (`generic`, `offline` and `maintenance` with a title and a description, `retry`, `details`, `details-name`, `details-message`, `details-cause`), `material.table.labels.no-results` and `no-results-description`, `material.command.empty` and `material.pdf-reader.retry`.
|
|
22
|
+
|
|
23
|
+
- [#647](https://github.com/softistx/nxgt-material/pull/647) [`0f88cbd`](https://github.com/softistx/nxgt-material/commit/0f88cbdd0ee2045901d568df2eb769196acaf73b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Escape closes only an open `ComboboxField`, `Combobox` or `Autocomplete` list inside a `Dialog`, `Sheet`, `Drawer`, `AlertDialog` or `Popover`; a second Escape closes the layer ([#631](https://github.com/softistx/nxgt-material/issues/631)). Before, the first Escape closed the whole layer. A `Select` list has always taken the first Escape, as a layer of its own, and is unchanged. The layer's own `onEscapeKeyDown` (`@escape-key-down`) now sees `defaultPrevented` true for an Escape that only closes a list. `@nxgt/material-core/escape-forward` gains `keepListEscape(event)` for your own overlay.
|
|
24
|
+
|
|
25
|
+
- [#679](https://github.com/softistx/nxgt-material/pull/679) [`c17e6c2`](https://github.com/softistx/nxgt-material/commit/c17e6c235e471990a3315c03a463bfd0c3d0b3e8) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: a scroll with no input the reader hears (find in page, a screen reader's cursor, setting `scrollTop`) that lands just after the opening page's pin corrects the view is reported once the scroll spy's hold ends, and the pin lets go; before, the page number stayed on the opening page and the next correction scrolled back to it. Fixes [#677](https://github.com/softistx/nxgt-material/issues/677). `@nxgt/material-core/pdf-viewer`'s `createScrollSpy` takes an options object as its fourth argument, `{ idleMs?, readerPosition? }` (a bare number is still `idleMs`): two frames after a hold ends, a scroll it held is measured once when `readerPosition` says the view is no longer where the reader put it; input in the page area during a hold measures a scroll it held. `pageInView(scroller, pages)`, the page most on screen, is exported.
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- [#708](https://github.com/softistx/nxgt-material/pull/708) [`4c5658b`](https://github.com/softistx/nxgt-material/commit/4c5658bba08aac0d5b0224683f9374a1411296ba) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Docs: the getting-started `/backgrounds` rows describe what ships, the roadmaps list the base as shipped and the remaining presets as next, and the troubleshooting pages explain the development warning for a preset that is not available yet, a grain that looks bare without `backgrounds.css`, and (in core) `assertSafeScene`'s errors.
|
|
30
|
+
|
|
31
|
+
- [#691](https://github.com/softistx/nxgt-material/pull/691) [`8feb417`](https://github.com/softistx/nxgt-material/commit/8feb41728fc4d4f9ce3ad4be7c78fd4ddd7b5b67) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The README links the libraries' new "Migrating to the Vite plugin" guide, which moves a hand-written CSP, zod, font and PDF setup to `nxgtMaterial()`.
|
|
32
|
+
|
|
3
33
|
## 0.12.0
|
|
4
34
|
|
|
5
35
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -6,20 +6,20 @@ The framework-free core that [`@nxgt/material`](https://www.npmjs.com/package/@n
|
|
|
6
6
|
the print formatters, the PDF file helpers, the PDF viewer's framework-free
|
|
7
7
|
half (page geometry, scroll spy, text search), the currency field's decimals,
|
|
8
8
|
the file-size formatter, the slider's tick marks, the emoji list's groups,
|
|
9
|
-
the date range presets, a debounce, accent-insensitive text matching and a race-free async search (`/debounce`, `/text-match`, `/async-search`), the file preview's kind detection, the fields' blur
|
|
9
|
+
the date range presets, a debounce, accent-insensitive text matching and a race-free async search (`/debounce`, `/text-match`, `/async-search`), the room an open list has above a Drawer's footer (`/drawer-room`), the file preview's kind detection, the fields' blur
|
|
10
10
|
rule, a field's first reachable control (`/focus-control`) and its `aria-describedby`
|
|
11
11
|
(`/described-by`), where the focus goes in a repeatable row group
|
|
12
12
|
(`/repeatable-group`), the page behind a modal layer made inert
|
|
13
|
-
(`/inert-others`), Escape pressed while an overlay closes
|
|
14
|
-
(`/escape-forward`), the Toaster's guard against a tap's emulated hover
|
|
13
|
+
(`/inert-others`), Escape pressed while an overlay closes or in a field whose
|
|
14
|
+
list is open (`/escape-forward`), the Toaster's guard against a tap's emulated hover
|
|
15
15
|
(`/touch-hover`), the focus a toast's action hands back (`/toast-focus`), a
|
|
16
16
|
table row's activation, the zod error map's issue messages
|
|
17
17
|
(`/zod-issue-messages`), a zod error map that words an issue by its code
|
|
18
|
-
(`/code-errors`), the page's Content Security Policy nonce (`/csp-nonce`), an
|
|
18
|
+
(`/code-errors`), the page's Content Security Policy nonce (`/csp-nonce`), the server and build side of the CSP (`/csp`), an
|
|
19
19
|
object built on its first use (`/lazy-object`), one submit in flight
|
|
20
20
|
(`/submit-guard`), the two-step verification helpers (`/two-factor`: a TOTP
|
|
21
21
|
secret grouped to be typed, the recovery codes' text file, a text download),
|
|
22
|
-
printing one element of the page (`/print-only`), the Vite plugin that
|
|
22
|
+
the empty and error states' kinds, copy keys, error details and illustrations (`/states`), printing one element of the page (`/print-only`), the engine behind the background presets (`/backgrounds`), the Vite plugin that
|
|
23
23
|
bundles pdfkit's standard fonts and a `build.assetsInlineLimit` that keeps
|
|
24
24
|
fonts out of `data:` URIs, and the Node-only resolution of those fonts that
|
|
25
25
|
the Next.js helper and the Nuxt module run at build time.
|
|
@@ -30,7 +30,7 @@ re-export what they always exported, at the same subpaths:
|
|
|
30
30
|
from here, and so do their `PdfReader`, `CurrencyField`, form fields (their
|
|
31
31
|
focus, blur, `aria-describedby` and zod messages), file list and upload
|
|
32
32
|
field (`formatFileSize`), modal layers (the inert page behind, Escape while
|
|
33
|
-
closing), `Toaster`, `DataTable` rows, `@nxgt/material/next` and
|
|
33
|
+
closing, Escape in a field whose list is open), `Toaster`, `DataTable` rows, `@nxgt/material/next` and
|
|
34
34
|
`@nxgt/material-vue/nuxt`. Import it directly when you want the model or a
|
|
35
35
|
formatter without a UI library, or one of these helpers in a component of
|
|
36
36
|
your own.
|
|
@@ -60,13 +60,15 @@ files are plain ES modules and also load in Node without a bundler.
|
|
|
60
60
|
| Optional peer | Needed by |
|
|
61
61
|
| --- | --- |
|
|
62
62
|
| `react`, `@react-pdf/renderer@^4.9` | `@nxgt/material-core/pdf/render` and `/pdf` — rendering a document |
|
|
63
|
-
| `vite@^6 \|\| ^7 \|\| ^8` | `@nxgt/material-core/vite` — its declarations name Vite's `Plugin` type (types only); the plugin itself runs in your Vite config |
|
|
63
|
+
| `vite@^5.2 \|\| ^6 \|\| ^7 \|\| ^8` | `@nxgt/material-core/vite` — its declarations name Vite's `Plugin` type (types only); the plugin itself runs in your Vite config |
|
|
64
|
+
| `@fontsource-variable/inter@>=5` | `@nxgt/material-core/vite` — `nxgtMaterial()` loads Inter only when it is installed, else warns and keeps the system font; `fonts: { sans: false }` silences the warning |
|
|
64
65
|
|
|
65
66
|
Every other subpath imports none of them, and is safe to import on a server
|
|
66
|
-
with no DOM.
|
|
67
|
-
and `node:path`,
|
|
68
|
-
from
|
|
69
|
-
|
|
67
|
+
with no DOM. Two subpaths are **Node only**: `/pdfkit-fonts` imports `node:module`
|
|
68
|
+
and `node:path`, and `/vite` imports `node:fs/promises`, `node:fs` and `node:path`
|
|
69
|
+
(the styles check reads stylesheets from disk; `pdf: { bundle: true }` reads installed `package.json` files), both for a framework's or a
|
|
70
|
+
Vite config at build time — never import them from code a browser bundle
|
|
71
|
+
can reach. No other subpath imports a `node:` built-in.
|
|
70
72
|
|
|
71
73
|
## Subpaths
|
|
72
74
|
|
|
@@ -85,14 +87,15 @@ built-in.
|
|
|
85
87
|
| `/pdf-source` | `PdfSource` and its helpers (`toPdfjsSource`, `pdfSourceKey`, …) |
|
|
86
88
|
| `/pdf-file-name` | `resolvePdfFileName`, `DEFAULT_PDF_FILE_NAME` |
|
|
87
89
|
| `/pdf-download` | `downloadPdfFile`, `printPdfFile` — in a browser |
|
|
88
|
-
| `/pdf-viewer` | The PDF viewer's pure half: page geometry and fit (`readerFitScale`, `placeholderSize`, `rotatedSize`, `PdfPageSize`, `FitMode`), page sizes (`readPageSizes`, `createPageSizesLoader`), the scroll spy (`createScrollSpy`, `revealHit`, `scrollIntoArea`), the opening page's pin (`pinToPage`, whose optional `onRelease` runs once when it lets go, and `pinHolds`, whether the view is still where it puts the page), text search (`pageTextFromItems`, `findTextMatches`, `searchDocument`), `escapeHtml`, `mapPdfOutline` |
|
|
90
|
+
| `/pdf-viewer` | The PDF viewer's pure half: page geometry and fit (`readerFitScale`, `placeholderSize`, `rotatedSize`, `PdfPageSize`, `FitMode`), page sizes (`readPageSizes`, `createPageSizesLoader`), the scroll spy (`createScrollSpy` and its `ScrollSpyOptions`, `pageInView`, `revealHit`, `scrollIntoArea`), the opening page's pin (`pinToPage`, whose optional `onRelease` runs once when it lets go, and `pinHolds`, whether the view is still where it puts the page), text search (`pageTextFromItems`, `findTextMatches`, `searchDocument`), `escapeHtml`, `mapPdfOutline` |
|
|
89
91
|
| `/currency-digits` | `currencyFractionDigits` — the decimals `CurrencyField` shows |
|
|
90
92
|
| `/field-blur-layers` | `layerOf`, `openerOf`, `FieldLayers` — whose overlay a layer of the page is, for a field's blur |
|
|
91
93
|
| `/focus-control` | `firstControl`, `focusHandle`, `FocusHandle` — the first control of a field a user can reach, and a stand-in element that focuses it for a form library |
|
|
92
94
|
| `/repeatable-group` | `focusAfterRemove`, `focusAfterMove`, `focusRowTarget`, `rowMoveTarget`, `canAddRow`, `canRemoveRow`, `RowFocusTarget`, `RowMoveDirection`, `RowAnnouncement`, `ROW_SLOT`, `ROW_FIELDS_SLOT`, `ADD_ATTRIBUTE` — where the focus goes after a row of a `RepeatableGroup` is added, removed or moved, and what its live region says |
|
|
93
|
-
| `/escape-forward` | `forwardEscape` — Escape pressed while an overlay closes, pressed again for the overlay below once it has gone |
|
|
95
|
+
| `/escape-forward` | `forwardEscape`, `keepListEscape` — Escape pressed while an overlay closes, pressed again for the overlay below once it has gone; Escape pressed in a field whose list is open, kept from closing the overlay around it |
|
|
94
96
|
| `/described-by` | `describedBy` — an `aria-describedby` value from the caller's ids and a field's help and error ids |
|
|
95
97
|
| `/inert-others` | `inertOthers`, `INERT_MARKER` — the rest of the page `inert` behind a modal layer, live regions kept, nested layers counted |
|
|
98
|
+
| `/backgrounds` | `buildBackgroundScene`, `buildGrain`, `assertSafeScene`, `svgIdPrefix`, `observeBackgroundActivity`, `mulberry32`, `valueNoise2`, `scatter`, `motifShape`, `place`, `BACKGROUND_PRESETS`, `SHIPPED_BACKGROUND_PRESETS`, `resolveShippedPreset`, `BACKGROUND_TONES`, `MOTIF_SHAPES`, `MOTIF_DEFAULTS` and the types `BackgroundPreset`, `ShippedBackgroundPreset`, `BackgroundTone`, `BackgroundIntensity`, `MotifOptions`, `SceneNode`, `PresetInfo` — the engine behind the libraries' background presets: a seeded generator, scattering, motif shapes, a plain SVG scene format with its safety check, ids for `url(#…)`, and the observer that pauses an animation |
|
|
96
99
|
| `/touch-hover` | `ignoreTouchHover`, `TOASTER_PHONE_QUERY`, `TOASTER_PHONE_POSITION` — keeps a tap from hovering an element, so a toast a finger touched does not stay paused; where the `Toaster` puts toasts on a phone |
|
|
97
100
|
| `/toast-focus` | `focusOrigin`, `handFocusBack`, `trackToasterOrigin`, `TOASTER_SELECTOR` — where the focus was before it entered a toast, and handing it back there before the toast's action runs, so a dialog the action opens keeps the focus |
|
|
98
101
|
| `/zod-issue-messages` | `sizeMessage`, `invalidFormatMessage`, `IssueText` — the translated text of a zod `too_big` / `too_small` and `invalid_format` issue |
|
|
@@ -102,17 +105,20 @@ built-in.
|
|
|
102
105
|
| `/emoji-groups` | `groupKey`, `groupEmojis`, `searchEmojis`, `groupName`, `GROUP_NAMES` and the type `EmojiItem` — how the emoji list groups, searches and names its emojis |
|
|
103
106
|
| `/date-range` | `DATE_RANGE_PRESET_IDS`, `presetRange`, `resolvePresetRange`, `isSameDayRange`, `isRangeDisabled`, `withTimeOf`, `endOfDayTime`, `clampRangeEnd`, `keepRangeTimes` and the types `DayRange`, `PartialDayRange`, `DateRangePreset`, `DateRangePresetId`, `DateRangePresetOption`, `DateRangeRules` — `DateRangeField`'s presets and times |
|
|
104
107
|
| `/csp-nonce` | `getCspNonce`, `setCspNonce`, `claimCspNonce(nonce, depth)`, `createNoncedStyle`, and `getNonce` / `setNonce` (the `get-nonce` API) — the page's Content Security Policy nonce, for a `<style>` created at runtime; browser only |
|
|
108
|
+
| `/csp` | `createCspNonce`, `cspPolicy`, `cspHash`, `applyCspNonce`, `CSP_NONCE_PLACEHOLDER`, `ZOD_JITLESS_SCRIPT`, `zodJitlessTag` and the types `CspPolicyOptions`, `CspDirectives` — the Content Security Policy for a server or a build: a nonce per request, the recommended header, hashes of inline code, zod's jitless switch; no DOM |
|
|
105
109
|
| `/lazy-object` | `lazyObject`, `buildOnce` — a value built on its first use rather than when its module loads: the libraries' zod schemas |
|
|
106
110
|
| `/debounce` | `debounce`, the type `Debounced` — a function called once calls pause, with `cancel` and `flush` (`SearchField`'s `debounceMs`) |
|
|
107
111
|
| `/initials` | `initials` — an avatar's initials from a name, `'Ada Lovelace'` → `'AL'` (`AccountMenu`) |
|
|
108
112
|
| `/text-match` | `foldText`, `matchesText` — case- and accent-insensitive matching (`'e'` finds "École"), lowercased for a locale |
|
|
109
113
|
| `/async-search` | `createAsyncSearch`, `searchAnnouncement`, `DEFAULT_SEARCH_DEBOUNCE_MS`, `DEFAULT_MIN_QUERY_LENGTH` and the types `AsyncSearch`, `AsyncSearchConfig`, `AsyncSearchState`, `AsyncSearchStatus`, `SearchFn`, `SearchContext`, `SearchAnnouncement` — `Autocomplete`'s server search: debounced, each run aborting the last, a stale response dropped |
|
|
114
|
+
| `/drawer-room` | `drawerRoom(element, top)` — the height in px an open floating list may take under `top` inside a Drawer without covering its footer (at least 80, an 8px gap kept); `null` outside a Drawer (`Autocomplete`) |
|
|
110
115
|
| `/submit-guard` | `createSubmitGuard`, `busySettler` and the types `SubmitGuard`, `SubmitOnceOptions` — one submit in flight until its request ends, behind both libraries' `useSubmitOnce` |
|
|
111
116
|
| `/code-errors` | `createCodeErrorMap`, `defaultCodeOf`, `isEmptyRequired` and the types `CodeErrorMap`, `CodeErrorMapOptions`, `CodeIssue` — a zod 4 error map that words an issue by the code it carries (a refinement's `params.code`), behind both libraries' `useCodeResolver` |
|
|
112
117
|
| `/two-factor` | `groupSecret`, `recoveryCodesText`, `downloadText` and the types `RecoveryCodesTextOptions`, `DownloadTextOptions` — a TOTP secret in groups of four, the recovery codes as a text file, and saving a text file, behind both libraries' `EnrolmentSecret` and `RecoveryCodes` |
|
|
113
118
|
| `/print-only` | `printOnly`, `PRINT_ATTRIBUTE`, `PRINT_TARGET_ATTRIBUTE` — printing one element of the page with no inline style, behind `RecoveryCodes`' Print button; re-exported by both libraries' `/lib` |
|
|
114
119
|
| `/file-preview` | `fileKind`, `describeFile`, `safePreviewUrl`, `readTextPreview`, `FilePreviewHttpError`, `FilePreviewUnsafeUrlError`, `TEXT_PREVIEW_MAX_BYTES`, `fileExtension`, `fileNameFromUrl`, `dataUrlType`, `kindOfMimeType`, `normaliseMimeType`, `isTextFileKind`, `TEXT_FILE_KINDS` and the types `FileKind`, `FileKindInput`, `DescribedFile`, `FilePreviewSource`, `TextPreview` — `FilePreview`'s kind detection, URL allow-list and text reader |
|
|
115
|
-
| `/
|
|
120
|
+
| `/states` | `EMPTY_STATE_KINDS`, `ERROR_STATE_KINDS`, `resolveEmptyStateKind`, `isErrorKind`, `stateCopy`, `STATE_ACTION_KEYS`, `describeError`, `STATE_ILLUSTRATIONS`, `STATE_ACCENT`, `STATE_ICON` and the types `EmptyStateKind`, `ErrorStateKind`, `StateKind`, `LegacyEmptyStateVariant`, `StateFamily`, `StateCopy`, `ErrorDetail`, `StateIllustration`, `IllustrationShape`, `IllustrationTone`, `StateAccent`, `StateIconName` — the kinds, copy keys, error details and illustrations behind `EmptyState` and `ErrorState` |
|
|
121
|
+
| `/vite` | `nxgtMaterial()`, `pdfkitStandardFonts()`, `cspAssetsInlineLimit`, `CSP_NONCE_PLACEHOLDER`, `ZOD_JITLESS_SCRIPT` and the types `NxgtMaterialOptions`, `NxgtMaterialCspOptions`, `NxgtMaterialLibrary` — see below |
|
|
116
122
|
| `/pdfkit-fonts` | **Node only.** `pdfkitStandardFontDirs`, `pdfkitStandardFontFiles`, `PDFKIT_STANDARD_FONTS`, the types `PdfkitStandardFont` and `PdfkitFontsWarn` — where the app's pdfkit keeps its standard fonts, for a server tracer |
|
|
117
123
|
|
|
118
124
|
## Usage
|
|
@@ -328,13 +334,30 @@ server). `pinHolds(node)` tells whether the view still shows `node` where the
|
|
|
328
334
|
pin puts it: at the top, or as near as the area can scroll at its end. A
|
|
329
335
|
scroll that leaves it elsewhere came from find in page, a screen reader's
|
|
330
336
|
cursor or your own `scrollTop`, none of which the pin hears: treat it as the
|
|
331
|
-
user's.
|
|
337
|
+
user's. Such a scroll can land within the hold a correction starts, which
|
|
338
|
+
ignores it: give `createScrollSpy` the option `readerPosition`, which says
|
|
339
|
+
whether the view is still where the reader put it, and the spy measures once,
|
|
340
|
+
two frames after release (past a resize's correction), when a held scroll
|
|
341
|
+
left it elsewhere. Its fourth argument is `{ idleMs?, readerPosition? }`; a
|
|
342
|
+
bare number is still `idleMs`. Not covered: a second correction within the
|
|
343
|
+
same hold puts the view back where the reader put it, and that scroll is lost.
|
|
332
344
|
|
|
333
345
|
```ts
|
|
334
|
-
import {
|
|
346
|
+
import {
|
|
347
|
+
createScrollSpy,
|
|
348
|
+
pinHolds,
|
|
349
|
+
pinToPage,
|
|
350
|
+
} from '@nxgt/material-core/pdf-viewer';
|
|
335
351
|
|
|
336
|
-
// pages: Map<number, HTMLElement
|
|
352
|
+
// pages: Map<number, HTMLElement>
|
|
337
353
|
let pinned = true;
|
|
354
|
+
const spy = createScrollSpy(
|
|
355
|
+
() => area, // the reader's page area
|
|
356
|
+
() => pages,
|
|
357
|
+
onScroll,
|
|
358
|
+
// still where the reader put it
|
|
359
|
+
{ readerPosition: () => !pinned || pinHolds(pages.get(3)) },
|
|
360
|
+
);
|
|
338
361
|
const unpin = pinToPage(
|
|
339
362
|
() => pages.get(3), // the page's element, looked up again on each correction
|
|
340
363
|
() => spy.hold(), // the corrections are not the user's scrolling
|
|
@@ -348,6 +371,14 @@ function onScroll(page: number) {
|
|
|
348
371
|
}
|
|
349
372
|
```
|
|
350
373
|
|
|
374
|
+
`pageInView(area, pages)` is the spy's own measure, on demand: the page most
|
|
375
|
+
on screen, the last one in view once the area is scrolled to its end, or `null` when
|
|
376
|
+
no page is in view.
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
const page = pageInView(area, pages); // 3
|
|
380
|
+
```
|
|
381
|
+
|
|
351
382
|
### A currency's decimals: `/currency-digits`
|
|
352
383
|
|
|
353
384
|
```ts
|
|
@@ -482,6 +513,25 @@ what a combobox's polite live region says: the failure, else "loading",
|
|
|
482
513
|
else the count once the query has settled — `null` while the user is still
|
|
483
514
|
typing.
|
|
484
515
|
|
|
516
|
+
### An open list above a Drawer's footer: `/drawer-room`
|
|
517
|
+
|
|
518
|
+
`drawerRoom(element, top)` looks for the `[data-slot="drawer-content"]` around
|
|
519
|
+
`element` and answers how many pixels a floating list that starts at `top`
|
|
520
|
+
(a viewport coordinate) may take before it reaches the Drawer's
|
|
521
|
+
`[data-slot="drawer-footer"]`, or the Drawer's bottom edge without a footer,
|
|
522
|
+
or the window's. It keeps an 8px gap and never answers less than 80, so the
|
|
523
|
+
list stays usable. Outside a Drawer it answers `null`: the list keeps its own
|
|
524
|
+
height.
|
|
525
|
+
|
|
526
|
+
```ts
|
|
527
|
+
import { drawerRoom } from '@nxgt/material-core/drawer-room';
|
|
528
|
+
|
|
529
|
+
const room = drawerRoom(field, field.getBoundingClientRect().bottom + 6);
|
|
530
|
+
list.style.maxHeight = room == null ? '' : `${room}px`;
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
Measure again when the field or the Drawer resizes or scrolls.
|
|
534
|
+
|
|
485
535
|
### One submit in flight: `/submit-guard`
|
|
486
536
|
|
|
487
537
|
`createSubmitGuard()` returns `{ once, settle, pendingRun }`. `once(run)` wraps a
|
|
@@ -709,6 +759,54 @@ document.head.append(style);
|
|
|
709
759
|
[`get-nonce`](https://www.npmjs.com/package/get-nonce)'s names, so a
|
|
710
760
|
bundler alias can point that package here.
|
|
711
761
|
|
|
762
|
+
### The CSP for a server or a build: `/csp`
|
|
763
|
+
|
|
764
|
+
`/csp-nonce` reads the nonce in a browser; `/csp` is for the code that
|
|
765
|
+
chooses it: an SSR request handler, a build script, a Vite plugin. It uses
|
|
766
|
+
only `globalThis.crypto`, `btoa` and strings, so it runs on Node, Bun, Deno,
|
|
767
|
+
workers and browsers, and keeps no state.
|
|
768
|
+
|
|
769
|
+
```ts
|
|
770
|
+
import {
|
|
771
|
+
applyCspNonce,
|
|
772
|
+
createCspNonce,
|
|
773
|
+
cspHash,
|
|
774
|
+
cspPolicy,
|
|
775
|
+
zodJitlessTag,
|
|
776
|
+
} from '@nxgt/material-core/csp';
|
|
777
|
+
|
|
778
|
+
// Per request (SSR): a fresh nonce, the header, the nonce in the HTML.
|
|
779
|
+
const nonce = createCspNonce(); // 16 random bytes, base64
|
|
780
|
+
const { header } = cspPolicy({ nonce }); // the policy of the CSP guide
|
|
781
|
+
response.setHeader('Content-Security-Policy', header);
|
|
782
|
+
const html = `<head>${zodJitlessTag(nonce)}…</head>`; // zod without new Function
|
|
783
|
+
|
|
784
|
+
// A build that left CSP_NONCE_PLACEHOLDER in the HTML fills it per request:
|
|
785
|
+
const page = applyCspNonce(builtHtml, nonce);
|
|
786
|
+
|
|
787
|
+
// Static hosting has no per-request nonce: allow inline code by its hash.
|
|
788
|
+
const hash = await cspHash("console.log('hi')"); // "'sha256-…'"
|
|
789
|
+
cspPolicy({ hashes: { script: [hash] } }); // script-src 'self' 'sha256-…'
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
- `cspPolicy({ nonce?, dev?, hashes?, directives? })` returns
|
|
793
|
+
`{ directives, header }`. Without a nonce there is no `'strict-dynamic'`:
|
|
794
|
+
scripts fall back to `'self'` (and their hashes), style elements to
|
|
795
|
+
`'self' 'unsafe-inline'`, since the libraries create `<style>` elements at
|
|
796
|
+
runtime that no build can hash (a `hashes.style` entry replaces
|
|
797
|
+
`'unsafe-inline'`, which a browser ignores beside a hash); `dev` adds `ws:` and
|
|
798
|
+
`wss:` to `connect-src`; `directives` is merged per directive (sources are
|
|
799
|
+
appended), and a directive set to `null` or `[]` is dropped.
|
|
800
|
+
- `applyCspNonce(html, nonce, placeholder?)` replaces every placeholder and
|
|
801
|
+
throws on a nonce outside base64 (`"`, `<`, a space would break out of the
|
|
802
|
+
attribute), and when `html` holds no placeholder (a hash-mode build, or a
|
|
803
|
+
custom placeholder not passed as the third argument).
|
|
804
|
+
- **Never serve `CSP_NONCE_PLACEHOLDER` as a nonce**: it is public, so it
|
|
805
|
+
voids the policy. `applyCspNonce` and `cspPolicy` throw when given it.
|
|
806
|
+
- `ZOD_JITLESS_SCRIPT` is `(globalThis.__zod_globalConfig??={}).jitless=true;`
|
|
807
|
+
(zod 4.4 and later), for a page whose policy has no `'unsafe-eval'`.
|
|
808
|
+
Each library tests it against the zod it runs on.
|
|
809
|
+
|
|
712
810
|
### Built on first use: `/lazy-object`
|
|
713
811
|
|
|
714
812
|
`buildOnce(build)` returns a function that calls `build` on its first call
|
|
@@ -788,6 +886,45 @@ const { text, truncated } = await readTextPreview('/logs/today.log', {
|
|
|
788
886
|
});
|
|
789
887
|
```
|
|
790
888
|
|
|
889
|
+
### The kinds of an empty or failed screen: `/states`
|
|
890
|
+
|
|
891
|
+
The framework-free half of `EmptyState` and `ErrorState`. Four **empty kinds**
|
|
892
|
+
(`empty`, `search`, `not-found`, `unauthorized`) are an absence; three **error
|
|
893
|
+
kinds** (`generic`, `offline`, `maintenance`) are something that failed.
|
|
894
|
+
`resolveEmptyStateKind({ kind, variant })` picks `kind`, else the legacy
|
|
895
|
+
`variant`, else `'generic'` (the default `EmptyState` has always had).
|
|
896
|
+
`stateCopy(kind, family)` gives the i18n keys and the English fallbacks:
|
|
897
|
+
`material.empty-state.<kind>.*` for the empty kinds, and for `EmptyState`
|
|
898
|
+
given a legacy error `variant`; `material.error-state.<kind>.*` for the error
|
|
899
|
+
kinds of `ErrorState` (`family` is `'empty-state'` or `'error-state'`).
|
|
900
|
+
`STATE_ILLUSTRATIONS` is each kind's SVG geometry as data (shapes with a
|
|
901
|
+
`tone`, no colour and no style); the libraries paint it with classes on theme
|
|
902
|
+
tokens, so it is CSP-safe. `STATE_ACCENT` and `STATE_ICON` name each kind's
|
|
903
|
+
accent and its lucide icon (kebab-case).
|
|
904
|
+
|
|
905
|
+
`describeError(error, { maxDepth })` is what an `ErrorState` may show of a
|
|
906
|
+
thrown value: `{ name, message }` for the error then each `cause` (3 entries
|
|
907
|
+
by default), for an `Error`, a `{ message }` object, a string or a primitive,
|
|
908
|
+
stopping at a cycle. It **never reads `.stack`**: a stack names files and
|
|
909
|
+
paths. [More in docs/states.md](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/states.md).
|
|
910
|
+
|
|
911
|
+
```ts
|
|
912
|
+
import {
|
|
913
|
+
describeError,
|
|
914
|
+
resolveEmptyStateKind,
|
|
915
|
+
stateCopy,
|
|
916
|
+
} from '@nxgt/material-core/states';
|
|
917
|
+
|
|
918
|
+
resolveEmptyStateKind({}); // 'generic' — the legacy default
|
|
919
|
+
resolveEmptyStateKind({ kind: 'search', variant: 'offline' }); // 'search'
|
|
920
|
+
|
|
921
|
+
stateCopy('offline', 'error-state').titleKey; // 'material.error-state.offline.title'
|
|
922
|
+
stateCopy('offline', 'empty-state').title; // 'You are offline'
|
|
923
|
+
|
|
924
|
+
describeError(new Error('Could not save', { cause: new TypeError('Failed to fetch') }));
|
|
925
|
+
// [{ name: 'Error', message: 'Could not save' }, { name: 'TypeError', message: 'Failed to fetch' }]
|
|
926
|
+
```
|
|
927
|
+
|
|
791
928
|
### A field's blur across overlays: `/field-blur-layers`
|
|
792
929
|
|
|
793
930
|
A field whose popover, listbox or dialog renders into `<body>` must not count
|
|
@@ -866,7 +1003,7 @@ const said: RowAnnouncement = { kind: 'removed', index: index + 1 };
|
|
|
866
1003
|
status.textContent = said.kind === 'removed' ? `Removed period ${said.index}` : '';
|
|
867
1004
|
```
|
|
868
1005
|
|
|
869
|
-
### Escape while an overlay closes: `/escape-forward`
|
|
1006
|
+
### Escape while an overlay closes or a list is open: `/escape-forward`
|
|
870
1007
|
|
|
871
1008
|
Radix's and reka's dismissable layers give Escape to the highest layer only,
|
|
872
1009
|
and a dialog's layer leaves their stack when its content unmounts, not when
|
|
@@ -892,6 +1029,35 @@ function onEscapeKeyDown(event: KeyboardEvent) {
|
|
|
892
1029
|
}
|
|
893
1030
|
```
|
|
894
1031
|
|
|
1032
|
+
`keepListEscape(event)` is the other half: Escape pressed in a field whose
|
|
1033
|
+
list is open belongs to the list. A Base UI combobox (`ComboboxField`,
|
|
1034
|
+
`Combobox`) closes its list from a handler on its input, but a Radix or reka
|
|
1035
|
+
overlay listens on the document first and closes itself with the list: the
|
|
1036
|
+
dialog, sheet, drawer or popover around the field vanished on the first
|
|
1037
|
+
Escape. Called from the content's escape handler, before the caller's own,
|
|
1038
|
+
`keepListEscape` prevents the default when the event's target is an open
|
|
1039
|
+
list's input, and returns whether it did; the list still closes, and the
|
|
1040
|
+
second Escape closes the overlay. The caller's handler then sees
|
|
1041
|
+
`event.defaultPrevented` true for an Escape that only closes a list. The
|
|
1042
|
+
target is read from the event's composed path and tested by `nodeType`, so a
|
|
1043
|
+
field in an iframe or a shadow root counts. The input counts as open when it
|
|
1044
|
+
is a `role="combobox"` with `aria-expanded="true"` whose `aria-controls`
|
|
1045
|
+
list sits in a `[data-slot="combobox-content"]` popup (a command palette's
|
|
1046
|
+
input is not), or when it carries `data-list-open="true"` (the Vue
|
|
1047
|
+
`Autocomplete`'s textbox).
|
|
1048
|
+
|
|
1049
|
+
```ts
|
|
1050
|
+
import { forwardEscape, keepListEscape } from '@nxgt/material-core/escape-forward';
|
|
1051
|
+
|
|
1052
|
+
const dialog = document.querySelector<HTMLElement>('[role="dialog"]');
|
|
1053
|
+
|
|
1054
|
+
function onEscapeKeyDown(event: KeyboardEvent) {
|
|
1055
|
+
keepListEscape(event); // an open list takes this Escape
|
|
1056
|
+
if (event.defaultPrevented) return; // the dialog stays open
|
|
1057
|
+
forwardEscape(dialog, event);
|
|
1058
|
+
}
|
|
1059
|
+
```
|
|
1060
|
+
|
|
895
1061
|
### A field's `aria-describedby`: `/described-by`
|
|
896
1062
|
|
|
897
1063
|
`describedBy(own, ...ids)` joins the ids a field's control is described by:
|
|
@@ -1031,22 +1197,99 @@ row.addEventListener('click', (event) => {
|
|
|
1031
1197
|
});
|
|
1032
1198
|
```
|
|
1033
1199
|
|
|
1200
|
+
### Background presets: `/backgrounds`
|
|
1201
|
+
|
|
1202
|
+
The engine under the libraries' background presets, with the grain built so far; the other presets follow.
|
|
1203
|
+
A background is a **scene**: a plain tree `{ tag, attrs, children }` of SVG
|
|
1204
|
+
elements that each library renders with `createElement` or `h` — never as an
|
|
1205
|
+
HTML string. `assertSafeScene` throws for a `style`, an `on*` handler, an
|
|
1206
|
+
`href`, a URL, or a `url(…)` that is not the id of an element in the same
|
|
1207
|
+
scene, so a scene can never need `style-src-attr 'unsafe-inline'`. Everything
|
|
1208
|
+
is **seeded**: the same `seed` gives the same scene on the server and in the
|
|
1209
|
+
browser (no `Math.random`, nothing measured).
|
|
1210
|
+
|
|
1211
|
+
```ts
|
|
1212
|
+
import {
|
|
1213
|
+
assertSafeScene,
|
|
1214
|
+
mulberry32,
|
|
1215
|
+
motifShape,
|
|
1216
|
+
observeBackgroundActivity,
|
|
1217
|
+
place,
|
|
1218
|
+
scatter,
|
|
1219
|
+
svgIdPrefix,
|
|
1220
|
+
} from '@nxgt/material-core/backgrounds';
|
|
1221
|
+
|
|
1222
|
+
const rand = mulberry32(42);
|
|
1223
|
+
const dots = scatter(rand, [60, 40, 24, 12]).map((p) =>
|
|
1224
|
+
place({ x: p.x, y: p.y, className: 'k1', opacity: 0.6 }, motifShape('ring', p.s, false)),
|
|
1225
|
+
);
|
|
1226
|
+
const scene = { tag: 'svg', attrs: { viewBox: '0 0 1600 1000' }, children: dots } as const;
|
|
1227
|
+
assertSafeScene(scene, svgIdPrefix(':r1:')); // throws if anything is unsafe
|
|
1228
|
+
|
|
1229
|
+
// pause an animation when the page is hidden, the element is off screen or
|
|
1230
|
+
// the user prefers reduced motion; call it from an effect, once mounted
|
|
1231
|
+
const stop = observeBackgroundActivity(element, (paused) => {
|
|
1232
|
+
element.toggleAttribute('data-paused', paused);
|
|
1233
|
+
});
|
|
1234
|
+
stop();
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
`svgIdPrefix(useId())` makes a generated id safe inside `url(#…)`
|
|
1238
|
+
(`':r1:'` becomes `'bg-r1'`). `BACKGROUND_PRESETS` is the table of the 20
|
|
1239
|
+
preset ids with their family, how each is drawn and whether `seed` changes
|
|
1240
|
+
it. `buildBackgroundScene(preset, { idPrefix, seed })` returns the scene of a
|
|
1241
|
+
preset, or `null` for a CSS-only one or one with no builder yet (only `grain` has one so far).
|
|
1242
|
+
`SHIPPED_BACKGROUND_PRESETS` (`gradient`, `grain`) is what the libraries draw today, and
|
|
1243
|
+
`resolveShippedPreset(id)` returns `id` when it ships, else `'gradient'`, with a one-time
|
|
1244
|
+
warning outside production. See
|
|
1245
|
+
[Backgrounds](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/backgrounds.md).
|
|
1246
|
+
|
|
1247
|
+
### One plugin: `nxgtMaterial()` (`/vite`)
|
|
1248
|
+
|
|
1249
|
+
```ts
|
|
1250
|
+
// vite.config.ts
|
|
1251
|
+
import { nxgtMaterial } from '@nxgt/material-core/vite';
|
|
1252
|
+
import { defineConfig } from 'vite';
|
|
1253
|
+
|
|
1254
|
+
export default defineConfig({ plugins: [...nxgtMaterial({ csp: { mode: 'hash' } })] });
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
It sets up a strict Content Security Policy (zod's jitless script, then the
|
|
1258
|
+
sha256 of every inline script and style of `index.html` written to
|
|
1259
|
+
`dist/.vite/nxgt-csp.json` with a ready header that uses the script hashes, or `csp: { mode: 'nonce' }`
|
|
1260
|
+
for a placeholder your server fills with `applyCspNonce`), keeps fonts out of
|
|
1261
|
+
`data:` URIs, and, when called with a library name (the libraries'
|
|
1262
|
+
`/vite` do it), loads Inter (when `@fontsource-variable/inter` is installed; otherwise it warns and keeps the system font) and fails the build when the library's stylesheet
|
|
1263
|
+
is not wired (neither `styles.css` nor the compiled stylesheet,
|
|
1264
|
+
`@import "<library>";`). The libraries' [Vite plugin guide](https://cdn.jsdelivr.net/npm/@nxgt/material@2/docs/guides/vite-plugin.md)
|
|
1265
|
+
has every option, and the [migration guide](https://cdn.jsdelivr.net/npm/@nxgt/material@2/docs/guides/migrate-to-vite-plugin.md)
|
|
1266
|
+
moves a hand-written setup over. It reads stylesheets and the renderer's
|
|
1267
|
+
`package.json` files from disk (`node:fs`, `node:fs/promises`, `node:path`), so
|
|
1268
|
+
`/vite` is build-time code like `/pdfkit-fonts`.
|
|
1269
|
+
|
|
1034
1270
|
### Bundling pdfkit's fonts: `/vite`
|
|
1035
1271
|
|
|
1036
1272
|
An SSR server bundled into a single file with no `node_modules` needs
|
|
1037
|
-
pdfkit's standard fonts inlined, or it throws on every request
|
|
1273
|
+
pdfkit's standard fonts inlined, or it throws on every request. With a
|
|
1274
|
+
library name (the libraries' `/vite` pass it), `nxgtMaterial()` does both
|
|
1275
|
+
halves:
|
|
1038
1276
|
|
|
1039
1277
|
```ts
|
|
1040
1278
|
// vite.config.ts
|
|
1041
|
-
import {
|
|
1279
|
+
import { nxgtMaterial } from '@nxgt/material-core/vite';
|
|
1042
1280
|
import { defineConfig } from 'vite';
|
|
1043
1281
|
|
|
1044
|
-
export default defineConfig({
|
|
1282
|
+
export default defineConfig({
|
|
1283
|
+
plugins: [...nxgtMaterial({ pdf: { bundle: true } }, '@nxgt/material')],
|
|
1284
|
+
});
|
|
1045
1285
|
```
|
|
1046
1286
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1287
|
+
`pdf` is on by default (`pdfkitStandardFonts()`, a no-op unless pdfkit goes
|
|
1288
|
+
through the bundle); `pdf: { bundle: true }` also puts the library, core,
|
|
1289
|
+
`pdfkit`, `@react-pdf/*` and the renderer's runtime dependencies into
|
|
1290
|
+
`ssr.noExternal` (`react` and your own dependencies stay yours to bundle). `@nxgt/material/vite` and
|
|
1291
|
+
`@nxgt/material-vue/vite` export `pdfkitStandardFonts` on its own too; its doc
|
|
1292
|
+
comment explains when keeping the library external is the lighter answer.
|
|
1050
1293
|
|
|
1051
1294
|
### Fonts under a strict CSP: `cspAssetsInlineLimit` (`/vite`)
|
|
1052
1295
|
|
|
@@ -1121,6 +1364,7 @@ Every message, with its cause, is in [troubleshooting](https://cdn.jsdelivr.net/
|
|
|
1121
1364
|
## Documentation
|
|
1122
1365
|
|
|
1123
1366
|
- [docs/README.md](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/README.md) — every page, and when to read it
|
|
1367
|
+
- [States](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/states.md) — the kinds, copy keys, `describeError` and illustrations of `EmptyState` and `ErrorState`
|
|
1124
1368
|
- [Troubleshooting](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md) — every error message, and its fix
|
|
1125
1369
|
- [Roadmap](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/roadmap.md) — what is next
|
|
1126
1370
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tells `onChange` whether an animated background should be paused: the page
|
|
3
|
+
* is hidden, the element is off screen, or the user prefers reduced motion.
|
|
4
|
+
* Calls `onChange` once with the current state, then on each change only.
|
|
5
|
+
* Returns the function that removes every listener and observer.
|
|
6
|
+
*
|
|
7
|
+
* Reads the DOM when called, never at import: call it from an effect or
|
|
8
|
+
* `onMounted`, and only when the background is `animated`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function observeBackgroundActivity(el: Element, onChange: (paused: boolean) => void): () => void;
|
|
11
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/backgrounds/activity.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACxC,EAAE,EAAE,OAAO,EACX,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GACjC,MAAM,IAAI,CAiCZ"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BackgroundPreset, SceneNode, SceneOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* The SVG scene of a preset, or `null` when it has none (the CSS-only
|
|
4
|
+
* presets, and a preset whose builder is not there yet). The same `seed`
|
|
5
|
+
* always gives the same scene. The scene is checked with `assertSafeScene`
|
|
6
|
+
* before it is returned.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildBackgroundScene(preset: BackgroundPreset, opts: SceneOptions): SceneNode | null;
|
|
9
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/backgrounds/build.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACX,gBAAgB,EAEhB,SAAS,EACT,YAAY,EACZ,MAAM,SAAS,CAAC;AAWjB;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,YAAY,GAChB,SAAS,GAAG,IAAI,CAIlB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns a framework's generated id (React's `useId` gives `:r1:` or `«r1»`,
|
|
3
|
+
* Vue's `v-0`) into a prefix that is safe inside `url(#…)` and as an `id`:
|
|
4
|
+
* only `A-Z a-z 0-9 _ -`, and a letter first. Ids in a scene are
|
|
5
|
+
* `<prefix>-<name>`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function svgIdPrefix(raw: string): string;
|
|
8
|
+
//# sourceMappingURL=id-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-prefix.d.ts","sourceRoot":"","sources":["../../src/backgrounds/id-prefix.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG/C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@nxgt/material-core/backgrounds` — the engine behind the background
|
|
3
|
+
* presets: types, the preset table, a seeded random generator, noise,
|
|
4
|
+
* scattering, the motif primitives, the scene format and its safety check,
|
|
5
|
+
* ids for `url(#…)`, and the activity observer that pauses an animation.
|
|
6
|
+
* Framework-free; the libraries render a scene with `createElement` / `h`.
|
|
7
|
+
*/
|
|
8
|
+
export { observeBackgroundActivity } from './activity';
|
|
9
|
+
export { buildBackgroundScene } from './build';
|
|
10
|
+
export { svgIdPrefix } from './id-prefix';
|
|
11
|
+
export { valueNoise2 } from './noise';
|
|
12
|
+
export { BACKGROUND_PRESETS, resolveShippedPreset, SHIPPED_BACKGROUND_PRESETS, } from './presets';
|
|
13
|
+
export { buildGrain } from './presets/grain';
|
|
14
|
+
export { MOTIF_DEFAULTS, MOTIF_SHAPES, motifShape, type PlaceOptions, place, } from './primitives';
|
|
15
|
+
export { mulberry32, pick, round1 } from './rng';
|
|
16
|
+
export { type ScatterOptions, type ScatterPoint, scatter, } from './scatter';
|
|
17
|
+
export { assertSafeScene, el, SCENE_ATTRIBUTES, SCENE_TAGS, } from './scene';
|
|
18
|
+
export { BACKGROUND_INTENSITIES, BACKGROUND_TONES } from './tones';
|
|
19
|
+
export type { BackgroundIntensity, BackgroundPreset, BackgroundTone, MotifDensity, MotifOptions, MotifShape, MotifSize, OpacitySpread, PresetInfo, SceneBuilder, SceneNode, SceneOptions, SceneTag, ShapeFill, ShippedBackgroundPreset, } from './types';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backgrounds/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EACN,kBAAkB,EAClB,oBAAoB,EACpB,0BAA0B,GAC1B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACN,cAAc,EACd,YAAY,EACZ,UAAU,EACV,KAAK,YAAY,EACjB,KAAK,GACL,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,OAAO,GACP,MAAM,WAAW,CAAC;AACnB,OAAO,EACN,eAAe,EACf,EAAE,EACF,gBAAgB,EAChB,UAAU,GACV,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACnE,YAAY,EACX,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,SAAS,EACT,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,uBAAuB,GACvB,MAAM,SAAS,CAAC"}
|