@nifrajs/web-react 1.13.0 → 2.1.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.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAcnE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAE/F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAkB7D"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAcnE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAE/F;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAmB7D"}
package/dist/client.js CHANGED
@@ -30,6 +30,7 @@ export function mountRouter(options) {
30
30
  data: state.data,
31
31
  actionData: state.actionData,
32
32
  pending: state.pending,
33
+ ...(state.pendingPath !== undefined ? { pendingPath: state.pendingPath } : {}),
33
34
  // `params`/`path` feed the routing hooks (useParams/useLocation) via compose's RouterContext —
34
35
  // sourced from router state here, matching the SSR render's request-derived values on hydration.
35
36
  params: state.params,
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAA0B,oBAAoB,EAAE,MAAM,OAAO,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,wGAAwG;AACxG,+FAA+F;AAC/F,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB,EAAE,SAAkB;IACvF,WAAW,CAAC,SAAoB,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAC7C,gBAAgB,CAAC,MAAM,CAAC,CAAA,CAAC,+DAA+D;IACxF,MAAM,MAAM,GAAsB,GAAG,EAAE;QACrC,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;QACtF,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE;YAC1C,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,+FAA+F;YAC/F,iGAAiG;YACjG,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,2EAA2E;YAC3E,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC,CAAA;IACJ,CAAC,CAAA;IACD,WAAW,CAAC,SAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;AAC1D,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAA0B,oBAAoB,EAAE,MAAM,OAAO,CAAA;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAE/C,wGAAwG;AACxG,+FAA+F;AAC/F,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,mGAAmG;AACnG,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB,EAAE,SAAkB;IACvF,WAAW,CAAC,SAAoB,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAA;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAC7C,gBAAgB,CAAC,MAAM,CAAC,CAAA,CAAC,+DAA+D;IACxF,MAAM,MAAM,GAAsB,GAAG,EAAE;QACrC,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAA;QACtF,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE;YAC1C,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9E,+FAA+F;YAC/F,iGAAiG;YACjG,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,2EAA2E;YAC3E,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC,CAAA;IACJ,CAAC,CAAA;IACD,WAAW,CAAC,SAAoB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAA;AAC1D,CAAC"}
package/dist/compose.d.ts CHANGED
@@ -5,10 +5,11 @@ import { type ReactNode } from "react";
5
5
  * (innermost) receives `props` (the loader data); each layout wraps the child via its
6
6
  * `children`. Shared by the server adapter (renderToString) and the client (hydrateRoot).
7
7
  *
8
- * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params` + the
9
- * current `path` (both threaded through `RenderProps` identically on SSR and client), so the routing
10
- * hooks (`useParams`/`useLocation`/`useSearchParams`) read the same value on both sides — no hydration
11
- * mismatch. A render with no routing fields (a non-router adapter usage) provides the empty default.
8
+ * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params`, the
9
+ * current `path`, and the client `pending` flag (all threaded through `RenderProps` identically on SSR
10
+ * and client), so the routing hooks (`useParams`/`useLocation`/`useNavigation`) read the same value on
11
+ * both sides - no hydration mismatch (`pending` is `false` on SSR and on the initial client render).
12
+ * A render with no routing fields (a non-router adapter usage) provides the empty default.
12
13
  */
13
14
  export declare function compose(chain: readonly unknown[], props: RenderProps): ReactNode;
14
15
  //# sourceMappingURL=compose.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAM7E;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,SAAS,CAYhF"}
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAyC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAM7E;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,SAAS,CAmBhF"}
package/dist/compose.js CHANGED
@@ -7,10 +7,11 @@ const EMPTY_PARAMS = Object.freeze({});
7
7
  * (innermost) receives `props` (the loader data); each layout wraps the child via its
8
8
  * `children`. Shared by the server adapter (renderToString) and the client (hydrateRoot).
9
9
  *
10
- * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params` + the
11
- * current `path` (both threaded through `RenderProps` identically on SSR and client), so the routing
12
- * hooks (`useParams`/`useLocation`/`useSearchParams`) read the same value on both sides — no hydration
13
- * mismatch. A render with no routing fields (a non-router adapter usage) provides the empty default.
10
+ * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params`, the
11
+ * current `path`, and the client `pending` flag (all threaded through `RenderProps` identically on SSR
12
+ * and client), so the routing hooks (`useParams`/`useLocation`/`useNavigation`) read the same value on
13
+ * both sides - no hydration mismatch (`pending` is `false` on SSR and on the initial client render).
14
+ * A render with no routing fields (a non-router adapter usage) provides the empty default.
14
15
  */
15
16
  export function compose(chain, props) {
16
17
  const last = chain.length - 1;
@@ -19,6 +20,13 @@ export function compose(chain, props) {
19
20
  // children passed as the 3rd arg (not a `children` prop) — React's canonical form.
20
21
  node = createElement(chain[i], null, node);
21
22
  }
22
- return createElement(RouterContext.Provider, { value: { params: props.params ?? EMPTY_PARAMS, path: props.path ?? "" } }, node);
23
+ return createElement(RouterContext.Provider, {
24
+ value: {
25
+ params: props.params ?? EMPTY_PARAMS,
26
+ path: props.path ?? "",
27
+ pending: props.pending ?? false,
28
+ pendingPath: props.pendingPath,
29
+ },
30
+ }, node);
23
31
  }
24
32
  //# sourceMappingURL=compose.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAA0C,MAAM,OAAO,CAAA;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,oGAAoG;AACpG,MAAM,YAAY,GAAqC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExE;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,IAAI,IAAI,GAAc,aAAa,CAAC,KAAK,CAAC,IAAI,CAAmC,EAAE,KAAK,CAAC,CAAA;IACzF,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,mFAAmF;QACnF,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAsB,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,aAAa,CAClB,aAAa,CAAC,QAAQ,EACtB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,YAAY,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,EAC3E,IAAI,CACL,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAA0C,MAAM,OAAO,CAAA;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,oGAAoG;AACpG,MAAM,YAAY,GAAqC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,OAAO,CAAC,KAAyB,EAAE,KAAkB;IACnE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,IAAI,IAAI,GAAc,aAAa,CAAC,KAAK,CAAC,IAAI,CAAmC,EAAE,KAAK,CAAC,CAAA;IACzF,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,mFAAmF;QACnF,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAsB,EAAE,IAAI,EAAE,IAAI,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,aAAa,CAClB,aAAa,CAAC,QAAQ,EACtB;QACE,KAAK,EAAE;YACL,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,YAAY;YACpC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;YACtB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,KAAK;YAC/B,WAAW,EAAE,KAAK,CAAC,WAAW;SAC/B;KACF,EACD,IAAI,CACL,CAAA;AACH,CAAC"}
package/dist/router.d.ts CHANGED
@@ -18,6 +18,13 @@ export interface RouterContextValue {
18
18
  readonly params: Readonly<Record<string, string>>;
19
19
  /** The current URL's `pathname + search` (no hash — the router never carries one). */
20
20
  readonly path: string;
21
+ /** True while a client navigation (or revalidation) is in flight — the current route stays mounted
22
+ * until the new one is ready. Always `false` on SSR (loaders block before render). Drives loading UI
23
+ * via {@link useNavigation}. */
24
+ readonly pending: boolean;
25
+ /** The `pathname + search` a navigation is transitioning TO while `pending` (`undefined` when idle or
26
+ * during a same-route revalidation). Powers {@link NavLink}'s per-link `isPending`. */
27
+ readonly pendingPath?: string | undefined;
21
28
  }
22
29
  /** Router context. The default ({} params, "" path) is what a component sees when rendered outside a
23
30
  * nifra route tree — the hooks stay defined (no throw) so a stray `useParams` degrades gracefully. */
@@ -38,6 +45,32 @@ export interface Location {
38
45
  }
39
46
  /** The current {@link Location} (`pathname`/`search`/`hash`), derived from the router context. */
40
47
  export declare function useLocation(): Location;
48
+ /** The current navigation state, mirroring the Remix `useNavigation()` shape for familiarity. */
49
+ export interface Navigation {
50
+ /** True while a client navigation (or revalidation) is in flight. The current route stays mounted
51
+ * until the new one is ready, so this drives a loading indicator, not a route swap. */
52
+ readonly pending: boolean;
53
+ /** `"loading"` while a navigation is in flight, else `"idle"`. */
54
+ readonly state: "idle" | "loading";
55
+ /** The `pathname + search` being navigated TO while `pending`; `undefined` when idle or during a
56
+ * same-route revalidation. Mirrors Remix's `useNavigation().location`. */
57
+ readonly location: string | undefined;
58
+ }
59
+ /**
60
+ * Observe client navigation to drive loading UI (a top-bar spinner, dimmed content, a skeleton). nifra
61
+ * navigates imperatively - it fetches the next route's chunk + loader data while the current route stays
62
+ * on screen, then swaps - so `pending` is the signal for "a transition is in flight," not a Suspense
63
+ * boundary. Always `{ pending: false, state: "idle" }` on the server (loaders block before render), so
64
+ * it is hydration-safe.
65
+ *
66
+ * ```tsx
67
+ * const { pending } = useNavigation()
68
+ * return <div className={pending ? "opacity-60" : ""}>{pending && <TopBarSpinner />}<Outlet /></div>
69
+ * ```
70
+ */
71
+ export declare function useNavigation(): Navigation;
72
+ /** Convenience boolean form of {@link useNavigation}: `true` while a client navigation is in flight. */
73
+ export declare function usePending(): boolean;
41
74
  /** A programmatic navigate: a string path (push, or replace via `{ replace: true }`) or a history delta
42
75
  * (`-1`/`1`). A no-op on the server / before hydration (a render-time navigate isn't valid — use
43
76
  * {@link Navigate}, which navigates in an effect). */
@@ -73,7 +106,8 @@ export declare const Link: import("react").ForwardRefExoticComponent<LinkProps &
73
106
  export interface NavLinkRenderProps {
74
107
  /** True when the current location matches this link's `to` (prefix match, or exact when `end`). */
75
108
  readonly isActive: boolean;
76
- /** Reserved for a future pending-navigation signal; currently always `false`. */
109
+ /** True while a client navigation to THIS link's target is in flight (matched like `isActive`).
110
+ * `false` on SSR and when idle. Use it to show a per-link spinner during the transition. */
77
111
  readonly isPending: boolean;
78
112
  }
79
113
  /** {@link NavLink} props — like {@link LinkProps}, but `className`/`style`/`children` may be functions
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
package/dist/router.js CHANGED
@@ -16,7 +16,11 @@ import { createContext, createElement, forwardRef, useCallback, useContext, useE
16
16
  const EMPTY_PARAMS = Object.freeze({});
17
17
  /** Router context. The default ({} params, "" path) is what a component sees when rendered outside a
18
18
  * nifra route tree — the hooks stay defined (no throw) so a stray `useParams` degrades gracefully. */
19
- export const RouterContext = createContext({ params: EMPTY_PARAMS, path: "" });
19
+ export const RouterContext = createContext({
20
+ params: EMPTY_PARAMS,
21
+ path: "",
22
+ pending: false,
23
+ });
20
24
  /** Split a `pathname + search` into its parts. `search` keeps its leading `?` (like `location.search`);
21
25
  * an empty query yields `""`. */
22
26
  function splitPath(path) {
@@ -41,6 +45,30 @@ export function useLocation() {
41
45
  return { pathname, search, hash: "" };
42
46
  }, [path]);
43
47
  }
