toiljs 0.0.15 → 0.0.16
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/.babelrc +13 -13
- package/.gitattributes +2 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
- package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -90
- package/.github/ISSUE_TEMPLATE/config.yml +8 -8
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +43 -43
- package/.github/changelog-config.json +45 -45
- package/.github/dependabot.yml +27 -27
- package/.github/workflows/ci.yml +191 -191
- package/.prettierrc.json +11 -11
- package/.vscode/settings.json +9 -9
- package/CHANGELOG.md +5 -5
- package/LICENSE +187 -187
- package/README.md +339 -315
- package/as-pect.asconfig.json +34 -34
- package/as-pect.config.js +65 -65
- package/assets/logo.svg +36 -36
- package/build/backend/.tsbuildinfo +1 -1
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +0 -0
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/dev/devtools.d.ts +6 -0
- package/build/client/dev/devtools.js +442 -0
- package/build/client/dev/error-overlay.d.ts +9 -0
- package/build/client/dev/error-overlay.js +19 -4
- package/build/client/navigation/prefetch.d.ts +1 -0
- package/build/client/navigation/prefetch.js +35 -0
- package/build/client/routing/Router.js +1 -1
- package/build/client/routing/hooks.js +6 -2
- package/build/client/routing/loader.d.ts +23 -0
- package/build/client/routing/loader.js +53 -7
- package/build/client/routing/mount.js +4 -3
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/config.d.ts +16 -0
- package/build/compiler/config.js +7 -0
- package/build/compiler/docs.js +16 -16
- package/build/compiler/index.d.ts +2 -2
- package/build/compiler/index.js +1 -1
- package/build/compiler/plugin.js +156 -0
- package/build/compiler/prerender.d.ts +1 -0
- package/build/compiler/prerender.js +1 -1
- package/build/compiler/seo.d.ts +1 -1
- package/build/compiler/seo.js +5 -4
- package/build/compiler/ssg.js +32 -1
- package/build/io/.tsbuildinfo +1 -1
- package/build/logger/.tsbuildinfo +1 -1
- package/build/shared/.tsbuildinfo +1 -1
- package/eslint.config.js +48 -48
- package/examples/basic/client/404.tsx +11 -11
- package/examples/basic/client/components/.gitkeep +1 -1
- package/examples/basic/client/global-error.tsx +13 -13
- package/examples/basic/client/layout.tsx +25 -25
- package/examples/basic/client/public/images/.gitkeep +1 -1
- package/examples/basic/client/public/images/logo.svg +36 -36
- package/examples/basic/client/public/robots.txt +2 -2
- package/examples/basic/client/routes/docs/[...slug].tsx +12 -12
- package/examples/basic/client/routes/features/error/error.tsx +16 -16
- package/examples/basic/client/routes/features/template/b.tsx +14 -14
- package/examples/basic/client/routes/files/[[...slug]].tsx +21 -21
- package/examples/basic/client/routes/gallery/layout.tsx +13 -13
- package/examples/basic/client/routes/io.tsx +24 -24
- package/examples/basic/client/routes/loader-demo/loading.tsx +13 -13
- package/examples/basic/client/routes/search.tsx +61 -61
- package/examples/basic/client/toil.tsx +5 -5
- package/package.json +155 -148
- package/presets/eslint.js +88 -88
- package/presets/no-uint8array-tostring.js +200 -200
- package/presets/prettier.json +18 -18
- package/presets/tsconfig.json +37 -37
- package/src/backend/index.ts +160 -160
- package/src/cli/proc.ts +50 -50
- package/src/cli/updates.ts +69 -69
- package/src/cli/validate.ts +31 -31
- package/src/client/channel/channel.ts +146 -146
- package/src/client/components/Form.tsx +65 -65
- package/src/client/components/Script.tsx +113 -113
- package/src/client/components/Slot.tsx +21 -21
- package/src/client/dev/devtools.tsx +973 -0
- package/src/client/dev/error-overlay.tsx +30 -4
- package/src/client/head/head.ts +167 -167
- package/src/client/head/metadata.ts +112 -112
- package/src/client/index.ts +89 -89
- package/src/client/navigation/NavLink.tsx +86 -86
- package/src/client/navigation/navigation.ts +235 -235
- package/src/client/navigation/prefetch.ts +169 -130
- package/src/client/navigation/scroll.ts +53 -53
- package/src/client/routing/Router.tsx +8 -2
- package/src/client/routing/action.ts +122 -122
- package/src/client/routing/error-boundary.tsx +43 -43
- package/src/client/routing/hooks.ts +21 -6
- package/src/client/routing/loader.ts +325 -235
- package/src/client/routing/match.ts +47 -47
- package/src/client/routing/mount.tsx +54 -52
- package/src/client/routing/params-context.ts +10 -10
- package/src/client/routing/slot-context.ts +7 -7
- package/src/client/search/search.ts +189 -189
- package/src/client/search/use-page-search.ts +73 -73
- package/src/client/types.ts +73 -73
- package/src/compiler/config.ts +219 -182
- package/src/compiler/docs.ts +228 -228
- package/src/compiler/generate.ts +394 -394
- package/src/compiler/index.ts +64 -57
- package/src/compiler/pages.ts +70 -70
- package/src/compiler/plugin.ts +170 -2
- package/src/compiler/prerender.ts +156 -156
- package/src/compiler/seo.ts +397 -390
- package/src/compiler/ssg.ts +162 -126
- package/src/io/BinaryReader.ts +340 -340
- package/src/io/BinaryWriter.ts +385 -385
- package/src/io/FastMap.ts +127 -127
- package/src/io/index.ts +11 -11
- package/src/io/lengths.ts +14 -14
- package/src/io/types.ts +18 -18
- package/src/logger/index.ts +22 -22
- package/src/server/index.ts +10 -10
- package/src/server/main.ts +13 -13
- package/src/server/tsconfig.json +4 -4
- package/src/shared/index.ts +10 -10
- package/std/client/index.d.ts +15 -15
- package/std/client/package.json +3 -3
- package/test/assembly/example.spec.ts +7 -7
- package/test/channel.test.ts +21 -21
- package/test/dom/Link.test.tsx +47 -47
- package/test/dom/NavLink.test.tsx +37 -37
- package/test/dom/error-overlay.test.tsx +44 -44
- package/test/dom/loader.test.tsx +121 -121
- package/test/dom/navigation.test.ts +59 -59
- package/test/dom/revalidate.test.tsx +38 -38
- package/test/dom/route-head.test.tsx +78 -78
- package/test/dom/router-loading.test.tsx +44 -44
- package/test/dom/scroll.test.ts +56 -56
- package/test/dom/use-metadata.test.tsx +58 -58
- package/test/io.test.ts +93 -93
- package/test/navlink.test.ts +28 -28
- package/test/placeholder.test.ts +9 -9
- package/test/routes.test.ts +76 -76
- package/test/seo.test.ts +175 -164
- package/test/slot-layouts.test.ts +69 -69
- package/test/ssg.test.ts +36 -36
- package/test/update.test.ts +44 -44
- package/test/validate.test.ts +42 -42
- package/toil-routes.d.ts +7 -0
- package/toilconfig.json +30 -30
- package/tsconfig.backend.json +13 -13
- package/tsconfig.base.json +35 -35
- package/tsconfig.cli.json +13 -13
- package/tsconfig.client.json +14 -14
- package/tsconfig.compiler.json +13 -13
- package/tsconfig.io.json +12 -12
- package/tsconfig.json +22 -22
- package/tsconfig.logger.json +12 -12
- package/tsconfig.server.json +10 -10
- package/tsconfig.shared.json +12 -12
- package/vitest.config.ts +26 -26
- package/.idea/codeStyles/Project.xml +0 -54
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -7
- package/.idea/toiljs.iml +0 -8
- package/.idea/vcs.xml +0 -6
- package/.toil/entry.tsx +0 -9
- package/.toil/index.html +0 -12
- package/.toil/routes.ts +0 -9
- package/build/cli/configure.d.ts +0 -16
- package/build/cli/configure.js +0 -272
- package/build/cli/create.d.ts +0 -16
- package/build/cli/create.js +0 -420
- package/build/cli/diagnostics.d.ts +0 -55
- package/build/cli/diagnostics.js +0 -333
- package/build/cli/doctor.d.ts +0 -6
- package/build/cli/doctor.js +0 -249
- package/build/cli/features.d.ts +0 -25
- package/build/cli/features.js +0 -107
- package/build/cli/index.d.ts +0 -2
- package/build/cli/proc.d.ts +0 -6
- package/build/cli/proc.js +0 -31
- package/build/cli/ui.d.ts +0 -9
- package/build/cli/ui.js +0 -75
- package/build/cli/update.d.ts +0 -7
- package/build/cli/update.js +0 -117
- package/build/cli/updates.d.ts +0 -10
- package/build/cli/updates.js +0 -45
- package/build/cli/validate.d.ts +0 -4
- package/build/cli/validate.js +0 -19
- package/build/client/Link.d.ts +0 -8
- package/build/client/Link.js +0 -44
- package/build/client/NavLink.d.ts +0 -14
- package/build/client/NavLink.js +0 -37
- package/build/client/Router.d.ts +0 -7
- package/build/client/Router.js +0 -55
- package/build/client/channel.d.ts +0 -23
- package/build/client/channel.js +0 -94
- package/build/client/error-boundary.d.ts +0 -16
- package/build/client/error-boundary.js +0 -19
- package/build/client/head.d.ts +0 -26
- package/build/client/head.js +0 -87
- package/build/client/hooks.d.ts +0 -17
- package/build/client/hooks.js +0 -48
- package/build/client/lazy.d.ts +0 -16
- package/build/client/lazy.js +0 -53
- package/build/client/match.d.ts +0 -2
- package/build/client/match.js +0 -32
- package/build/client/mount.d.ts +0 -2
- package/build/client/mount.js +0 -13
- package/build/client/navigation.d.ts +0 -13
- package/build/client/navigation.js +0 -97
- package/build/client/params-context.d.ts +0 -2
- package/build/client/params-context.js +0 -2
- package/build/client/prefetch.d.ts +0 -11
- package/build/client/prefetch.js +0 -100
- package/build/client/runtime.d.ts +0 -31
- package/build/client/runtime.js +0 -112
- package/build/client/scroll.d.ts +0 -8
- package/build/client/scroll.js +0 -36
- package/toil-env.d.ts +0 -16
package/src/client/index.ts
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* toiljs client runtime, published as `toiljs/client`. Re-exports the router (mount/Router/Link),
|
|
3
|
-
* navigation hooks, prefetching, and the route types consumed by the compiler-generated entry.
|
|
4
|
-
* Zero imports needed in user route files beyond this package.
|
|
5
|
-
*
|
|
6
|
-
* Internals are split by concern: route `types`, history-based `navigation`, the params
|
|
7
|
-
* `params-context` + `hooks`, `lazy` component resolution, the `Link`/`Router` components,
|
|
8
|
-
* `mount`, `match` (pure matcher), `prefetch` (link prefetcher), and `channel` (WebSocket helper).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export { mount } from './routing/mount.js';
|
|
12
|
-
export { Router } from './routing/Router.js';
|
|
13
|
-
export { Link } from './navigation/Link.js';
|
|
14
|
-
export type { LinkProps } from './navigation/Link.js';
|
|
15
|
-
export { NavLink, matchActive } from './navigation/NavLink.js';
|
|
16
|
-
export type { NavLinkProps, NavLinkState } from './navigation/NavLink.js';
|
|
17
|
-
export {
|
|
18
|
-
navigate,
|
|
19
|
-
back,
|
|
20
|
-
forward,
|
|
21
|
-
refresh,
|
|
22
|
-
setViewTransitions,
|
|
23
|
-
setTransitions,
|
|
24
|
-
href,
|
|
25
|
-
} from './navigation/navigation.js';
|
|
26
|
-
export type { NavigateOptions } from './navigation/navigation.js';
|
|
27
|
-
export {
|
|
28
|
-
useParams,
|
|
29
|
-
useNavigate,
|
|
30
|
-
useLocation,
|
|
31
|
-
usePathname,
|
|
32
|
-
useSearchParams,
|
|
33
|
-
useRouter,
|
|
34
|
-
useNavigationPending,
|
|
35
|
-
} from './routing/hooks.js';
|
|
36
|
-
export type { RouterInstance } from './routing/hooks.js';
|
|
37
|
-
export { useLoaderData, revalidate, invalidateLoaderData } from './routing/loader.js';
|
|
38
|
-
export type {
|
|
39
|
-
LoaderArgs,
|
|
40
|
-
LoaderFunction,
|
|
41
|
-
LoaderData,
|
|
42
|
-
Revalidate,
|
|
43
|
-
StaticParams,
|
|
44
|
-
GenerateStaticParams,
|
|
45
|
-
} from './routing/loader.js';
|
|
46
|
-
export { useAction } from './routing/action.js';
|
|
47
|
-
export type {
|
|
48
|
-
UseActionOptions,
|
|
49
|
-
ActionState,
|
|
50
|
-
ActionHandle,
|
|
51
|
-
RevalidateTarget,
|
|
52
|
-
} from './routing/action.js';
|
|
53
|
-
export { prefetch } from './navigation/prefetch.js';
|
|
54
|
-
export type {
|
|
55
|
-
RouteDef,
|
|
56
|
-
LayoutLoader,
|
|
57
|
-
LayoutComponentLoader,
|
|
58
|
-
NotFoundLoader,
|
|
59
|
-
RouteErrorProps,
|
|
60
|
-
Register,
|
|
61
|
-
RoutePath,
|
|
62
|
-
Href,
|
|
63
|
-
} from './types.js';
|
|
64
|
-
export { matchRoute } from './routing/match.js';
|
|
65
|
-
export type { RouteParams } from './routing/match.js';
|
|
66
|
-
export { connectChannel, useChannel, resolveChannelUrl } from './channel/channel.js';
|
|
67
|
-
export type { Channel, ChannelOptions, ChannelHook, ChannelData } from './channel/channel.js';
|
|
68
|
-
export { useHead, useTitle, Head, mergeHead } from './head/head.js';
|
|
69
|
-
export type { HeadSpec, MetaTag, LinkTag, ResolvedHead } from './head/head.js';
|
|
70
|
-
export { resolveMetadata, useMetadata, Metadata } from './head/metadata.js';
|
|
71
|
-
export type { GenerateMetadata, GenerateMetadataArgs, OpenGraph } from './head/metadata.js';
|
|
72
|
-
export { searchPages, registerPages, getPages, pagePath } from './search/search.js';
|
|
73
|
-
export type {
|
|
74
|
-
PageMeta,
|
|
75
|
-
PageSearchResult,
|
|
76
|
-
PageSearchOptions,
|
|
77
|
-
SearchField,
|
|
78
|
-
SearchHints,
|
|
79
|
-
} from './search/search.js';
|
|
80
|
-
export { usePageSearch } from './search/use-page-search.js';
|
|
81
|
-
export type { PageSearch } from './search/use-page-search.js';
|
|
82
|
-
export { Image } from './components/Image.js';
|
|
83
|
-
export type { ImageProps } from './components/Image.js';
|
|
84
|
-
export { Script } from './components/Script.js';
|
|
85
|
-
export type { ScriptProps, ScriptStrategy } from './components/Script.js';
|
|
86
|
-
export { Form } from './components/Form.js';
|
|
87
|
-
export type { FormProps } from './components/Form.js';
|
|
88
|
-
export { Slot } from './components/Slot.js';
|
|
89
|
-
export type { SlotProps } from './components/Slot.js';
|
|
1
|
+
/**
|
|
2
|
+
* toiljs client runtime, published as `toiljs/client`. Re-exports the router (mount/Router/Link),
|
|
3
|
+
* navigation hooks, prefetching, and the route types consumed by the compiler-generated entry.
|
|
4
|
+
* Zero imports needed in user route files beyond this package.
|
|
5
|
+
*
|
|
6
|
+
* Internals are split by concern: route `types`, history-based `navigation`, the params
|
|
7
|
+
* `params-context` + `hooks`, `lazy` component resolution, the `Link`/`Router` components,
|
|
8
|
+
* `mount`, `match` (pure matcher), `prefetch` (link prefetcher), and `channel` (WebSocket helper).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export { mount } from './routing/mount.js';
|
|
12
|
+
export { Router } from './routing/Router.js';
|
|
13
|
+
export { Link } from './navigation/Link.js';
|
|
14
|
+
export type { LinkProps } from './navigation/Link.js';
|
|
15
|
+
export { NavLink, matchActive } from './navigation/NavLink.js';
|
|
16
|
+
export type { NavLinkProps, NavLinkState } from './navigation/NavLink.js';
|
|
17
|
+
export {
|
|
18
|
+
navigate,
|
|
19
|
+
back,
|
|
20
|
+
forward,
|
|
21
|
+
refresh,
|
|
22
|
+
setViewTransitions,
|
|
23
|
+
setTransitions,
|
|
24
|
+
href,
|
|
25
|
+
} from './navigation/navigation.js';
|
|
26
|
+
export type { NavigateOptions } from './navigation/navigation.js';
|
|
27
|
+
export {
|
|
28
|
+
useParams,
|
|
29
|
+
useNavigate,
|
|
30
|
+
useLocation,
|
|
31
|
+
usePathname,
|
|
32
|
+
useSearchParams,
|
|
33
|
+
useRouter,
|
|
34
|
+
useNavigationPending,
|
|
35
|
+
} from './routing/hooks.js';
|
|
36
|
+
export type { RouterInstance } from './routing/hooks.js';
|
|
37
|
+
export { useLoaderData, revalidate, invalidateLoaderData } from './routing/loader.js';
|
|
38
|
+
export type {
|
|
39
|
+
LoaderArgs,
|
|
40
|
+
LoaderFunction,
|
|
41
|
+
LoaderData,
|
|
42
|
+
Revalidate,
|
|
43
|
+
StaticParams,
|
|
44
|
+
GenerateStaticParams,
|
|
45
|
+
} from './routing/loader.js';
|
|
46
|
+
export { useAction } from './routing/action.js';
|
|
47
|
+
export type {
|
|
48
|
+
UseActionOptions,
|
|
49
|
+
ActionState,
|
|
50
|
+
ActionHandle,
|
|
51
|
+
RevalidateTarget,
|
|
52
|
+
} from './routing/action.js';
|
|
53
|
+
export { prefetch } from './navigation/prefetch.js';
|
|
54
|
+
export type {
|
|
55
|
+
RouteDef,
|
|
56
|
+
LayoutLoader,
|
|
57
|
+
LayoutComponentLoader,
|
|
58
|
+
NotFoundLoader,
|
|
59
|
+
RouteErrorProps,
|
|
60
|
+
Register,
|
|
61
|
+
RoutePath,
|
|
62
|
+
Href,
|
|
63
|
+
} from './types.js';
|
|
64
|
+
export { matchRoute } from './routing/match.js';
|
|
65
|
+
export type { RouteParams } from './routing/match.js';
|
|
66
|
+
export { connectChannel, useChannel, resolveChannelUrl } from './channel/channel.js';
|
|
67
|
+
export type { Channel, ChannelOptions, ChannelHook, ChannelData } from './channel/channel.js';
|
|
68
|
+
export { useHead, useTitle, Head, mergeHead } from './head/head.js';
|
|
69
|
+
export type { HeadSpec, MetaTag, LinkTag, ResolvedHead } from './head/head.js';
|
|
70
|
+
export { resolveMetadata, useMetadata, Metadata } from './head/metadata.js';
|
|
71
|
+
export type { GenerateMetadata, GenerateMetadataArgs, OpenGraph } from './head/metadata.js';
|
|
72
|
+
export { searchPages, registerPages, getPages, pagePath } from './search/search.js';
|
|
73
|
+
export type {
|
|
74
|
+
PageMeta,
|
|
75
|
+
PageSearchResult,
|
|
76
|
+
PageSearchOptions,
|
|
77
|
+
SearchField,
|
|
78
|
+
SearchHints,
|
|
79
|
+
} from './search/search.js';
|
|
80
|
+
export { usePageSearch } from './search/use-page-search.js';
|
|
81
|
+
export type { PageSearch } from './search/use-page-search.js';
|
|
82
|
+
export { Image } from './components/Image.js';
|
|
83
|
+
export type { ImageProps } from './components/Image.js';
|
|
84
|
+
export { Script } from './components/Script.js';
|
|
85
|
+
export type { ScriptProps, ScriptStrategy } from './components/Script.js';
|
|
86
|
+
export { Form } from './components/Form.js';
|
|
87
|
+
export type { FormProps } from './components/Form.js';
|
|
88
|
+
export { Slot } from './components/Slot.js';
|
|
89
|
+
export type { SlotProps } from './components/Slot.js';
|
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
-
|
|
3
|
-
import { useLocation } from '../routing/hooks.js';
|
|
4
|
-
import { Link, type LinkProps } from './Link.js';
|
|
5
|
-
|
|
6
|
-
/** State passed to `NavLink`'s function-form `className` / `style` / `children`. */
|
|
7
|
-
export interface NavLinkState {
|
|
8
|
-
readonly isActive: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Props for {@link NavLink}: all {@link LinkProps}, but `className` / `style` / `children` may also
|
|
13
|
-
* be functions of the active state.
|
|
14
|
-
*/
|
|
15
|
-
export interface NavLinkProps extends Omit<LinkProps, 'className' | 'style' | 'children'> {
|
|
16
|
-
className?: string | ((state: NavLinkState) => string | undefined);
|
|
17
|
-
style?: CSSProperties | ((state: NavLinkState) => CSSProperties | undefined);
|
|
18
|
-
children?: ReactNode | ((state: NavLinkState) => ReactNode);
|
|
19
|
-
/** Match `href` exactly; without it, sub-paths are also active. Default `false`. */
|
|
20
|
-
end?: boolean;
|
|
21
|
-
/** Class added when active (used with a string `className`). Default `"active"`. */
|
|
22
|
-
activeClassName?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function normalizePath(p: string): string {
|
|
26
|
-
return p.length > 1 ? p.replace(/\/+$/, '') : p;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Whether a link to `linkPath` is active for `currentPath`. Exact when `end`; otherwise a parent
|
|
31
|
-
* path is active for its sub-paths (and `/` is active everywhere, matching React Router).
|
|
32
|
-
*/
|
|
33
|
-
export function matchActive(linkPath: string, currentPath: string, end: boolean): boolean {
|
|
34
|
-
const link = normalizePath(linkPath);
|
|
35
|
-
const current = normalizePath(currentPath);
|
|
36
|
-
if (current === link) return true;
|
|
37
|
-
if (end) return false;
|
|
38
|
-
if (link === '/') return true;
|
|
39
|
-
return current.startsWith(link + '/');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* A {@link Link} that knows whether it points at the current location. Applies an active class
|
|
44
|
-
* (default `"active"`) and `aria-current="page"` when active; `className` / `style` / `children`
|
|
45
|
-
* may be functions of `{ isActive }`. Inherits Link's full anchor API and prefetching.
|
|
46
|
-
*/
|
|
47
|
-
export function NavLink(props: NavLinkProps): ReactNode {
|
|
48
|
-
const {
|
|
49
|
-
href,
|
|
50
|
-
className,
|
|
51
|
-
style,
|
|
52
|
-
children,
|
|
53
|
-
end = false,
|
|
54
|
-
activeClassName = 'active',
|
|
55
|
-
...rest
|
|
56
|
-
} = props;
|
|
57
|
-
const pathname = useLocation();
|
|
58
|
-
|
|
59
|
-
let linkPath = href;
|
|
60
|
-
try {
|
|
61
|
-
linkPath = new URL(href, window.location.href).pathname;
|
|
62
|
-
} catch {
|
|
63
|
-
linkPath = href;
|
|
64
|
-
}
|
|
65
|
-
const isActive = matchActive(linkPath, pathname, end);
|
|
66
|
-
const state: NavLinkState = { isActive };
|
|
67
|
-
|
|
68
|
-
const resolvedClassName =
|
|
69
|
-
typeof className === 'function'
|
|
70
|
-
? className(state)
|
|
71
|
-
: [className, isActive ? activeClassName : undefined].filter(Boolean).join(' ') ||
|
|
72
|
-
undefined;
|
|
73
|
-
const resolvedStyle = typeof style === 'function' ? style(state) : style;
|
|
74
|
-
const resolvedChildren = typeof children === 'function' ? children(state) : children;
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<Link
|
|
78
|
-
{...rest}
|
|
79
|
-
href={href}
|
|
80
|
-
className={resolvedClassName}
|
|
81
|
-
style={resolvedStyle}
|
|
82
|
-
aria-current={isActive ? 'page' : undefined}>
|
|
83
|
-
{resolvedChildren}
|
|
84
|
-
</Link>
|
|
85
|
-
);
|
|
86
|
-
}
|
|
1
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useLocation } from '../routing/hooks.js';
|
|
4
|
+
import { Link, type LinkProps } from './Link.js';
|
|
5
|
+
|
|
6
|
+
/** State passed to `NavLink`'s function-form `className` / `style` / `children`. */
|
|
7
|
+
export interface NavLinkState {
|
|
8
|
+
readonly isActive: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Props for {@link NavLink}: all {@link LinkProps}, but `className` / `style` / `children` may also
|
|
13
|
+
* be functions of the active state.
|
|
14
|
+
*/
|
|
15
|
+
export interface NavLinkProps extends Omit<LinkProps, 'className' | 'style' | 'children'> {
|
|
16
|
+
className?: string | ((state: NavLinkState) => string | undefined);
|
|
17
|
+
style?: CSSProperties | ((state: NavLinkState) => CSSProperties | undefined);
|
|
18
|
+
children?: ReactNode | ((state: NavLinkState) => ReactNode);
|
|
19
|
+
/** Match `href` exactly; without it, sub-paths are also active. Default `false`. */
|
|
20
|
+
end?: boolean;
|
|
21
|
+
/** Class added when active (used with a string `className`). Default `"active"`. */
|
|
22
|
+
activeClassName?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizePath(p: string): string {
|
|
26
|
+
return p.length > 1 ? p.replace(/\/+$/, '') : p;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Whether a link to `linkPath` is active for `currentPath`. Exact when `end`; otherwise a parent
|
|
31
|
+
* path is active for its sub-paths (and `/` is active everywhere, matching React Router).
|
|
32
|
+
*/
|
|
33
|
+
export function matchActive(linkPath: string, currentPath: string, end: boolean): boolean {
|
|
34
|
+
const link = normalizePath(linkPath);
|
|
35
|
+
const current = normalizePath(currentPath);
|
|
36
|
+
if (current === link) return true;
|
|
37
|
+
if (end) return false;
|
|
38
|
+
if (link === '/') return true;
|
|
39
|
+
return current.startsWith(link + '/');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A {@link Link} that knows whether it points at the current location. Applies an active class
|
|
44
|
+
* (default `"active"`) and `aria-current="page"` when active; `className` / `style` / `children`
|
|
45
|
+
* may be functions of `{ isActive }`. Inherits Link's full anchor API and prefetching.
|
|
46
|
+
*/
|
|
47
|
+
export function NavLink(props: NavLinkProps): ReactNode {
|
|
48
|
+
const {
|
|
49
|
+
href,
|
|
50
|
+
className,
|
|
51
|
+
style,
|
|
52
|
+
children,
|
|
53
|
+
end = false,
|
|
54
|
+
activeClassName = 'active',
|
|
55
|
+
...rest
|
|
56
|
+
} = props;
|
|
57
|
+
const pathname = useLocation();
|
|
58
|
+
|
|
59
|
+
let linkPath = href;
|
|
60
|
+
try {
|
|
61
|
+
linkPath = new URL(href, window.location.href).pathname;
|
|
62
|
+
} catch {
|
|
63
|
+
linkPath = href;
|
|
64
|
+
}
|
|
65
|
+
const isActive = matchActive(linkPath, pathname, end);
|
|
66
|
+
const state: NavLinkState = { isActive };
|
|
67
|
+
|
|
68
|
+
const resolvedClassName =
|
|
69
|
+
typeof className === 'function'
|
|
70
|
+
? className(state)
|
|
71
|
+
: [className, isActive ? activeClassName : undefined].filter(Boolean).join(' ') ||
|
|
72
|
+
undefined;
|
|
73
|
+
const resolvedStyle = typeof style === 'function' ? style(state) : style;
|
|
74
|
+
const resolvedChildren = typeof children === 'function' ? children(state) : children;
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Link
|
|
78
|
+
{...rest}
|
|
79
|
+
href={href}
|
|
80
|
+
className={resolvedClassName}
|
|
81
|
+
style={resolvedStyle}
|
|
82
|
+
aria-current={isActive ? 'page' : undefined}>
|
|
83
|
+
{resolvedChildren}
|
|
84
|
+
</Link>
|
|
85
|
+
);
|
|
86
|
+
}
|