@solidjs/router 0.4.3 → 0.5.1

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/README.md CHANGED
@@ -28,6 +28,7 @@ It supports all of Solid's SSR methods and has Solid's transitions baked in, so
28
28
  - [useRouteData](#useroutedata)
29
29
  - [useMatch](#usematch)
30
30
  - [useRoutes](#useroutes)
31
+ - [useBeforeLeave](#usebeforeleave)
31
32
 
32
33
  ## Getting Started
33
34
 
@@ -98,7 +99,7 @@ export default function App() {
98
99
 
99
100
  3. Lazy-load route components
100
101
 
101
- This way, the `Users` and `Home` components will only be loaded if you're navigating to `/users` or `/home`, respectively.
102
+ This way, the `Users` and `Home` components will only be loaded if you're navigating to `/users` or `/`, respectively.
102
103
 
103
104
  ```jsx
104
105
  import { lazy } from "solid-js";
@@ -120,11 +121,11 @@ export default function App() {
120
121
 
121
122
  ## Create Links to Your Routes
122
123
 
123
- Use the `Link` component to create an anchor tag that takes you to a route:
124
+ Use the `A` component to create an anchor tag that takes you to a route:
124
125
 
125
126
  ```jsx
126
127
  import { lazy } from "solid-js";
127
- import { Routes, Route, Link } from "@solidjs/router"
128
+ import { Routes, Route, A } from "@solidjs/router"
128
129
  const Users = lazy(() => import("./pages/Users"));
129
130
  const Home = lazy(() => import("./pages/Home"));
130
131
 
@@ -132,8 +133,8 @@ export default function App() {
132
133
  return <>
133
134
  <h1>My Site with Lots of Pages</h1>
134
135
  <nav>
135
- <Link href="/about">About</Link>
136
- <Link href="/">Home</Link>
136
+ <A href="/about">About</A>
137
+ <A href="/">Home</A>
137
138
  </nav>
138
139
  <Routes>
139
140
  <Route path="/users" component={Users} />
@@ -144,33 +145,21 @@ export default function App() {
144
145
  }
145
146
  ```
146
147
 
147
- If you use `NavLink` instead of `Link`, the anchor tag will have an `active` class if its href matches the current location, and `inactive` otherwise. **Note:** By default matching includes locations that are descendents (eg. href `/users` matches locations `/users` and `/users/123`), use the boolean `end` prop to prevent matching these. This is particularly useful for links to the root route `/` which would match everything.
148
+ The `<A>` tag also has an `active` class if its href matches the current location, and `inactive` otherwise. **Note:** By default matching includes locations that are descendents (eg. href `/users` matches locations `/users` and `/users/123`), use the boolean `end` prop to prevent matching these. This is particularly useful for links to the root route `/` which would match everything.
148
149
 
149
- Both of these components have these props:
150
150
 
151
151
  | prop | type | description |
152
152
  |----------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
153
153
  | href | string | The path of the route to navigate to. This will be resolved relative to the route that the link is in, but you can preface it with `/` to refer back to the root. |
154
154
  | noScroll | boolean | If true, turn off the default behavior of scrolling to the top of the new page |
155
155
  | replace | boolean | If true, don't add a new entry to the browser history. (By default, the new page will be added to the browser history, so pressing the back button will take you to the previous route.) |
156
- | state | unknown | [Push this value](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) to the history stack when navigating |
157
-
158
-
159
- `NavLink` additionally has:
160
-
161
- | prop | type | description |
162
- |----------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
156
+ | state | unknown | [Push this value](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState) to the history stack when navigating | |
163
157
  | inactiveClass | string | The class to show when the link is inactive (when the current location doesn't match the link) |
164
158
  | activeClass | string | The class to show when the link is active |
165
159
  | end | boolean | If `true`, only considers the link to be active when the curent location matches the `href` exactly; if `false`, check if the current location _starts with_ `href` |
166
160
 
167
-
168
-
169
-
170
- If you have a same-domain path that you want to link to _without_ going through the router, set `rel="external"` on the link component.
171
-
172
161
  ### The Navigate Component
173
- Solid Router provides a `Navigate` component that works similarly to `Link` and `NavLink`, but it will _immediately_ navigate to the provided path as soon as the component is rendered. It also uses the `href` prop, but you have the additional option of passing a function to `href` that returns a path to navigate to:
162
+ Solid Router provides a `Navigate` component that works similarly to `A`, but it will _immediately_ navigate to the provided path as soon as the component is rendered. It also uses the `href` prop, but you have the additional option of passing a function to `href` that returns a path to navigate to:
174
163
 
175
164
  ```jsx
176
165
  function getPath ({navigate, location}) {
@@ -180,9 +169,7 @@ function getPath ({navigate, location}) {
180
169
  }
181
170
 
182
171
  //Navigating to /redirect will redirect you to the result of getPath
183
- <Route path="/redirect">
184
- <Navigate href={getPath}/>
185
- </Route>
172
+ <Route path="/redirect" element={<Navigate href={getPath}/>}/>
186
173
  ```
187
174
 
188
175
  ## Dynamic Routes
@@ -198,7 +185,7 @@ const Home = lazy(() => import("./pages/Home"));
198
185
 
199
186
  export default function App() {
200
187
  return <>
201
- <h1>My Site with Lots of Pages<h1/>
188
+ <h1>My Site with Lots of Pages</h1>
202
189
  <Routes>
203
190
  <Route path="/users" component={Users} />
204
191
  <Route path="/users/:id" component={User} />
@@ -224,7 +211,7 @@ export default function User () {
224
211
 
225
212
  const [userData] = createResource(() => params.id, fetchUser);
226
213
 
227
- return <a href={userData.twitter}>{userData.name}</a>
214
+ return <A href={userData.twitter}>{userData.name}</A>
228
215
  }
229
216
  ```
230
217
 
@@ -273,9 +260,9 @@ To do this, create a function that fetches and returns the data using `createRes
273
260
  ```js
274
261
  import { lazy } from "solid-js";
275
262
  import { Route } from "@solidjs/router";
276
- import { fetchUser } ...
263
+ import { fetchUser } ...
277
264
 
278
- const User = lazy(() => import("/pages/users/[id].js"));
265
+ const User = lazy(() => import("./pages/users/[id].js"));
279
266
 
280
267
  //Data function
281
268
  function UserData({params, location, navigate, data}) {
@@ -361,7 +348,7 @@ function PageWrapper () {
361
348
  return <div>
362
349
  <h1> We love our users! </h1>
363
350
  <Outlet/>
364
- <Link href="/">Back Home</Link>
351
+ <A href="/">Back Home</A>
365
352
  </div>
366
353
  }
367
354
 
@@ -401,7 +388,7 @@ You don't have to use JSX to set up your routes; you can pass an object directly
401
388
  ```jsx
402
389
  import { lazy } from "solid-js";
403
390
  import { render } from "solid-js/web";
404
- import { Router, useRoutes, Link } from "@solidjs/router";
391
+ import { Router, useRoutes, A } from "@solidjs/router";
405
392
 
406
393
  const routes = [
407
394
  {
@@ -432,12 +419,12 @@ function App() {
432
419
  return (
433
420
  <>
434
421
  <h1>Awesome Site</h1>
435
- <Link class="nav" href="/">
422
+ <A class="nav" href="/">
436
423
  Home
437
- </Link>
438
- <Link class="nav" href="/users">
424
+ </A>
425
+ <A class="nav" href="/users">
439
426
  Users
440
- </Link>
427
+ </A>
441
428
  <Routes />
442
429
  </>
443
430
  );
@@ -552,3 +539,32 @@ return <div classList={{ active: Boolean(match()) }} />;
552
539
  ### useRoutes
553
540
 
554
541
  Used to define routes via a config object instead of JSX. See [Config Based Routing](#config-based-routing).
542
+
543
+ ### useBeforeLeave
544
+
545
+ `useBeforeLeave` takes a function that will be called prior to leaving a route. The function will be called with:
546
+
547
+ - from (_Location_): current location (before change).
548
+ - to (_string | number_}: path passed to `navigate`.
549
+ - options (_NavigateOptions_}: options passed to `navigate`.
550
+ - preventDefault (_void function_): call to block the route change.
551
+ - defaultPrevented (_readonly boolean_): true if any previously called leave handlers called preventDefault().
552
+ - retry (_void function_, _force?: boolean_ ): call to retry the same navigation, perhaps after confirming with the user. Pass `true` to skip running the leave handlers again (ie force navigate without confirming).
553
+
554
+ Example usage:
555
+ ```js
556
+ useBeforeLeave((e: BeforeLeaveEventArgs) => {
557
+ if (form.isDirty && !e.defaultPrevented) {
558
+ // preventDefault to block immediately and prompt user async
559
+ e.preventDefault();
560
+ setTimeout(() => {
561
+ if (window.confirm("Discard unsaved changes - are you sure?")) {
562
+ // user wants to proceed anyway so retry with force=true
563
+ e.retry(true);
564
+ }
565
+ }, 100);
566
+ }
567
+ });
568
+ ```
569
+
570
+
@@ -6,6 +6,7 @@ declare module "solid-js" {
6
6
  state?: string;
7
7
  noScroll?: boolean;
8
8
  replace?: boolean;
9
+ link?: boolean;
9
10
  }
10
11
  }
11
12
  }
@@ -27,7 +28,7 @@ export interface RoutesProps {
27
28
  children: JSX.Element;
28
29
  }
29
30
  export declare const Routes: (props: RoutesProps) => JSX.Element;
30
- export declare const useRoutes: (routes: RouteDefinition | RouteDefinition[], base?: string | undefined) => () => JSX.Element;
31
+ export declare const useRoutes: (routes: RouteDefinition | RouteDefinition[], base?: string) => () => JSX.Element;
31
32
  export declare type RouteProps = {
32
33
  path: string | string[];
33
34
  children?: JSX.Element;
@@ -42,19 +43,17 @@ export declare type RouteProps = {
42
43
  });
43
44
  export declare const Route: (props: RouteProps) => JSX.Element;
44
45
  export declare const Outlet: () => JSX.Element;
45
- export interface LinkProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, "state"> {
46
+ export interface AnchorProps extends Omit<JSX.AnchorHTMLAttributes<HTMLAnchorElement>, "state"> {
46
47
  href: string;
47
48
  replace?: boolean;
48
49
  noScroll?: boolean;
49
50
  state?: unknown;
50
- }
51
- export declare function Link(props: LinkProps): JSX.Element;
52
- export interface NavLinkProps extends LinkProps {
53
51
  inactiveClass?: string;
54
52
  activeClass?: string;
55
53
  end?: boolean;
56
54
  }
57
- export declare function NavLink(props: NavLinkProps): JSX.Element;
55
+ export declare function A(props: AnchorProps): JSX.Element;
56
+ export { A as Link, A as NavLink, AnchorProps as LinkProps, AnchorProps as NavLinkProps };
58
57
  export interface NavigateProps {
59
58
  href: ((args: {
60
59
  navigate: Navigator;
@@ -3,7 +3,7 @@ import { children, createMemo, createRoot, mergeProps, on, Show, splitProps } fr
3
3
  import { isServer } from "solid-js/web";
4
4
  import { pathIntegration, staticIntegration } from "./integration";
5
5
  import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj, useHref, useLocation, useNavigate, useResolvedPath, useRoute, useRouter } from "./routing";
6
- import { joinPaths } from "./utils";
6
+ import { joinPaths, normalizePath } from "./utils";
7
7
  export const Router = (props) => {
8
8
  const { source, url, base, data, out } = props;
9
9
  const integration = source || (isServer ? staticIntegration({ value: url || "" }) : pathIntegration());
@@ -54,7 +54,7 @@ export const Routes = (props) => {
54
54
  return next;
55
55
  }));
56
56
  return (<Show when={routeStates() && root}>
57
- {route => <RouteContextObj.Provider value={route}>{route.outlet()}</RouteContextObj.Provider>}
57
+ {((route) => (<RouteContextObj.Provider value={route}>{route.outlet()}</RouteContextObj.Provider>))}
58
58
  </Show>);
59
59
  };
60
60
  export const useRoutes = (routes, base) => {
@@ -71,36 +71,32 @@ export const Route = (props) => {
71
71
  export const Outlet = () => {
72
72
  const route = useRoute();
73
73
  return (<Show when={route.child}>
74
- {child => <RouteContextObj.Provider value={child}>{child.outlet()}</RouteContextObj.Provider>}
74
+ {((child) => (<RouteContextObj.Provider value={child}>{child.outlet()}</RouteContextObj.Provider>))}
75
75
  </Show>);
76
76
  };
77
- function LinkBase(props) {
78
- const [, rest] = splitProps(props, ["children", "to", "href", "state"]);
79
- const href = useHref(() => props.to);
80
- return (<a {...rest} href={href() || props.href} state={JSON.stringify(props.state)}>
81
- {props.children}
82
- </a>);
83
- }
84
- export function Link(props) {
85
- const to = useResolvedPath(() => props.href);
86
- return <LinkBase {...props} to={to()}/>;
87
- }
88
- export function NavLink(props) {
77
+ export function A(props) {
89
78
  props = mergeProps({ inactiveClass: "inactive", activeClass: "active" }, props);
90
- const [, rest] = splitProps(props, ["activeClass", "inactiveClass", "end"]);
91
- const location = useLocation();
79
+ const [, rest] = splitProps(props, ["href", "state", "class", "activeClass", "inactiveClass", "end"]);
92
80
  const to = useResolvedPath(() => props.href);
81
+ const href = useHref(to);
82
+ const location = useLocation();
93
83
  const isActive = createMemo(() => {
94
84
  const to_ = to();
95
- if (to_ === undefined) {
85
+ if (to_ === undefined)
96
86
  return false;
97
- }
98
- const path = to_.split(/[?#]/, 1)[0].toLowerCase();
99
- const loc = location.pathname.toLowerCase();
87
+ const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
88
+ const loc = normalizePath(location.pathname).toLowerCase();
100
89
  return props.end ? path === loc : loc.startsWith(path);
101
90
  });
102
- return (<LinkBase {...rest} to={to()} classList={{ [props.inactiveClass]: !isActive(), [props.activeClass]: isActive(), ...rest.classList }} aria-current={isActive() ? "page" : undefined}/>);
91
+ return (<a link {...rest} href={href() || props.href} state={JSON.stringify(props.state)} classList={{
92
+ ...(props.class && { [props.class]: true }),
93
+ [props.inactiveClass]: !isActive(),
94
+ [props.activeClass]: isActive(),
95
+ ...rest.classList
96
+ }} aria-current={isActive() ? "page" : undefined}/>);
103
97
  }
98
+ // deprecated alias exports
99
+ export { A as Link, A as NavLink };
104
100
  export function Navigate(props) {
105
101
  const navigate = useNavigate();
106
102
  const location = useLocation();
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./components";
2
2
  export * from "./integration";
3
- export { useRouteData, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams } from "./routing";
3
+ export * from "./lifecycle";
4
+ export { useRouteData, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing";
4
5
  export { mergeSearchString as _mergeSearchString } from "./utils";
5
- export type { Location, LocationChange, LocationChangeSignal, NavigateOptions, Navigator, OutputMatch, Params, RouteDataFunc, RouteDataFuncArgs, RouteDefinition, RouterIntegration, RouterOutput, RouterUtils, SetParams } from "./types";
6
+ export type { Location, LocationChange, LocationChangeSignal, NavigateOptions, Navigator, OutputMatch, Params, RouteDataFunc, RouteDataFuncArgs, RouteDefinition, RouterIntegration, RouterOutput, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types";