48
+ /**
49
+ * Observe client navigation to drive loading UI (a top-bar spinner, dimmed content, a skeleton). nifra
50
+ * navigates imperatively - it fetches the next route's chunk + loader data while the current route stays
51
+ * on screen, then swaps - so `pending` is the signal for "a transition is in flight," not a Suspense
52
+ * boundary. Always `{ pending: false, state: "idle" }` on the server (loaders block before render), so
53
+ * it is hydration-safe.
54
+ *
55
+ * ```tsx
56
+ * const { pending } = useNavigation()
57
+ * return <div className={pending ? "opacity-60" : ""}>{pending && <TopBarSpinner />}<Outlet /></div>
58
+ * ```
59
+ */
60
+ export function useNavigation() {
61
+ const { pending, pendingPath } = useContext(RouterContext);
62
+ return useMemo(() => ({
63
+ pending,
64
+ state: pending ? "loading" : "idle",
65
+ location: pending ? pendingPath : undefined,
66
+ }), [pending, pendingPath]);
67
+ }
68
+ /** Convenience boolean form of {@link useNavigation}: `true` while a client navigation is in flight. */
69
+ export function usePending() {
70
+ return useContext(RouterContext).pending;
71
+ }
44
72
  /** Get the {@link NavigateFunction}. Stable across renders; resolves the browser navigate at call time
45
73
  * (so it works as soon as `installHistory` has run, and no-ops before then / on the server). */
