@nxgt/material-core 0.11.0 → 0.12.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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # @nxgt/material-core
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#624](https://github.com/softistx/nxgt-material/pull/624) [`94a77a7`](https://github.com/softistx/nxgt-material/commit/94a77a76b54834bda18d1e2acc494d5a089d245d) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material-core/initials`: `initials(name)`, an avatar's initials from a name (`'Ada Lovelace'` → `'AL'`), the first letter of the first and last word, upper-cased, a whole code point each. Both libraries' new `AccountMenu` use it ([#585](https://github.com/softistx/nxgt-material/issues/585)).
8
+
9
+ ## 0.11.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [#607](https://github.com/softistx/nxgt-material/pull/607) [`3b671a5`](https://github.com/softistx/nxgt-material/commit/3b671a5b8d0109fa645d0de52bc716104f7f8831) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Two fixes seen on a phone and on the collapsed sidebar (softistx/nxgt-material#605).
14
+
15
+ - **`SidebarMenuItem` no tooltip beside a visible label.** The collapsed rail showed each label under its icon and also opened a tooltip repeating it, above the entry, over the entry above it (the selected pill). An entry that shows its label, collapsed or expanded, now has no tooltip; its accessible name is the label. A `tooltip` you pass is still shown, now to the right of the entry (`side="right"`) so it never covers a neighbour, and an icon-only entry (no label, an `aria-label`) shows its `aria-label` there. Keyboard focus shows it as before. `Tooltip` (React) takes a `side` prop. In `@nxgt/material-vue`, `SidebarMenuItem` now binds its attributes (`aria-label`, `onClick`...) to the link or button itself.
16
+ - **`Toaster` on a phone starts below the app bar.** Under 600px with no `position`, toasts sat at the very top and covered the app shell's sticky bar (menu button, title, account control), which could not be reached while a toast showed. They now start 80px down (`mobileOffset` `{ top: 80 }`, from `@nxgt/material-core/touch-hover`). Desktop is unchanged, and a `position` or a `mobileOffset` you pass is kept as is.
17
+
18
+ Migration: an app that relied on the toast at the very top of a phone, or on the collapsed rail's label tooltip, sets `<Toaster mobileOffset={{ top: 16 }} />` (Vue: `:mobile-offset="{ top: 16 }"`) or passes `tooltip` on the entry.
19
+
3
20
  ## 0.11.0
4
21
 
5
22
  ### Minor Changes
package/README.md CHANGED
@@ -104,6 +104,7 @@ built-in.
104
104
  | `/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 |
105
105
  | `/lazy-object` | `lazyObject`, `buildOnce` — a value built on its first use rather than when its module loads: the libraries' zod schemas |
106
106
  | `/debounce` | `debounce`, the type `Debounced` — a function called once calls pause, with `cancel` and `flush` (`SearchField`'s `debounceMs`) |
107
+ | `/initials` | `initials` — an avatar's initials from a name, `'Ada Lovelace'` → `'AL'` (`AccountMenu`) |
107
108
  | `/text-match` | `foldText`, `matchesText` — case- and accent-insensitive matching (`'e'` finds "École"), lowercased for a locale |
108
109
  | `/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 |
109
110
  | `/submit-guard` | `createSubmitGuard`, `busySettler` and the types `SubmitGuard`, `SubmitOnceOptions` — one submit in flight until its request ends, behind both libraries' `useSubmitOnce` |
@@ -407,6 +408,20 @@ save('ab'); // 300 ms later: "save ab", once
407
408
  save.flush(); // or now
