@nifrajs/web-react 3.3.0 → 4.0.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 (46) hide show
  1. package/dist/auth.d.ts +32 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +85 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/client.d.ts.map +1 -1
  6. package/dist/client.js +9 -24
  7. package/dist/client.js.map +1 -1
  8. package/dist/compose.d.ts.map +1 -1
  9. package/dist/compose.js +1 -0
  10. package/dist/compose.js.map +1 -1
  11. package/dist/content.d.ts +2 -1
  12. package/dist/content.d.ts.map +1 -1
  13. package/dist/content.js.map +1 -1
  14. package/dist/i18n.d.ts +19 -6
  15. package/dist/i18n.d.ts.map +1 -1
  16. package/dist/i18n.js +27 -6
  17. package/dist/i18n.js.map +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +17 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/react-dom-server.d.ts +3 -1
  22. package/dist/react-dom-server.d.ts.map +1 -1
  23. package/dist/react-dom-server.js.map +1 -1
  24. package/dist/render-error.d.ts +4 -0
  25. package/dist/render-error.d.ts.map +1 -0
  26. package/dist/render-error.js +15 -0
  27. package/dist/render-error.js.map +1 -0
  28. package/dist/route-props.d.ts +4 -0
  29. package/dist/route-props.d.ts.map +1 -0
  30. package/dist/route-props.js +32 -0
  31. package/dist/route-props.js.map +1 -0
  32. package/dist/router.d.ts +23 -3
  33. package/dist/router.d.ts.map +1 -1
  34. package/dist/router.js +29 -2
  35. package/dist/router.js.map +1 -1
  36. package/package.json +14 -5
  37. package/src/auth.ts +128 -0
  38. package/src/client.ts +9 -24
  39. package/src/compose.ts +1 -0
  40. package/src/content.ts +3 -1
  41. package/src/i18n.ts +49 -9
  42. package/src/index.ts +15 -3
  43. package/src/react-dom-server.ts +4 -1
  44. package/src/render-error.ts +18 -0
  45. package/src/route-props.ts +37 -0
  46. package/src/router.ts +42 -4
package/src/router.ts CHANGED
@@ -22,6 +22,9 @@ import type {
22
22
  NavigateFunction,
23
23
  NavigateOptions,
24
24
  NavigateTargetInput,
25
+ PrefetchMode,
26
+ RenderProps,
27
+ UIMatch,
25
28
  } from "@nifrajs/web"
26
29
  // `/client`, not the root - these are DOM values, and the root's graph carries the
27
30
  // server, which Vite's dev server evaluates rather than tree-shakes.
@@ -31,6 +34,8 @@ import {
31
34
  registerBlocker,
32
35
  resolveNavigate,
33
36
  } from "@nifrajs/web/client"
37
+ // Its own subpath, so an app that never calls `useMatches` bundles none of it.
38
+ import { chainMatches } from "@nifrajs/web/internal/matches-runtime"
34
39
  import {
35
40
  type AnchorHTMLAttributes,
36
41
  type CSSProperties,
@@ -47,7 +52,7 @@ import {
47
52
  useState,
48
53
  } from "react"
49
54
 
50
- export type { Blocker, BlockerFunction, BlockerState, NavigateFunction }
55
+ export type { Blocker, BlockerFunction, BlockerState, NavigateFunction, PrefetchMode, UIMatch }
51
56
 
52
57
  /** The current route the routing hooks read. Provided by `compose` on SSR + client mount alike. */
53
58
  export interface RouterContextValue {
@@ -66,6 +71,8 @@ export interface RouterContextValue {
66
71
  /** The `pathname + search` a navigation is transitioning TO while `pending` (`undefined` when idle or
67
72
  * during a same-route revalidation). Powers {@link NavLink}'s per-link `isPending`. */
68
73
  readonly pendingPath?: string | undefined
74
+ /** The render's props, which {@link useMatches} derives the matches from. */
75
+ readonly renderProps?: RenderProps | undefined
69
76
  }
70
77
 
71
78
  // Frozen empty params/search so the default context value has a stable reference (no needless re-renders).
@@ -141,6 +148,26 @@ export function useSearch<
141
148
  : Record<string, unknown>
142
149
  }
143
150
 
151
+ const NO_MATCHES: readonly UIMatch[] = Object.freeze([])
152
+
153
+ /**
154
+ * The rendered chain, outermost layout first and the page last: each module's `id`, the URL
155
+ * `pathname` it wraps, its `params` and loader `data`, and its `handle` export. The same list on the
156
+ * server and in the browser, so a layout can render breadcrumbs or read a flag the page exports:
157
+ *
158
+ * ```tsx
159
+ * export const handle = { crumb: "Settings" } // in a page or a layout
160
+ * // in a layout above it:
161
+ * const crumbs = useMatches().flatMap((m) => (m.handle as { crumb?: string } | undefined)?.crumb ?? [])
162
+ * ```
163
+ *
164
+ * Empty outside a nifra route tree.
165
+ */
166
+ export function useMatches(): readonly UIMatch[] {
167
+ const props = useContext(RouterContext).renderProps
168
+ return useMemo(() => (props === undefined ? NO_MATCHES : chainMatches(props)), [props])
169
+ }
170
+
144
171
  /** The parsed current location. `hash` is always `""` - the fragment is client-only and never reaches
145
172
  * the router state / server, so exposing a live hash would hydration-mismatch; read `window.location.hash`
146
173
  * directly (in an effect) if you truly need it. */
@@ -307,12 +334,16 @@ function isPlainLeftClick(event: MouseEvent, target: string | undefined): boolea
307
334
  return target === undefined || target === "" || target === "_self"
308
335
  }
309
336
 
310
- /** {@link Link} props: every `<a>` attribute except `href` (set from `to`), plus `to` + `replace`. */
337
+ /** {@link Link} props: every `<a>` attribute except `href` (set from `to`), plus `to`, `replace` and
338
+ * `prefetch`. */
311
339
  export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
312
340
  /** Same-origin destination path (e.g. `/users/7?tab=posts`). Rendered as the `<a href>`. */
313
341
  readonly to: string
314
342
  /** Replace the current history entry instead of pushing. */
315
343
  readonly replace?: boolean
344
+ /** When the link warms its route ahead of a click - rendered as `data-nifra-prefetch`. Unset, the
345
+ * nearest ancestor's `data-nifra-prefetch` decides, else `intent` (hover or focus). */
346
+ readonly prefetch?: PrefetchMode
316
347
  }
317
348
 
318
349
  /**
@@ -322,7 +353,7 @@ export interface LinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>,
322
353
  * document-level click handler sees `defaultPrevented` and stands down - exactly one navigation.
323
354
  */
324
355
  export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
325
- { to, replace, onClick, target, ...rest },
356
+ { to, replace, prefetch, onClick, target, ...rest },
326
357
  ref,
327
358
  ) {
328
359
  const handleClick = (event: MouseEvent<HTMLAnchorElement>): void => {
@@ -334,7 +365,14 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
334
365
  event.preventDefault()
335
366
  navigate(to, { replace: replace === true })
336
367
  }
337
- return createElement("a", { ...rest, href: to, target, onClick: handleClick, ref })
368
+ return createElement("a", {
369
+ ...rest,
370
+ href: to,
371
+ target,
372
+ onClick: handleClick,
373
+ ref,
374
+ ...(prefetch !== undefined ? { "data-nifra-prefetch": prefetch } : {}),
375
+ })
338
376
  })
339
377
 
340
378
  /** The state a {@link NavLink}'s function-form `className`/`style`/`children` receive. */