@alepha/react 0.9.2 → 0.9.3
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/dist/index.browser.js +136 -78
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +159 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +232 -158
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +230 -156
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +160 -87
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/src/components/ErrorViewer.tsx +1 -1
- package/src/components/Link.tsx +4 -24
- package/src/components/NestedView.tsx +11 -2
- package/src/components/NotFound.tsx +4 -1
- package/src/descriptors/$page.ts +71 -9
- package/src/errors/{RedirectionError.ts → Redirection.ts} +1 -1
- package/src/hooks/RouterHookApi.ts +35 -11
- package/src/hooks/useActive.ts +22 -15
- package/src/hooks/useClient.ts +2 -0
- package/src/hooks/useRouter.ts +2 -1
- package/src/hooks/useRouterEvents.ts +5 -2
- package/src/hooks/useStore.ts +9 -1
- package/src/index.shared.ts +2 -2
- package/src/providers/BrowserRouterProvider.ts +9 -0
- package/src/providers/PageDescriptorProvider.ts +111 -31
- package/src/providers/ReactBrowserProvider.ts +17 -11
- package/src/providers/ReactServerProvider.ts +47 -10
package/src/components/Link.tsx
CHANGED
|
@@ -1,39 +1,19 @@
|
|
|
1
|
+
import type React from "react";
|
|
1
2
|
import type { AnchorHTMLAttributes } from "react";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { RouterContext } from "../contexts/RouterContext.ts";
|
|
4
|
-
import type { PageDescriptor } from "../descriptors/$page.ts";
|
|
5
3
|
import { useRouter } from "../hooks/useRouter.ts";
|
|
6
4
|
|
|
7
5
|
export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
8
|
-
to: string
|
|
6
|
+
to: string;
|
|
9
7
|
children?: React.ReactNode;
|
|
10
8
|
}
|
|
11
9
|
|
|
12
10
|
const Link = (props: LinkProps) => {
|
|
13
|
-
React.useContext(RouterContext);
|
|
14
|
-
|
|
15
11
|
const router = useRouter();
|
|
16
|
-
|
|
17
|
-
const to = typeof props.to === "string" ? props.to : props.to.options.path;
|
|
18
|
-
if (!to) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const can = typeof props.to === "string" ? undefined : props.to.options.can;
|
|
23
|
-
if (can && !can()) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const name = typeof props.to === "string" ? undefined : props.to.options.name;
|
|
28
|
-
|
|
29
|
-
const anchorProps = {
|
|
30
|
-
...props,
|
|
31
|
-
to: undefined,
|
|
32
|
-
};
|
|
12
|
+
const { to, ...anchorProps } = props;
|
|
33
13
|
|
|
34
14
|
return (
|
|
35
15
|
<a {...router.anchor(to)} {...anchorProps}>
|
|
36
|
-
{props.children
|
|
16
|
+
{props.children}
|
|
37
17
|
</a>
|
|
38
18
|
);
|
|
39
19
|
};
|
|
@@ -41,7 +41,10 @@ const NestedView = (props: NestedViewProps) => {
|
|
|
41
41
|
|
|
42
42
|
useRouterEvents(
|
|
43
43
|
{
|
|
44
|
-
onEnd: ({ state }) => {
|
|
44
|
+
onEnd: ({ state, context }) => {
|
|
45
|
+
if (app) {
|
|
46
|
+
app.context = context;
|
|
47
|
+
}
|
|
45
48
|
if (!state.layers[index]?.cache) {
|
|
46
49
|
setView(state.layers[index]?.element);
|
|
47
50
|
}
|
|
@@ -57,7 +60,13 @@ const NestedView = (props: NestedViewProps) => {
|
|
|
57
60
|
const element = view ?? props.children ?? null;
|
|
58
61
|
|
|
59
62
|
return (
|
|
60
|
-
<ErrorBoundary
|
|
63
|
+
<ErrorBoundary
|
|
64
|
+
fallback={(error) => {
|
|
65
|
+
return app.context.onError?.(error, app.context) as ReactNode;
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
{element}
|
|
69
|
+
</ErrorBoundary>
|
|
61
70
|
);
|
|
62
71
|
};
|
|
63
72
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
export default function NotFoundPage(props: { style?: CSSProperties }) {
|
|
2
4
|
return (
|
|
3
5
|
<div
|
|
4
6
|
style={{
|
|
@@ -10,6 +12,7 @@ export default function NotFoundPage() {
|
|
|
10
12
|
textAlign: "center",
|
|
11
13
|
fontFamily: "sans-serif",
|
|
12
14
|
padding: "1rem",
|
|
15
|
+
...props.style,
|
|
13
16
|
}}
|
|
14
17
|
>
|
|
15
18
|
<h1 style={{ fontSize: "1rem", marginBottom: "0.5rem" }}>
|
package/src/descriptors/$page.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
createDescriptor,
|
|
4
4
|
Descriptor,
|
|
5
5
|
KIND,
|
|
6
|
-
NotImplementedError,
|
|
7
6
|
type Static,
|
|
8
7
|
type TSchema,
|
|
9
8
|
} from "@alepha/core";
|
|
@@ -11,6 +10,7 @@ import type { ServerRequest } from "@alepha/server";
|
|
|
11
10
|
import type { ServerRouteCache } from "@alepha/server-cache";
|
|
12
11
|
import type { FC, ReactNode } from "react";
|
|
13
12
|
import type { ClientOnlyProps } from "../components/ClientOnly.tsx";
|
|
13
|
+
import type { Redirection } from "../errors/Redirection.ts";
|
|
14
14
|
import type { PageReactContext } from "../providers/PageDescriptorProvider.ts";
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -109,13 +109,52 @@ export interface PageDescriptorOptions<
|
|
|
109
109
|
|
|
110
110
|
can?: () => boolean;
|
|
111
111
|
|
|
112
|
-
errorHandler?: (error: Error) => ReactNode;
|
|
113
|
-
|
|
114
112
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
113
|
+
* Catch any error from the `resolve` function or during `rendering`.
|
|
114
|
+
*
|
|
115
|
+
* Expected to return one of the following:
|
|
116
|
+
* - a ReactNode to render an error page
|
|
117
|
+
* - a Redirection to redirect the user
|
|
118
|
+
* - undefined to let the error propagate
|
|
119
|
+
*
|
|
120
|
+
* If not defined, the error will be thrown and handled by the server or client error handler.
|
|
121
|
+
* If a leaf $page does not define an error handler, the error can be caught by parent pages.
|
|
117
122
|
*
|
|
123
|
+
* @example Catch a 404 from API and render a custom not found component:
|
|
124
|
+
* ```ts
|
|
125
|
+
* resolve: async ({ params, query }) => {
|
|
126
|
+
* api.fetch("/api/resource", { params, query });
|
|
127
|
+
* },
|
|
128
|
+
* errorHandler: (error, context) => {
|
|
129
|
+
* if (HttpError.is(error, 404)) {
|
|
130
|
+
* return <ResourceNotFound />;
|
|
131
|
+
* }
|
|
132
|
+
* }
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @example Catch an 401 error and redirect the user to the login page:
|
|
136
|
+
* ```ts
|
|
137
|
+
* resolve: async ({ params, query }) => {
|
|
138
|
+
* // but the user is not authenticated
|
|
139
|
+
* api.fetch("/api/resource", { params, query });
|
|
140
|
+
* },
|
|
141
|
+
* errorHandler: (error, context) => {
|
|
142
|
+
* if (HttpError.is(error, 401)) {
|
|
143
|
+
* // throwing a Redirection is also valid!
|
|
144
|
+
* return new Redirection("/login");
|
|
145
|
+
* }
|
|
146
|
+
* }
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
errorHandler?: ErrorHandler;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* If true, the page will be considered as a static page, immutable and cacheable.
|
|
118
153
|
* Replace boolean by an object to define static entries. (e.g. list of params/query)
|
|
154
|
+
*
|
|
155
|
+
* For now, it only works with `@alepha/vite` which can pre-render the page at build time.
|
|
156
|
+
*
|
|
157
|
+
* It will act as timeless cached page server-side. You can use `cache` to configure the cache behavior.
|
|
119
158
|
*/
|
|
120
159
|
static?:
|
|
121
160
|
| boolean
|
|
@@ -123,21 +162,44 @@ export interface PageDescriptorOptions<
|
|
|
123
162
|
entries?: Array<Partial<PageRequestConfig<TConfig>>>;
|
|
124
163
|
};
|
|
125
164
|
|
|
165
|
+
cache?: ServerRouteCache;
|
|
166
|
+
|
|
126
167
|
/**
|
|
127
|
-
* If true, the page
|
|
168
|
+
* If true, force the page to be rendered only on the client-side.
|
|
169
|
+
* It uses the `<ClientOnly/>` component to render the page.
|
|
128
170
|
*/
|
|
129
171
|
client?: boolean | ClientOnlyProps;
|
|
130
172
|
|
|
131
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Called before the server response is sent to the client.
|
|
175
|
+
*/
|
|
176
|
+
onServerResponse?: (request: ServerRequest) => any;
|
|
132
177
|
|
|
133
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Called when user leaves the page. (browser only)
|
|
180
|
+
*/
|
|
181
|
+
onLeave?: () => void;
|
|
134
182
|
}
|
|
135
183
|
|
|
184
|
+
export type ErrorHandler = (
|
|
185
|
+
error: Error,
|
|
186
|
+
context: PageReactContext,
|
|
187
|
+
) => ReactNode | Redirection | undefined;
|
|
188
|
+
|
|
136
189
|
export class PageDescriptor<
|
|
137
190
|
TConfig extends PageConfigSchema = PageConfigSchema,
|
|
138
191
|
TProps extends object = TPropsDefault,
|
|
139
192
|
TPropsParent extends object = TPropsParentDefault,
|
|
140
193
|
> extends Descriptor<PageDescriptorOptions<TConfig, TProps, TPropsParent>> {
|
|
194
|
+
protected onInit() {
|
|
195
|
+
if (this.options.static) {
|
|
196
|
+
this.options.cache ??= {
|
|
197
|
+
provider: "memory",
|
|
198
|
+
ttl: [1, "week"],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
141
203
|
public get name(): string {
|
|
142
204
|
return this.options.name ?? this.config.propertyKey;
|
|
143
205
|
}
|
|
@@ -149,7 +211,7 @@ export class PageDescriptor<
|
|
|
149
211
|
public async render(
|
|
150
212
|
options?: PageDescriptorRenderOptions,
|
|
151
213
|
): Promise<PageDescriptorRenderResult> {
|
|
152
|
-
throw new
|
|
214
|
+
throw new Error("render method is not implemented in this environment");
|
|
153
215
|
}
|
|
154
216
|
}
|
|
155
217
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PageDescriptor } from "../descriptors/$page.ts";
|
|
2
2
|
import type {
|
|
3
3
|
AnchorProps,
|
|
4
|
+
PageDescriptorProvider,
|
|
4
5
|
PageReactContext,
|
|
5
6
|
PageRoute,
|
|
6
7
|
RouterState,
|
|
@@ -10,7 +11,7 @@ import type {
|
|
|
10
11
|
RouterGoOptions,
|
|
11
12
|
} from "../providers/ReactBrowserProvider.ts";
|
|
12
13
|
|
|
13
|
-
export class RouterHookApi {
|
|
14
|
+
export class RouterHookApi<T extends object> {
|
|
14
15
|
constructor(
|
|
15
16
|
private readonly pages: PageRoute[],
|
|
16
17
|
private readonly context: PageReactContext,
|
|
@@ -18,9 +19,26 @@ export class RouterHookApi {
|
|
|
18
19
|
private readonly layer: {
|
|
19
20
|
path: string;
|
|
20
21
|
},
|
|
22
|
+
private readonly pageApi: PageDescriptorProvider,
|
|
21
23
|
private readonly browser?: ReactBrowserProvider,
|
|
22
24
|
) {}
|
|
23
25
|
|
|
26
|
+
public path(
|
|
27
|
+
name: keyof VirtualRouter<T>,
|
|
28
|
+
config: {
|
|
29
|
+
params?: Record<string, string>;
|
|
30
|
+
query?: Record<string, string>;
|
|
31
|
+
} = {},
|
|
32
|
+
): string {
|
|
33
|
+
return this.pageApi.pathname(name as string, {
|
|
34
|
+
params: {
|
|
35
|
+
...this.context.params,
|
|
36
|
+
...config.params,
|
|
37
|
+
},
|
|
38
|
+
query: config.query,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
public getURL(): URL {
|
|
25
43
|
if (!this.browser) {
|
|
26
44
|
return this.context.url;
|
|
@@ -95,48 +113,54 @@ export class RouterHookApi {
|
|
|
95
113
|
}
|
|
96
114
|
|
|
97
115
|
public async go(path: string, options?: RouterGoOptions): Promise<void>;
|
|
98
|
-
public async go
|
|
116
|
+
public async go(
|
|
99
117
|
path: keyof VirtualRouter<T>,
|
|
100
118
|
options?: RouterGoOptions,
|
|
101
119
|
): Promise<void>;
|
|
102
|
-
public async go(
|
|
120
|
+
public async go(
|
|
121
|
+
path: string | keyof VirtualRouter<T>,
|
|
122
|
+
options?: RouterGoOptions,
|
|
123
|
+
): Promise<void> {
|
|
103
124
|
for (const page of this.pages) {
|
|
104
125
|
if (page.name === path) {
|
|
105
|
-
|
|
106
|
-
|
|
126
|
+
await this.browser?.go(
|
|
127
|
+
this.path(path as keyof VirtualRouter<T>, options),
|
|
128
|
+
options,
|
|
129
|
+
);
|
|
130
|
+
return;
|
|
107
131
|
}
|
|
108
132
|
}
|
|
109
133
|
|
|
110
|
-
await this.browser?.go(
|
|
134
|
+
await this.browser?.go(path as string, options);
|
|
111
135
|
}
|
|
112
136
|
|
|
113
137
|
public anchor(
|
|
114
138
|
path: string,
|
|
115
139
|
options?: { params?: Record<string, any> },
|
|
116
140
|
): AnchorProps;
|
|
117
|
-
public anchor
|
|
141
|
+
public anchor(
|
|
118
142
|
path: keyof VirtualRouter<T>,
|
|
119
143
|
options?: { params?: Record<string, any> },
|
|
120
144
|
): AnchorProps;
|
|
121
145
|
public anchor(
|
|
122
|
-
path: string
|
|
146
|
+
path: string | keyof VirtualRouter<T>,
|
|
123
147
|
options: { params?: Record<string, any> } = {},
|
|
124
148
|
): AnchorProps {
|
|
149
|
+
let href = path as string;
|
|
125
150
|
for (const page of this.pages) {
|
|
126
151
|
if (page.name === path) {
|
|
127
|
-
|
|
152
|
+
href = this.path(path as keyof VirtualRouter<T>, options);
|
|
128
153
|
break;
|
|
129
154
|
}
|
|
130
155
|
}
|
|
131
156
|
|
|
132
|
-
const href = this.createHref(path, this.layer, options);
|
|
133
157
|
return {
|
|
134
158
|
href,
|
|
135
159
|
onClick: (ev: any) => {
|
|
136
160
|
ev.stopPropagation();
|
|
137
161
|
ev.preventDefault();
|
|
138
162
|
|
|
139
|
-
this.go(
|
|
163
|
+
this.go(href, options).catch(console.error);
|
|
140
164
|
},
|
|
141
165
|
};
|
|
142
166
|
}
|
package/src/hooks/useActive.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { HrefLike } from "./RouterHookApi.ts";
|
|
|
6
6
|
import { useRouter } from "./useRouter.ts";
|
|
7
7
|
import { useRouterEvents } from "./useRouterEvents.ts";
|
|
8
8
|
|
|
9
|
-
export const useActive = (path
|
|
9
|
+
export const useActive = (path?: HrefLike): UseActiveHook => {
|
|
10
10
|
const router = useRouter();
|
|
11
11
|
const ctx = useContext(RouterContext);
|
|
12
12
|
const layer = useContext(RouterLayerContext);
|
|
@@ -14,29 +14,36 @@ export const useActive = (path: HrefLike): UseActiveHook => {
|
|
|
14
14
|
throw new Error("useRouter must be used within a RouterProvider");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
let name: string | undefined;
|
|
18
|
-
if (typeof path === "object" && path.options.name) {
|
|
19
|
-
name = path.options.name;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
const [current, setCurrent] = useState(ctx.state.pathname);
|
|
23
|
-
const href = useMemo(
|
|
18
|
+
const href = useMemo(
|
|
19
|
+
() => router.createHref(path ?? "", layer),
|
|
20
|
+
[path, layer],
|
|
21
|
+
);
|
|
22
|
+
|
|
24
23
|
const [isPending, setPending] = useState(false);
|
|
25
|
-
const isActive = current === href;
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// TODO: loose [default] or strict
|
|
26
|
+
// TODO: startWith: true (e.g. /p/1 should match /p/1/2)
|
|
27
|
+
const isActive =
|
|
28
|
+
current === href || current === `${href}/` || `${current}/` === href;
|
|
29
|
+
|
|
30
|
+
useRouterEvents(
|
|
31
|
+
{
|
|
32
|
+
onEnd: ({ state }) => {
|
|
33
|
+
path ? setCurrent(state.pathname) : undefined;
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
[path],
|
|
37
|
+
);
|
|
30
38
|
|
|
31
39
|
return {
|
|
32
|
-
name,
|
|
33
40
|
isPending,
|
|
34
41
|
isActive,
|
|
35
42
|
anchorProps: {
|
|
36
43
|
href,
|
|
37
|
-
onClick: (ev
|
|
38
|
-
ev
|
|
39
|
-
ev
|
|
44
|
+
onClick: (ev?: any) => {
|
|
45
|
+
ev?.stopPropagation();
|
|
46
|
+
ev?.preventDefault();
|
|
40
47
|
if (isActive) return;
|
|
41
48
|
if (isPending) return;
|
|
42
49
|
|
package/src/hooks/useClient.ts
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
LinkProvider,
|
|
5
5
|
} from "@alepha/server-links";
|
|
6
6
|
import { useInject } from "./useInject.ts";
|
|
7
|
+
import { useStore } from "./useStore.ts";
|
|
7
8
|
|
|
8
9
|
export const useClient = <T extends object>(
|
|
9
10
|
_scope?: ClientScope,
|
|
10
11
|
): HttpVirtualClient<T> => {
|
|
12
|
+
useStore("user" as any);
|
|
11
13
|
return useInject(LinkProvider).client<T>();
|
|
12
14
|
};
|
package/src/hooks/useRouter.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { ReactBrowserProvider } from "../providers/ReactBrowserProvider.ts";
|
|
|
6
6
|
import { RouterHookApi } from "./RouterHookApi.ts";
|
|
7
7
|
import { useAlepha } from "./useAlepha.ts";
|
|
8
8
|
|
|
9
|
-
export const useRouter = (): RouterHookApi => {
|
|
9
|
+
export const useRouter = <T extends object>(): RouterHookApi<T> => {
|
|
10
10
|
const alepha = useAlepha();
|
|
11
11
|
const ctx = useContext(RouterContext);
|
|
12
12
|
const layer = useContext(RouterLayerContext);
|
|
@@ -25,6 +25,7 @@ export const useRouter = (): RouterHookApi => {
|
|
|
25
25
|
ctx.context,
|
|
26
26
|
ctx.state,
|
|
27
27
|
layer,
|
|
28
|
+
alepha.inject(PageDescriptorProvider),
|
|
28
29
|
alepha.isBrowser() ? alepha.inject(ReactBrowserProvider) : undefined,
|
|
29
30
|
),
|
|
30
31
|
[layer],
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
PageReactContext,
|
|
4
|
+
RouterState,
|
|
5
|
+
} from "../providers/PageDescriptorProvider.ts";
|
|
3
6
|
import { useAlepha } from "./useAlepha.ts";
|
|
4
7
|
|
|
5
8
|
export const useRouterEvents = (
|
|
6
9
|
opts: {
|
|
7
10
|
onBegin?: (ev: { state: RouterState }) => void;
|
|
8
|
-
onEnd?: (ev: { state: RouterState }) => void;
|
|
11
|
+
onEnd?: (ev: { state: RouterState; context: PageReactContext }) => void;
|
|
9
12
|
onError?: (ev: { state: RouterState; error: Error }) => void;
|
|
10
13
|
} = {},
|
|
11
14
|
deps: any[] = [],
|
package/src/hooks/useStore.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { State } from "@alepha/core";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { useAlepha } from "./useAlepha.ts";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -7,8 +7,16 @@ import { useAlepha } from "./useAlepha.ts";
|
|
|
7
7
|
*/
|
|
8
8
|
export const useStore = <Key extends keyof State>(
|
|
9
9
|
key: Key,
|
|
10
|
+
defaultValue?: State[Key],
|
|
10
11
|
): [State[Key], (value: State[Key]) => void] => {
|
|
11
12
|
const alepha = useAlepha();
|
|
13
|
+
|
|
14
|
+
useMemo(() => {
|
|
15
|
+
if (defaultValue != null && alepha.state(key) == null) {
|
|
16
|
+
alepha.state(key, defaultValue);
|
|
17
|
+
}
|
|
18
|
+
}, [defaultValue]);
|
|
19
|
+
|
|
12
20
|
const [state, setState] = useState(alepha.state(key));
|
|
13
21
|
|
|
14
22
|
useEffect(() => {
|
package/src/index.shared.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { default as ClientOnly } from "./components/ClientOnly.tsx";
|
|
2
2
|
export { default as ErrorBoundary } from "./components/ErrorBoundary.tsx";
|
|
3
3
|
export * from "./components/ErrorViewer.tsx";
|
|
4
|
-
export { default as Link } from "./components/Link.tsx";
|
|
4
|
+
export { default as Link, type LinkProps } from "./components/Link.tsx";
|
|
5
5
|
export { default as NestedView } from "./components/NestedView.tsx";
|
|
6
6
|
export { default as NotFound } from "./components/NotFound.tsx";
|
|
7
7
|
export * from "./contexts/AlephaContext.ts";
|
|
8
8
|
export * from "./contexts/RouterContext.ts";
|
|
9
9
|
export * from "./contexts/RouterLayerContext.ts";
|
|
10
10
|
export * from "./descriptors/$page.ts";
|
|
11
|
-
export * from "./errors/
|
|
11
|
+
export * from "./errors/Redirection.ts";
|
|
12
12
|
export * from "./hooks/RouterHookApi.ts";
|
|
13
13
|
export * from "./hooks/useActive.ts";
|
|
14
14
|
export * from "./hooks/useAlepha.ts";
|
|
@@ -129,6 +129,15 @@ export class BrowserRouterProvider extends RouterProvider<BrowserRoute> {
|
|
|
129
129
|
options.state.search = state.search;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
if (options.previous) {
|
|
133
|
+
for (let i = 0; i < options.previous.length; i++) {
|
|
134
|
+
const layer = options.previous[i];
|
|
135
|
+
if (state.layers[i]?.name !== layer.name) {
|
|
136
|
+
this.pageDescriptorProvider.page(layer.name)?.onLeave?.();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
132
141
|
await this.alepha.emit("react:transition:end", {
|
|
133
142
|
state: options.state,
|
|
134
143
|
context,
|