408
409
  ```
409
410
 
411
+ ### An avatar's initials: `/initials`
412
+
413
+ What both libraries' `AccountMenu` shows in its avatar when there is no
414
+ picture: the first letter of the first and of the last word, upper-cased, a
415
+ whole code point each.
416
+
417
+ ```ts
418
+ import { initials } from '@nxgt/material-core/initials';
419
+
420
+ initials('Ada Lovelace'); // 'AL'
421
+ initials('Ada'); // 'A'
422
+ initials(' '); // ''
423
+ ```
424
+
410
425
  ### Matching text as the user types: `/text-match`
411
426
 
412
427
  `foldText(text, locale?)` lowercases `text` for `locale` (the runtime's when
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The initials an avatar shows in place of a picture: the first letter of the
3
+ * first and of the last word of `name`, upper-cased — `'Ada Lovelace'` →
4
+ * `'AL'`, `'Ada'` → `'A'`, `'Jean-Paul Sartre'` → `'JS'` — and `''` for a
5
+ * blank name. A letter is a whole code point, so an emoji or an astral
6
+ * character is never cut in half. Locale-free (`toUpperCase`, not
7
+ * `toLocaleUpperCase`): a server and a browser in Turkish or Azeri would
8
+ * otherwise upper-case an `i` differently, and the hydration would differ.
9
+ * Both libraries' `AccountMenu` use it.
10
+ */
11
+ export declare function initials(name: string): string;
12
+ //# sourceMappingURL=initials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initials.d.ts","sourceRoot":"","sources":["../../src/lib/initials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS7C"}
@@ -0,0 +1,15 @@
1
+ // src/lib/initials.ts
2
+ function initials(name) {
3
+ const words = name.trim().split(/\s+/).filter(Boolean);
4
+ if (words.length === 0)
5
+ return "";
6
+ const first = words[0];
7
+ const last = words.length > 1 ? words.at(-1) : "";
8
+ return [first, last].map((word) => Array.from(word)[0] ?? "").join("").toUpperCase();
9
+ }
10
+ export {
11
+ initials
12
+ };
13
+
14
+ //# debugId=BB7FB6C4C0A3758264756E2164756E21
15
+ //# sourceMappingURL=initials.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/lib/initials.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * The initials an avatar shows in place of a picture: the first letter of the\n * first and of the last word of `name`, upper-cased — `'Ada Lovelace'` →\n * `'AL'`, `'Ada'` → `'A'`, `'Jean-Paul Sartre'` → `'JS'` — and `''` for a\n * blank name. A letter is a whole code point, so an emoji or an astral\n * character is never cut in half. Locale-free (`toUpperCase`, not\n * `toLocaleUpperCase`): a server and a browser in Turkish or Azeri would\n * otherwise upper-case an `i` differently, and the hydration would differ.\n * Both libraries' `AccountMenu` use it.\n */\nexport function initials(name: string): string {\n\tconst words = name.trim().split(/\\s+/).filter(Boolean);\n\tif (words.length === 0) return '';\n\tconst first = words[0] as string;\n\tconst last = words.length > 1 ? (words.at(-1) as string) : '';\n\treturn [first, last]\n\t\t.map((word) => Array.from(word)[0] ?? '')\n\t\t.join('')\n\t\t.toUpperCase();\n}\n"
6
+ ],
7
+ "mappings": ";AAUO,SAAS,QAAQ,CAAC,MAAsB;AAAA,EAC9C,MAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AAAA,EACrD,IAAI,MAAM,WAAW;AAAA,IAAG,OAAO;AAAA,EAC/B,MAAM,QAAQ,MAAM;AAAA,EACpB,MAAM,OAAO,MAAM,SAAS,IAAK,MAAM,GAAG,EAAE,IAAe;AAAA,EAC3D,OAAO,CAAC,OAAO,IAAI,EACjB,IAAI,CAAC,SAAS,MAAM,KAAK,IAAI,EAAE,MAAM,EAAE,EACvC,KAAK,EAAE,EACP,YAAY;AAAA;",
8
+ "debugId": "BB7FB6C4C0A3758264756E2164756E21",
9
+ "names": []
10
+ }
@@ -5,6 +5,13 @@
5
5
  export declare const TOASTER_PHONE_QUERY = "(max-width: 600px)";
6
6
  /** Where toasts go on a phone when the caller names no `position`. */
7
7
  export declare const TOASTER_PHONE_POSITION = "top-center";
8
+ /**
9
+ * How far below the top of a phone the toasts start when the caller names no
10
+ * `position`: past the app shell's sticky bar (64px high, 8px from the top),
11
+ * so the menu button, the title and the account control stay reachable while
12
+ * a toast shows. The caller's own `mobileOffset` replaces it.
13
+ */
14
+ export declare const TOASTER_PHONE_OFFSET_TOP = 80;
8
15
  type Listen = Pick<Document, 'addEventListener' | 'removeEventListener'>;
9
16
  /**
10
17
  * Keeps a tap from hovering whatever matches `selector`: while the last
@@ -1 +1 @@
1
- {"version":3,"file":"touch-hover.d.ts","sourceRoot":"","sources":["../../src/lib/touch-hover.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAYnD,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,kBAAkB,GAAG,qBAAqB,CAAC,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,IAAI,CAqB1E"}
1
+ {"version":3,"file":"touch-hover.d.ts","sourceRoot":"","sources":["../../src/lib/touch-hover.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAY3C,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,kBAAkB,GAAG,qBAAqB,CAAC,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,IAAI,CAqB1E"}
@@ -1,6 +1,7 @@
1
1
  // src/lib/touch-hover.ts
2
2
  var TOASTER_PHONE_QUERY = "(max-width: 600px)";
3
3
  var TOASTER_PHONE_POSITION = "top-center";
4
+ var TOASTER_PHONE_OFFSET_TOP = 80;
4
5
  var HOVER_EVENTS = ["mouseover", "mouseenter", "mousemove"];
5
6
  var POINTER_EVENTS = ["pointerdown", "pointermove", "pointerup"];
6
7
  function ignoreTouchHover(doc, selector) {
@@ -30,10 +31,11 @@ function ignoreTouchHover(doc, selector) {
30
31
  };
31
32
  }
32
33
  export {
34
+ TOASTER_PHONE_OFFSET_TOP,
33
35
  TOASTER_PHONE_POSITION,
34
36
  TOASTER_PHONE_QUERY,
35
37
  ignoreTouchHover
36
38
  };
37
39
 
38
- //# debugId=F1500A469841898064756E2164756E21
40
+ //# debugId=5B9CEF7EA94C41D764756E2164756E21
39
41
  //# sourceMappingURL=touch-hover.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/lib/touch-hover.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * A phone, as sonner sees one: under 600px wide it lays the toasts out\n * across the whole width (`@media (max-width: 600px)` in its styles).\n */\nexport const TOASTER_PHONE_QUERY = '(max-width: 600px)';\n\n/** Where toasts go on a phone when the caller names no `position`. */\nexport const TOASTER_PHONE_POSITION = 'top-center';\n\n/**\n * The mouse events a browser emulates after a tap (\"compatibility mouse\n * events\"): a tap reads as a mouse arriving over what was tapped and staying\n * there, with no `mouseleave` until the next tap lands somewhere else.\n */\nconst HOVER_EVENTS = ['mouseover', 'mouseenter', 'mousemove'] as const;\n\n/** The pointer events that say which kind of pointer is in use. */\nconst POINTER_EVENTS = ['pointerdown', 'pointermove', 'pointerup'] as const;\n\ntype Listen = Pick<Document, 'addEventListener' | 'removeEventListener'>;\n\n/**\n * Keeps a tap from hovering whatever matches `selector`: while the last\n * pointer was a finger (`pointerType: 'touch'`), the hover events the browser\n * emulates for it (`mouseover`, `mouseenter`, `mousemove`) stop at `doc`, in\n * the capture phase, before any handler inside the matching element hears\n * them. A mouse or a pen hovers as before: its first `pointermove` ends the\n * touch. `mouseout` and `mouseleave` always pass, so a hover that did start\n * still ends.\n *\n * Toasts pause while hovered (WCAG 2.2.1), and a tap on one used to leave it\n * \"hovered\", so paused, until the next tap elsewhere. Returns the function\n * that removes the listeners; nothing is read before an event arrives.\n */\nexport function ignoreTouchHover(doc: Listen, selector: string): () => void {\n\tlet touch = false;\n\tconst onPointer = (event: Event) => {\n\t\ttouch = (event as PointerEvent).pointerType === 'touch';\n\t};\n\tconst onHover = (event: Event) => {\n\t\tif (!touch) return;\n\t\tconst target = event.target as Partial<Element> | null;\n\t\tif (typeof target?.closest !== 'function') return;\n\t\tif (target.closest(selector)) event.stopPropagation();\n\t};\n\tconst options = { capture: true, passive: true };\n\tfor (const type of POINTER_EVENTS)\n\t\tdoc.addEventListener(type, onPointer, options);\n\tfor (const type of HOVER_EVENTS) doc.addEventListener(type, onHover, options);\n\treturn () => {\n\t\tfor (const type of POINTER_EVENTS)\n\t\t\tdoc.removeEventListener(type, onPointer, options);\n\t\tfor (const type of HOVER_EVENTS)\n\t\t\tdoc.removeEventListener(type, onHover, options);\n\t};\n}\n"
5
+ "/**\n * A phone, as sonner sees one: under 600px wide it lays the toasts out\n * across the whole width (`@media (max-width: 600px)` in its styles).\n */\nexport const TOASTER_PHONE_QUERY = '(max-width: 600px)';\n\n/** Where toasts go on a phone when the caller names no `position`. */\nexport const TOASTER_PHONE_POSITION = 'top-center';\n\n/**\n * How far below the top of a phone the toasts start when the caller names no\n * `position`: past the app shell's sticky bar (64px high, 8px from the top),\n * so the menu button, the title and the account control stay reachable while\n * a toast shows. The caller's own `mobileOffset` replaces it.\n */\nexport const TOASTER_PHONE_OFFSET_TOP = 80;\n\n/**\n * The mouse events a browser emulates after a tap (\"compatibility mouse\n * events\"): a tap reads as a mouse arriving over what was tapped and staying\n * there, with no `mouseleave` until the next tap lands somewhere else.\n */\nconst HOVER_EVENTS = ['mouseover', 'mouseenter', 'mousemove'] as const;\n\n/** The pointer events that say which kind of pointer is in use. */\nconst POINTER_EVENTS = ['pointerdown', 'pointermove', 'pointerup'] as const;\n\ntype Listen = Pick<Document, 'addEventListener' | 'removeEventListener'>;\n\n/**\n * Keeps a tap from hovering whatever matches `selector`: while the last\n * pointer was a finger (`pointerType: 'touch'`), the hover events the browser\n * emulates for it (`mouseover`, `mouseenter`, `mousemove`) stop at `doc`, in\n * the capture phase, before any handler inside the matching element hears\n * them. A mouse or a pen hovers as before: its first `pointermove` ends the\n * touch. `mouseout` and `mouseleave` always pass, so a hover that did start\n * still ends.\n *\n * Toasts pause while hovered (WCAG 2.2.1), and a tap on one used to leave it\n * \"hovered\", so paused, until the next tap elsewhere. Returns the function\n * that removes the listeners; nothing is read before an event arrives.\n */\nexport function ignoreTouchHover(doc: Listen, selector: string): () => void {\n\tlet touch = false;\n\tconst onPointer = (event: Event) => {\n\t\ttouch = (event as PointerEvent).pointerType === 'touch';\n\t};\n\tconst onHover = (event: Event) => {\n\t\tif (!touch) return;\n\t\tconst target = event.target as Partial<Element> | null;\n\t\tif (typeof target?.closest !== 'function') return;\n\t\tif (target.closest(selector)) event.stopPropagation();\n\t};\n\tconst options = { capture: true, passive: true };\n\tfor (const type of POINTER_EVENTS)\n\t\tdoc.addEventListener(type, onPointer, options);\n\tfor (const type of HOVER_EVENTS) doc.addEventListener(type, onHover, options);\n\treturn () => {\n\t\tfor (const type of POINTER_EVENTS)\n\t\t\tdoc.removeEventListener(type, onPointer, options);\n\t\tfor (const type of HOVER_EVENTS)\n\t\t\tdoc.removeEventListener(type, onHover, options);\n\t};\n}\n"
6
6
  ],
7
- "mappings": ";AAIO,IAAM,sBAAsB;AAG5B,IAAM,yBAAyB;AAOtC,IAAM,eAAe,CAAC,aAAa,cAAc,WAAW;AAG5D,IAAM,iBAAiB,CAAC,eAAe,eAAe,WAAW;AAiB1D,SAAS,gBAAgB,CAAC,KAAa,UAA8B;AAAA,EAC3E,IAAI,QAAQ;AAAA,EACZ,MAAM,YAAY,CAAC,UAAiB;AAAA,IACnC,QAAS,MAAuB,gBAAgB;AAAA;AAAA,EAEjD,MAAM,UAAU,CAAC,UAAiB;AAAA,IACjC,IAAI,CAAC;AAAA,MAAO;AAAA,IACZ,MAAM,SAAS,MAAM;AAAA,IACrB,IAAI,OAAO,QAAQ,YAAY;AAAA,MAAY;AAAA,IAC3C,IAAI,OAAO,QAAQ,QAAQ;AAAA,MAAG,MAAM,gBAAgB;AAAA;AAAA,EAErD,MAAM,UAAU,EAAE,SAAS,MAAM,SAAS,KAAK;AAAA,EAC/C,WAAW,QAAQ;AAAA,IAClB,IAAI,iBAAiB,MAAM,WAAW,OAAO;AAAA,EAC9C,WAAW,QAAQ;AAAA,IAAc,IAAI,iBAAiB,MAAM,SAAS,OAAO;AAAA,EAC5E,OAAO,MAAM;AAAA,IACZ,WAAW,QAAQ;AAAA,MAClB,IAAI,oBAAoB,MAAM,WAAW,OAAO;AAAA,IACjD,WAAW,QAAQ;AAAA,MAClB,IAAI,oBAAoB,MAAM,SAAS,OAAO;AAAA;AAAA;",
8
- "debugId": "F1500A469841898064756E2164756E21",
7
+ "mappings": ";AAIO,IAAM,sBAAsB;AAG5B,IAAM,yBAAyB;AAQ/B,IAAM,2BAA2B;AAOxC,IAAM,eAAe,CAAC,aAAa,cAAc,WAAW;AAG5D,IAAM,iBAAiB,CAAC,eAAe,eAAe,WAAW;AAiB1D,SAAS,gBAAgB,CAAC,KAAa,UAA8B;AAAA,EAC3E,IAAI,QAAQ;AAAA,EACZ,MAAM,YAAY,CAAC,UAAiB;AAAA,IACnC,QAAS,MAAuB,gBAAgB;AAAA;AAAA,EAEjD,MAAM,UAAU,CAAC,UAAiB;AAAA,IACjC,IAAI,CAAC;AAAA,MAAO;AAAA,IACZ,MAAM,SAAS,MAAM;AAAA,IACrB,IAAI,OAAO,QAAQ,YAAY;AAAA,MAAY;AAAA,IAC3C,IAAI,OAAO,QAAQ,QAAQ;AAAA,MAAG,MAAM,gBAAgB;AAAA;AAAA,EAErD,MAAM,UAAU,EAAE,SAAS,MAAM,SAAS,KAAK;AAAA,EAC/C,WAAW,QAAQ;AAAA,IAClB,IAAI,iBAAiB,MAAM,WAAW,OAAO;AAAA,EAC9C,WAAW,QAAQ;AAAA,IAAc,IAAI,iBAAiB,MAAM,SAAS,OAAO;AAAA,EAC5E,OAAO,MAAM;AAAA,IACZ,WAAW,QAAQ;AAAA,MAClB,IAAI,oBAAoB,MAAM,WAAW,OAAO;AAAA,IACjD,WAAW,QAAQ;AAAA,MAClB,IAAI,oBAAoB,MAAM,SAAS,OAAO;AAAA;AAAA;",
8
+ "debugId": "5B9CEF7EA94C41D764756E2164756E21",
9
9
  "names": []
10
10
  }
package/docs/roadmap.md CHANGED
@@ -76,6 +76,9 @@ Nothing is in progress.
76
76
 
77
77
  ## Shipped
78
78
 
79
+ - **An avatar's initials** (`/initials`) — the first letter of a name's
80
+ first and last word, upper-cased and never cut inside a character, behind
81
+ both libraries' `AccountMenu`.
79
82
  - **A repeatable row group's focus rules** (`/repeatable-group`) — where the
80
83
  focus goes after a row is added, removed or moved, the add and remove
81
84
  limits, and what the live region announces, behind both libraries'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nxgt/material-core",
3
- "version": "0.11.0",
4
- "description": "The framework-free core of @nxgt/material and @nxgt/material-vue: the PDF document model and its react-pdf renderer, print formatters, PDF file helpers, the PDF viewer's pure modules, the currency digits, the file-size formatter, the field-blur layers, a field's first focusable control, a repeatable row group's focus and announcement rules, the aria-describedby joiner, the inert background of a modal layer, Escape forwarded while an overlay closes, the Toaster's touch-hover guard and its focus hand-back, the page's Content Security Policy nonce, an object built on first use, the zod issue messages, the date range presets, the debounce, accent-insensitive text matching and the race-free async search behind Autocomplete and SearchField, the submit guard, the code error map, the two-factor helpers (a grouped TOTP secret, the recovery codes' file, a text download), printing one element of the page, the file preview's kind detection and text reader, the pdfkit fonts Vite plugin, a CSP-safe Vite assetsInlineLimit and the Node-only pdfkit fonts resolution for Next.js and Nuxt builds",
3
+ "version": "0.12.0",
4
+ "description": "The framework-free core of @nxgt/material and @nxgt/material-vue: the PDF document model and its react-pdf renderer, print formatters, PDF file helpers, the PDF viewer's pure modules, the currency digits, the file-size formatter, the field-blur layers, a field's first focusable control, a repeatable row group's focus and announcement rules, the aria-describedby joiner, the inert background of a modal layer, Escape forwarded while an overlay closes, the Toaster's touch-hover guard and its focus hand-back, the page's Content Security Policy nonce, an object built on first use, the zod issue messages, the date range presets, the debounce, accent-insensitive text matching and the race-free async search behind Autocomplete and SearchField, the submit guard, the code error map, an avatar's initials, the two-factor helpers (a grouped TOTP secret, the recovery codes' file, a text download), printing one element of the page, the file preview's kind detection and text reader, the pdfkit fonts Vite plugin, a CSP-safe Vite assetsInlineLimit and the Node-only pdfkit fonts resolution for Next.js and Nuxt builds",
5
5
  "license": "MIT",
6
6
  "bugs": {
7
7
  "email": "stevegires@gmail.com"
@@ -178,6 +178,11 @@
178
178
  "import": "./dist/lib/debounce.js",
179
179
  "default": "./dist/lib/debounce.js"
180
180
  },
181
+ "./initials": {
182
+ "types": "./dist/lib/initials.d.ts",
183
+ "import": "./dist/lib/initials.js",
184
+ "default": "./dist/lib/initials.js"
185
+ },
181
186
  "./text-match": {
182
187
  "types": "./dist/lib/text-match.d.ts",
183
188
  "import": "./dist/lib/text-match.js",
@@ -288,6 +293,7 @@
288
293
  "src/lib/csp-nonce.ts",
289
294
  "src/lib/lazy-object.ts",
290
295
  "src/lib/debounce.ts",
296
+ "src/lib/initials.ts",
291
297
  "src/lib/text-match.ts",
292
298
  "src/lib/async-search.ts",
293
299
  "src/lib/submit-guard.ts",