46
74
  export function useNavigate() {
@@ -125,11 +153,19 @@ function normalizePath(pathname, caseSensitive) {
125
153
  */
126
154
  export const NavLink = forwardRef(function NavLink({ to, end, caseSensitive, className, style, children, ...rest }, ref) {
127
155
  const { pathname } = useLocation();
156
+ const { pending, pendingPath } = useContext(RouterContext);
128
157
  const cs = caseSensitive === true;
129
158
  const current = normalizePath(pathname, cs);
130
159
  const target = normalizePath(splitPath(to).pathname, cs);
131
160
  const isActive = end === true ? current === target : matchesPrefix(current, target);
132
- const renderProps = { isActive, isPending: false };
161
+ // isPending: a navigation is in flight AND it's heading to THIS link's target (same match rule as
162
+ // isActive). `false` on SSR and idle (no pendingPath).
163
+ const pendingTarget = pending && pendingPath !== undefined
164
+ ? normalizePath(splitPath(pendingPath).pathname, cs)
165
+ : undefined;
166
+ const isPending = pendingTarget !== undefined &&
167
+ (end === true ? pendingTarget === target : matchesPrefix(pendingTarget, target));
168
+ const renderProps = { isActive, isPending };
133
169
  const resolvedClassName = typeof className === "function" ? className(renderProps) : className;
134
170
  const resolvedStyle = typeof style === "function" ? style(renderProps) : style;
135
171
  const resolvedChildren = typeof children === "function" ? children(renderProps) : children;
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,kBAAkB,EAAwB,MAAM,cAAc,CAAA;AACvE,OAAO,EAGL,aAAa,EACb,aAAa,EACb,UAAU,EAGV,WAAW,EACX,UAAU,EACV,SAAS,EACT,OAAO,GACR,MAAM,OAAO,CAAA;AAiBd,oGAAoG;AACpG,MAAM,YAAY,GAAqC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;AAExE;sGACsG;AACtG,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAqB;IAC7D,MAAM,EAAE,YAAY;IACpB,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,KAAK;CACf,CAAC,CAAA;AAEF;iCACiC;AACjC,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAC;QACb,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QAChC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS;IAGvB,OAAO,UAAU,CAAC,aAAa,CAAC,CAAC,MAAqB,CAAA;AACxD,CAAC;AAWD,kGAAkG;AAClG,MAAM,UAAU,WAAW;IACzB,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IAC1C,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;QAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;IACvC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;AACZ,CAAC;AAcD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IAC1D,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC;QACL,OAAO;QACP,KAAK,EAAE,OAAO,CAAC,CAAC,CAAE,SAAmB,CAAC,CAAC,CAAE,MAAgB;QACzD,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;KAC5C,CAAC,EACF,CAAC,OAAO,EAAE,WAAW,CAAC,CACvB,CAAA;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,UAAU;IACxB,OAAO,UAAU,CAAC,aAAa,CAAC,CAAC,OAAO,CAAA;AAC1C,CAAC;AAOD;gGACgG;AAChG,MAAM,UAAU,WAAW;IACzB,OAAO,WAAW,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE;QACjC,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;QACrC,IAAI,QAAQ,KAAK,SAAS;YAAE,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IACnD,CAAC,EAAE,EAAE,CAAC,CAAA;AACR,CAAC;AAKD,SAAS,cAAc,CAAC,IAAsB;IAC5C,IAAI,IAAI,YAAY,eAAe;QAAE,OAAO,IAAI,CAAA;IAChD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,eAAe,CAAC,IAAI,CAAC,CAAA;IAC9D,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAA;IACjC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,KAAK,MAAM,IAAI,IAAI,KAAK;gBAAE,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;;YACpE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAe,CAAC,CAAA;IACpC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AASD;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IAC1C,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAC5C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACzE,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAC9B,MAAM,eAAe,GAAG,WAAW,CACjC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,QAAQ,GAAG,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACtF,MAAM,EAAE,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC9C,QAAQ,CAAC,QAAQ,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAA;IAC3D,CAAC,EACD,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAC7B,CAAA;IACD,OAAO,CAAC,YAAY,EAAE,eAAe,CAAU,CAAA;AACjD,CAAC;AAED;+FAC+F;AAC/F,SAAS,gBAAgB,CAAC,KAAiB,EAAE,MAA0B;IACrE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACpC,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAClF,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,OAAO,CAAA;AACpE,CAAC;AAUD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAA+B,SAAS,IAAI,CACxE,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EACzC,GAAG;IAEH,MAAM,WAAW,GAAG,CAAC,KAAoC,EAAQ,EAAE;QACjE,OAAO,EAAE,CAAC,KAAK,CAAC,CAAA;QAChB,IAAI,KAAK,CAAC,gBAAgB;YAAE,OAAM,CAAC,0CAA0C;QAC7E,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC;YAAE,OAAM,CAAC,oCAAoC;QACjF,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAA;QACrC,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAM,CAAC,oDAAoD;QACvF,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC,CAAA;IAC7C,CAAC,CAAA;IACD,OAAO,aAAa,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAA;AACrF,CAAC,CAAC,CAAA;AAuBF;kBACkB;AAClB,SAAS,aAAa,CAAC,QAAgB,EAAE,aAAsB;IAC7D,MAAM,KAAK,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAA;IAC/D,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AAC1E,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,UAAU,CAAkC,SAAS,OAAO,CACjF,EAAE,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAC/D,GAAG;IAEH,MAAM,EAAE,QAAQ,EAAE,GAAG,WAAW,EAAE,CAAA;IAClC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IAC1D,MAAM,EAAE,GAAG,aAAa,KAAK,IAAI,CAAA;IACjC,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;IAC3C,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAA;IACxD,MAAM,QAAQ,GAAG,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACnF,kGAAkG;IAClG,uDAAuD;IACvD,MAAM,aAAa,GACjB,OAAO,IAAI,WAAW,KAAK,SAAS;QAClC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;QACpD,CAAC,CAAC,SAAS,CAAA;IACf,MAAM,SAAS,GACb,aAAa,KAAK,SAAS;QAC3B,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAA;IAClF,MAAM,WAAW,GAAuB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAA;IAC/D,MAAM,iBAAiB,GAAG,OAAO,SAAS,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC9F,MAAM,aAAa,GAAG,OAAO,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAC9E,MAAM,gBAAgB,GAAG,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;IAC1F,OAAO,aAAa,CAClB,IAAI,EACJ;QACE,GAAG,IAAI;QACP,EAAE;QACF,GAAG;QACH,GAAG,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,GAAG,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,6FAA6F;QAC7F,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;KAC9C,EACD,gBAAgB,CACjB,CAAA;AACH,CAAC,CAAC,CAAA;AAEF;4GAC4G;AAC5G,SAAS,aAAa,CAAC,OAAe,EAAE,MAAc;IACpD,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,IAAI,CAAA;IACnC,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,CAAC,CAAA;AAChE,CAAC;AAQD;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,EAAiB;IACrD,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAA;IAC9B,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC,CAAA;IAC7C,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3B,OAAO,IAAI,CAAA;AACb,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nifrajs/web-react",
3
- "version": "1.13.0",
3
+ "version": "2.1.0",
4
4
  "description": "React render adapter for @nifrajs/web — SSR + hydration (Bun-native JSX, no Babel).",
5
5
  "keywords": [
6
6
  "bun",
@@ -80,10 +80,10 @@
80
80
  "access": "public"
81
81
  },
82
82
  "dependencies": {
83
- "@nifrajs/core": "1.13.0",
84
- "@nifrajs/i18n": "1.13.0",
85
- "@nifrajs/image": "1.13.0",
86
- "@nifrajs/web": "1.13.0"
83
+ "@nifrajs/core": "2.1.0",
84
+ "@nifrajs/i18n": "2.1.0",
85
+ "@nifrajs/image": "2.1.0",
86
+ "@nifrajs/web": "2.1.0"
87
87
  },
88
88
  "peerDependencies": {
89
89
  "react": "^19.0.0",
package/src/client.ts CHANGED
@@ -34,6 +34,7 @@ export function mountRouter(options: MountRouterOptions): void {
34
34
  data: state.data,
35
35
  actionData: state.actionData,
36
36
  pending: state.pending,
37
+ ...(state.pendingPath !== undefined ? { pendingPath: state.pendingPath } : {}),
37
38
  // `params`/`path` feed the routing hooks (useParams/useLocation) via compose's RouterContext —
38
39
  // sourced from router state here, matching the SSR render's request-derived values on hydration.
39
40
  params: state.params,
package/src/compose.ts CHANGED
@@ -10,10 +10,11 @@ const EMPTY_PARAMS: Readonly<Record<string, string>> = Object.freeze({})
10
10
  * (innermost) receives `props` (the loader data); each layout wraps the child via its
11
11
  * `children`. Shared by the server adapter (renderToString) and the client (hydrateRoot).
12
12
  *
13
- * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params` + the
14
- * current `path` (both threaded through `RenderProps` identically on SSR and client), so the routing
15
- * hooks (`useParams`/`useLocation`/`useSearchParams`) read the same value on both sides — no hydration
16
- * mismatch. A render with no routing fields (a non-router adapter usage) provides the empty default.
13
+ * The whole tree is wrapped in a {@link RouterContext} provider carrying the matched `params`, the
14
+ * current `path`, and the client `pending` flag (all threaded through `RenderProps` identically on SSR
15
+ * and client), so the routing hooks (`useParams`/`useLocation`/`useNavigation`) read the same value on
16
+ * both sides - no hydration mismatch (`pending` is `false` on SSR and on the initial client render).
17
+ * A render with no routing fields (a non-router adapter usage) provides the empty default.
17
18
  */
18
19
  export function compose(chain: readonly unknown[], props: RenderProps): ReactNode {
19
20
  const last = chain.length - 1
@@ -24,7 +25,14 @@ export function compose(chain: readonly unknown[], props: RenderProps): ReactNod
24
25
  }
25
26
  return createElement(
26
27
  RouterContext.Provider,
27
- { value: { params: props.params ?? EMPTY_PARAMS, path: props.path ?? "" } },
28
+ {
29
+ value: {
30
+ params: props.params ?? EMPTY_PARAMS,
31
+ path: props.path ?? "",
32
+ pending: props.pending ?? false,
33
+ pendingPath: props.pendingPath,
34
+ },
35
+ },
28
36
  node,
29
37
  )
30
38
  }
package/src/router.ts CHANGED
@@ -31,6 +31,13 @@ export interface RouterContextValue {
31
31
  readonly params: Readonly<Record<string, string>>
32
32
  /** The current URL's `pathname + search` (no hash — the router never carries one). */
33
33
  readonly path: string
34
+ /** True while a client navigation (or revalidation) is in flight — the current route stays mounted
35
+ * until the new one is ready. Always `false` on SSR (loaders block before render). Drives loading UI
36
+ * via {@link useNavigation}. */
37
+ readonly pending: boolean
38
+ /** The `pathname + search` a navigation is transitioning TO while `pending` (`undefined` when idle or
39
+ * during a same-route revalidation). Powers {@link NavLink}'s per-link `isPending`. */
40
+ readonly pendingPath?: string | undefined
34
41
  }
35
42
 
36
43
  // Frozen empty params so the default context value has a stable reference (no needless re-renders).
@@ -38,7 +45,11 @@ const EMPTY_PARAMS: Readonly<Record<string, string>> = Object.freeze({})
38
45
 
39
46
  /** Router context. The default ({} params, "" path) is what a component sees when rendered outside a
40
47
  * nifra route tree — the hooks stay defined (no throw) so a stray `useParams` degrades gracefully. */
41
- export const RouterContext = createContext<RouterContextValue>({ params: EMPTY_PARAMS, path: "" })
48
+ export const RouterContext = createContext<RouterContextValue>({
49
+ params: EMPTY_PARAMS,
50
+ path: "",
51
+ pending: false,
52
+ })
42
53
 
43
54
  /** Split a `pathname + search` into its parts. `search` keeps its leading `?` (like `location.search`);
44
55
  * an empty query yields `""`. */
@@ -78,6 +89,47 @@ export function useLocation(): Location {
78
89
  }, [path])
79
90
  }
80
91
 
92
+ /** The current navigation state, mirroring the Remix `useNavigation()` shape for familiarity. */
93
+ export interface Navigation {
94
+ /** True while a client navigation (or revalidation) is in flight. The current route stays mounted
95
+ * until the new one is ready, so this drives a loading indicator, not a route swap. */
96
+ readonly pending: boolean
97
+ /** `"loading"` while a navigation is in flight, else `"idle"`. */
98
+ readonly state: "idle" | "loading"
99
+ /** The `pathname + search` being navigated TO while `pending`; `undefined` when idle or during a
100
+ * same-route revalidation. Mirrors Remix's `useNavigation().location`. */
101
+ readonly location: string | undefined
102
+ }
103
+
104
+ /**
105
+ * Observe client navigation to drive loading UI (a top-bar spinner, dimmed content, a skeleton). nifra
106
+ * navigates imperatively - it fetches the next route's chunk + loader data while the current route stays
107
+ * on screen, then swaps - so `pending` is the signal for "a transition is in flight," not a Suspense
108
+ * boundary. Always `{ pending: false, state: "idle" }` on the server (loaders block before render), so
109
+ * it is hydration-safe.
110
+ *
111
+ * ```tsx
112
+ * const { pending } = useNavigation()
113
+ * return <div className={pending ? "opacity-60" : ""}>{pending && <TopBarSpinner />}<Outlet /></div>
114
+ * ```
115
+ */
116
+ export function useNavigation(): Navigation {
117
+ const { pending, pendingPath } = useContext(RouterContext)
118
+ return useMemo(
119
+ () => ({
120
+ pending,
121
+ state: pending ? ("loading" as const) : ("idle" as const),
122
+ location: pending ? pendingPath : undefined,
123
+ }),
124
+ [pending, pendingPath],
125
+ )
126
+ }
127
+
128
+ /** Convenience boolean form of {@link useNavigation}: `true` while a client navigation is in flight. */
129
+ export function usePending(): boolean {
130
+ return useContext(RouterContext).pending
131
+ }
132
+
81
133
  /** A programmatic navigate: a string path (push, or replace via `{ replace: true }`) or a history delta
82
134
  * (`-1`/`1`). A no-op on the server / before hydration (a render-time navigate isn't valid — use
83
135
  * {@link Navigate}, which navigates in an effect). */
@@ -175,7 +227,8 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
175
227
  export interface NavLinkRenderProps {
176
228
  /** True when the current location matches this link's `to` (prefix match, or exact when `end`). */
177
229
  readonly isActive: boolean
178
- /** Reserved for a future pending-navigation signal; currently always `false`. */
230
+ /** True while a client navigation to THIS link's target is in flight (matched like `isActive`).
231
+ * `false` on SSR and when idle. Use it to show a per-link spinner during the transition. */
179
232
  readonly isPending: boolean
180
233
  }
181
234
 
@@ -209,11 +262,21 @@ export const NavLink = forwardRef<HTMLAnchorElement, NavLinkProps>(function NavL
209
262
  ref,
210
263
  ) {
211
264
  const { pathname } = useLocation()
265
+ const { pending, pendingPath } = useContext(RouterContext)
212
266
  const cs = caseSensitive === true
213
267
  const current = normalizePath(pathname, cs)
214
268
  const target = normalizePath(splitPath(to).pathname, cs)
215
269
  const isActive = end === true ? current === target : matchesPrefix(current, target)
216
- const renderProps: NavLinkRenderProps = { isActive, isPending: false }
270
+ // isPending: a navigation is in flight AND it's heading to THIS link's target (same match rule as
271
+ // isActive). `false` on SSR and idle (no pendingPath).
272
+ const pendingTarget =
273
+ pending && pendingPath !== undefined
274
+ ? normalizePath(splitPath(pendingPath).pathname, cs)
275
+ : undefined
276
+ const isPending =
277
+ pendingTarget !== undefined &&
278
+ (end === true ? pendingTarget === target : matchesPrefix(pendingTarget, target))
279
+ const renderProps: NavLinkRenderProps = { isActive, isPending }
217
280
  const resolvedClassName = typeof className === "function" ? className(renderProps) : className
218
281
  const resolvedStyle = typeof style === "function" ? style(renderProps) : style
219
282
  const resolvedChildren = typeof children === "function" ? children(renderProps) : children