@alepha/react 0.5.1 → 0.6.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.
- package/dist/index.browser.cjs +23 -19
- package/dist/index.browser.js +18 -0
- package/dist/index.cjs +419 -340
- package/dist/{index.d.cts → index.d.ts} +591 -420
- package/dist/index.js +554 -0
- package/dist/{useRouterState-BlKHWZwk.cjs → useAuth-DOVx2kqa.cjs} +243 -102
- package/dist/{useRouterState-CvFCmaq7.mjs → useAuth-i7wbKVrt.js} +214 -77
- package/package.json +21 -23
- package/src/components/Link.tsx +22 -0
- package/src/components/NestedView.tsx +2 -2
- package/src/constants/SSID.ts +1 -0
- package/src/contexts/RouterContext.ts +2 -2
- package/src/descriptors/$auth.ts +28 -0
- package/src/descriptors/$page.ts +57 -3
- package/src/errors/RedirectionError.ts +7 -0
- package/src/hooks/useAuth.ts +29 -0
- package/src/hooks/useInject.ts +3 -3
- package/src/index.browser.ts +3 -1
- package/src/index.shared.ts +14 -3
- package/src/index.ts +23 -6
- package/src/providers/ReactAuthProvider.ts +410 -0
- package/src/providers/ReactBrowserProvider.ts +41 -19
- package/src/providers/ReactServerProvider.ts +106 -49
- package/src/services/Auth.ts +45 -0
- package/src/services/Router.ts +154 -41
- package/dist/index.browser.mjs +0 -17
- package/dist/index.d.mts +0 -872
- package/dist/index.mjs +0 -477
- package/src/providers/ReactSessionProvider.ts +0 -363
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var core = require('@alepha/core');
|
|
4
|
-
var
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var React = require('react');
|
|
5
6
|
var server = require('@alepha/server');
|
|
6
7
|
var client = require('react-dom/client');
|
|
7
8
|
var pathToRegexp = require('path-to-regexp');
|
|
8
9
|
|
|
10
|
+
const KEY = "AUTH";
|
|
11
|
+
const $auth = (options) => {
|
|
12
|
+
core.__descriptor(KEY);
|
|
13
|
+
return {
|
|
14
|
+
[core.KIND]: KEY,
|
|
15
|
+
options
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
$auth[core.KIND] = KEY;
|
|
19
|
+
|
|
9
20
|
const pageDescriptorKey = "PAGE";
|
|
10
21
|
const $page = (options) => {
|
|
11
22
|
core.__descriptor(pageDescriptorKey);
|
|
@@ -25,35 +36,36 @@ const $page = (options) => {
|
|
|
25
36
|
};
|
|
26
37
|
$page[core.KIND] = pageDescriptorKey;
|
|
27
38
|
|
|
28
|
-
const RouterContext =
|
|
39
|
+
const RouterContext = React.createContext(
|
|
29
40
|
void 0
|
|
30
41
|
);
|
|
31
42
|
|
|
32
|
-
const RouterLayerContext =
|
|
43
|
+
const RouterLayerContext = React.createContext(void 0);
|
|
33
44
|
|
|
34
45
|
const NestedView = (props) => {
|
|
35
|
-
const app =
|
|
36
|
-
const layer =
|
|
46
|
+
const app = React.useContext(RouterContext);
|
|
47
|
+
const layer = React.useContext(RouterLayerContext);
|
|
37
48
|
const index = layer?.index ?? 0;
|
|
38
|
-
const [view, setView] =
|
|
49
|
+
const [view, setView] = React.useState(
|
|
39
50
|
app?.state.layers[index]?.element
|
|
40
51
|
);
|
|
41
|
-
|
|
52
|
+
React.useEffect(() => {
|
|
42
53
|
if (app?.alepha.isBrowser()) {
|
|
43
|
-
return app?.router.on("end", (
|
|
44
|
-
setView(layers[index]?.element);
|
|
54
|
+
return app?.router.on("end", (state) => {
|
|
55
|
+
setView(state.layers[index]?.element);
|
|
45
56
|
});
|
|
46
57
|
}
|
|
47
58
|
}, [app]);
|
|
48
59
|
return view ?? props.children ?? null;
|
|
49
60
|
};
|
|
50
61
|
|
|
51
|
-
class
|
|
62
|
+
class RedirectionError extends Error {
|
|
52
63
|
constructor(page) {
|
|
53
64
|
super("Redirection");
|
|
54
65
|
this.page = page;
|
|
55
66
|
}
|
|
56
67
|
}
|
|
68
|
+
|
|
57
69
|
class Router extends core.EventEmitter {
|
|
58
70
|
log = core.$logger();
|
|
59
71
|
alepha = core.$inject(core.Alepha);
|
|
@@ -75,15 +87,18 @@ class Router extends core.EventEmitter {
|
|
|
75
87
|
/**
|
|
76
88
|
*
|
|
77
89
|
*/
|
|
78
|
-
root(state,
|
|
79
|
-
return
|
|
90
|
+
root(state, context = {}) {
|
|
91
|
+
return React.createElement(
|
|
80
92
|
RouterContext.Provider,
|
|
81
93
|
{
|
|
82
94
|
value: {
|
|
83
95
|
state,
|
|
84
96
|
router: this,
|
|
85
97
|
alepha: this.alepha,
|
|
86
|
-
|
|
98
|
+
args: {
|
|
99
|
+
user: context.user,
|
|
100
|
+
cookies: context.cookies
|
|
101
|
+
}
|
|
87
102
|
}
|
|
88
103
|
},
|
|
89
104
|
state.layers[0]?.element
|
|
@@ -99,11 +114,12 @@ class Router extends core.EventEmitter {
|
|
|
99
114
|
const state = {
|
|
100
115
|
pathname,
|
|
101
116
|
search,
|
|
102
|
-
layers: []
|
|
117
|
+
layers: [],
|
|
118
|
+
context: {}
|
|
103
119
|
};
|
|
104
|
-
this.emit("begin", void 0);
|
|
120
|
+
await this.emit("begin", void 0);
|
|
105
121
|
try {
|
|
106
|
-
let layers = await this.match(url, options);
|
|
122
|
+
let layers = await this.match(url, options, state.context);
|
|
107
123
|
if (layers.length === 0) {
|
|
108
124
|
if (this.notFoundPageRoute) {
|
|
109
125
|
layers = await this.createLayers(url, this.notFoundPageRoute);
|
|
@@ -117,13 +133,14 @@ class Router extends core.EventEmitter {
|
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
state.layers = layers;
|
|
120
|
-
this.emit("success", void 0);
|
|
136
|
+
await this.emit("success", void 0);
|
|
121
137
|
} catch (e) {
|
|
122
|
-
if (e instanceof
|
|
138
|
+
if (e instanceof RedirectionError) {
|
|
123
139
|
return {
|
|
124
140
|
element: null,
|
|
125
141
|
layers: [],
|
|
126
|
-
redirect: typeof e.page === "string" ? e.page : this.href(e.page)
|
|
142
|
+
redirect: typeof e.page === "string" ? e.page : this.href(e.page),
|
|
143
|
+
context: state.context
|
|
127
144
|
};
|
|
128
145
|
}
|
|
129
146
|
this.log.error(e);
|
|
@@ -135,31 +152,35 @@ class Router extends core.EventEmitter {
|
|
|
135
152
|
path: "/"
|
|
136
153
|
}
|
|
137
154
|
];
|
|
138
|
-
this.emit("error", e);
|
|
155
|
+
await this.emit("error", e);
|
|
139
156
|
}
|
|
140
157
|
if (options.state) {
|
|
141
158
|
options.state.layers = state.layers;
|
|
142
159
|
options.state.pathname = state.pathname;
|
|
143
160
|
options.state.search = state.search;
|
|
144
|
-
|
|
161
|
+
options.state.context = state.context;
|
|
162
|
+
await this.emit("end", options.state);
|
|
145
163
|
return {
|
|
146
|
-
element: this.root(options.state, options),
|
|
147
|
-
layers: options.state.layers
|
|
164
|
+
element: this.root(options.state, options.args),
|
|
165
|
+
layers: options.state.layers,
|
|
166
|
+
context: state.context
|
|
148
167
|
};
|
|
149
168
|
}
|
|
150
|
-
this.emit("end", state);
|
|
169
|
+
await this.emit("end", state);
|
|
151
170
|
return {
|
|
152
|
-
element: this.root(state, options),
|
|
153
|
-
layers: state.layers
|
|
171
|
+
element: this.root(state, options.args),
|
|
172
|
+
layers: state.layers,
|
|
173
|
+
context: state.context
|
|
154
174
|
};
|
|
155
175
|
}
|
|
156
176
|
/**
|
|
157
177
|
*
|
|
158
178
|
* @param url
|
|
159
179
|
* @param options
|
|
180
|
+
* @param context
|
|
160
181
|
* @protected
|
|
161
182
|
*/
|
|
162
|
-
async match(url, options = {}) {
|
|
183
|
+
async match(url, options = {}, context = {}) {
|
|
163
184
|
const pages = this.pages;
|
|
164
185
|
const previous = options.previous;
|
|
165
186
|
const [pathname, search] = url.split("?");
|
|
@@ -181,7 +202,8 @@ class Router extends core.EventEmitter {
|
|
|
181
202
|
params,
|
|
182
203
|
query,
|
|
183
204
|
previous,
|
|
184
|
-
options.
|
|
205
|
+
options.args,
|
|
206
|
+
context
|
|
185
207
|
);
|
|
186
208
|
}
|
|
187
209
|
}
|
|
@@ -190,14 +212,16 @@ class Router extends core.EventEmitter {
|
|
|
190
212
|
/**
|
|
191
213
|
* Create layers for the given route.
|
|
192
214
|
*
|
|
215
|
+
* @param url
|
|
193
216
|
* @param route
|
|
194
217
|
* @param params
|
|
195
218
|
* @param query
|
|
196
219
|
* @param previous
|
|
197
|
-
* @param
|
|
220
|
+
* @param args
|
|
221
|
+
* @param renderContext
|
|
198
222
|
* @protected
|
|
199
223
|
*/
|
|
200
|
-
async createLayers(url, route, params = {}, query = {}, previous = [],
|
|
224
|
+
async createLayers(url, route, params = {}, query = {}, previous = [], args, renderContext) {
|
|
201
225
|
const layers = [];
|
|
202
226
|
let context = {};
|
|
203
227
|
const stack = [{ route }];
|
|
@@ -212,13 +236,13 @@ class Router extends core.EventEmitter {
|
|
|
212
236
|
const route2 = it.route;
|
|
213
237
|
const config = {};
|
|
214
238
|
try {
|
|
215
|
-
config.query = route2.schema?.query ? this.alepha.parse(route2.schema.query, query) :
|
|
239
|
+
config.query = route2.schema?.query ? this.alepha.parse(route2.schema.query, query) : query;
|
|
216
240
|
} catch (e) {
|
|
217
241
|
it.error = e;
|
|
218
242
|
break;
|
|
219
243
|
}
|
|
220
244
|
try {
|
|
221
|
-
config.params = route2.schema?.params ? this.alepha.parse(route2.schema.params, params) :
|
|
245
|
+
config.params = route2.schema?.params ? this.alepha.parse(route2.schema.params, params) : params;
|
|
222
246
|
} catch (e) {
|
|
223
247
|
it.error = e;
|
|
224
248
|
break;
|
|
@@ -250,12 +274,15 @@ class Router extends core.EventEmitter {
|
|
|
250
274
|
forceRefresh = true;
|
|
251
275
|
}
|
|
252
276
|
try {
|
|
253
|
-
const props = await route2.resolve?.(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
277
|
+
const props = await route2.resolve?.(
|
|
278
|
+
{
|
|
279
|
+
...config,
|
|
280
|
+
...context,
|
|
281
|
+
context: args,
|
|
282
|
+
url
|
|
283
|
+
},
|
|
284
|
+
args ?? {}
|
|
285
|
+
) ?? {};
|
|
259
286
|
it.props = {
|
|
260
287
|
...props
|
|
261
288
|
};
|
|
@@ -264,7 +291,7 @@ class Router extends core.EventEmitter {
|
|
|
264
291
|
...props
|
|
265
292
|
};
|
|
266
293
|
} catch (e) {
|
|
267
|
-
if (e instanceof
|
|
294
|
+
if (e instanceof RedirectionError) {
|
|
268
295
|
throw e;
|
|
269
296
|
}
|
|
270
297
|
this.log.error(e);
|
|
@@ -280,6 +307,12 @@ class Router extends core.EventEmitter {
|
|
|
280
307
|
for (const key of Object.keys(params2)) {
|
|
281
308
|
params2[key] = String(params2[key]);
|
|
282
309
|
}
|
|
310
|
+
if (it.route.helmet && renderContext) {
|
|
311
|
+
this.mergeRenderContext(it.route, renderContext, {
|
|
312
|
+
...props,
|
|
313
|
+
...context
|
|
314
|
+
});
|
|
315
|
+
}
|
|
283
316
|
acc += "/";
|
|
284
317
|
acc += it.route.path ? pathToRegexp.compile(it.route.path)(params2) : "";
|
|
285
318
|
const path = acc.replace(/\/+/, "/");
|
|
@@ -339,20 +372,41 @@ class Router extends core.EventEmitter {
|
|
|
339
372
|
async createElement(page, props) {
|
|
340
373
|
if (page.lazy) {
|
|
341
374
|
const component = await page.lazy();
|
|
342
|
-
return
|
|
375
|
+
return React.createElement(component.default, props);
|
|
343
376
|
}
|
|
344
377
|
if (page.component) {
|
|
345
|
-
return
|
|
378
|
+
return React.createElement(page.component, props);
|
|
346
379
|
}
|
|
347
380
|
return void 0;
|
|
348
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Merge the render context with the page context.
|
|
384
|
+
*
|
|
385
|
+
* @param page
|
|
386
|
+
* @param ctx
|
|
387
|
+
* @param props
|
|
388
|
+
* @protected
|
|
389
|
+
*/
|
|
390
|
+
mergeRenderContext(page, ctx, props) {
|
|
391
|
+
if (page.helmet) {
|
|
392
|
+
const helmet = typeof page.helmet === "function" ? page.helmet(props) : page.helmet;
|
|
393
|
+
if (helmet.title) {
|
|
394
|
+
ctx.helmet ??= {};
|
|
395
|
+
if (ctx.helmet?.title) {
|
|
396
|
+
ctx.helmet.title = `${helmet.title} - ${ctx.helmet.title}`;
|
|
397
|
+
} else {
|
|
398
|
+
ctx.helmet.title = helmet.title;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
349
403
|
/**
|
|
350
404
|
*
|
|
351
405
|
* @param e
|
|
352
406
|
* @protected
|
|
353
407
|
*/
|
|
354
408
|
renderError(e) {
|
|
355
|
-
return
|
|
409
|
+
return React.createElement("pre", { style: { overflow: "auto" } }, `${e.stack}`);
|
|
356
410
|
}
|
|
357
411
|
/**
|
|
358
412
|
* Render an empty view.
|
|
@@ -360,7 +414,7 @@ class Router extends core.EventEmitter {
|
|
|
360
414
|
* @protected
|
|
361
415
|
*/
|
|
362
416
|
renderEmptyView() {
|
|
363
|
-
return
|
|
417
|
+
return React.createElement(NestedView, {});
|
|
364
418
|
}
|
|
365
419
|
/**
|
|
366
420
|
* Create a valid href for the given page.
|
|
@@ -389,7 +443,7 @@ class Router extends core.EventEmitter {
|
|
|
389
443
|
* @protected
|
|
390
444
|
*/
|
|
391
445
|
renderView(index, path, view = this.renderEmptyView()) {
|
|
392
|
-
return
|
|
446
|
+
return React.createElement(
|
|
393
447
|
RouterLayerContext.Provider,
|
|
394
448
|
{
|
|
395
449
|
value: {
|
|
@@ -517,7 +571,12 @@ class ReactBrowserProvider {
|
|
|
517
571
|
router = core.$inject(Router);
|
|
518
572
|
root;
|
|
519
573
|
transitioning;
|
|
520
|
-
state = {
|
|
574
|
+
state = {
|
|
575
|
+
layers: [],
|
|
576
|
+
pathname: "",
|
|
577
|
+
search: "",
|
|
578
|
+
context: {}
|
|
579
|
+
};
|
|
521
580
|
/**
|
|
522
581
|
*
|
|
523
582
|
*/
|
|
@@ -599,12 +658,17 @@ class ReactBrowserProvider {
|
|
|
599
658
|
this.transitioning = void 0;
|
|
600
659
|
return { url };
|
|
601
660
|
}
|
|
661
|
+
renderHelmetContext(ctx) {
|
|
662
|
+
if (ctx.title) {
|
|
663
|
+
this.document.title = ctx.title;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
602
666
|
/**
|
|
603
667
|
* Get embedded layers from the server.
|
|
604
668
|
*
|
|
605
669
|
* @protected
|
|
606
670
|
*/
|
|
607
|
-
|
|
671
|
+
getHydrationState() {
|
|
608
672
|
try {
|
|
609
673
|
if ("__ssr" in window && typeof window.__ssr === "object") {
|
|
610
674
|
return window.__ssr;
|
|
@@ -627,6 +691,18 @@ class ReactBrowserProvider {
|
|
|
627
691
|
this.document.body.appendChild(div);
|
|
628
692
|
return div;
|
|
629
693
|
}
|
|
694
|
+
getUserFromCookies() {
|
|
695
|
+
const cookies = this.document.cookie.split("; ");
|
|
696
|
+
const userCookie = cookies.find((cookie) => cookie.startsWith("user="));
|
|
697
|
+
try {
|
|
698
|
+
if (userCookie) {
|
|
699
|
+
return JSON.parse(decodeURIComponent(userCookie.split("=")[1]));
|
|
700
|
+
}
|
|
701
|
+
} catch (error) {
|
|
702
|
+
this.log.warn(error, "Failed to parse user cookie");
|
|
703
|
+
}
|
|
704
|
+
return void 0;
|
|
705
|
+
}
|
|
630
706
|
// -------------------------------------------------------------------------------------------------------------------
|
|
631
707
|
/**
|
|
632
708
|
*
|
|
@@ -635,11 +711,12 @@ class ReactBrowserProvider {
|
|
|
635
711
|
ready = core.$hook({
|
|
636
712
|
name: "ready",
|
|
637
713
|
handler: async () => {
|
|
638
|
-
const cache = this.
|
|
714
|
+
const cache = this.getHydrationState();
|
|
639
715
|
const previous = cache?.layers ?? [];
|
|
640
|
-
const session = cache?.session ?? await this.client.of().session();
|
|
641
716
|
await this.render({ previous });
|
|
642
|
-
const element = this.router.root(this.state,
|
|
717
|
+
const element = this.router.root(this.state, {
|
|
718
|
+
user: cache?.user ?? this.getUserFromCookies()
|
|
719
|
+
});
|
|
643
720
|
if (previous.length > 0) {
|
|
644
721
|
this.root = client.hydrateRoot(this.getRootElement(), element);
|
|
645
722
|
this.log.info("Hydrated root element");
|
|
@@ -651,6 +728,11 @@ class ReactBrowserProvider {
|
|
|
651
728
|
window.addEventListener("popstate", () => {
|
|
652
729
|
this.render();
|
|
653
730
|
});
|
|
731
|
+
this.router.on("end", ({ context }) => {
|
|
732
|
+
if (context.helmet) {
|
|
733
|
+
this.renderHelmetContext(context.helmet);
|
|
734
|
+
}
|
|
735
|
+
});
|
|
654
736
|
}
|
|
655
737
|
});
|
|
656
738
|
/**
|
|
@@ -668,6 +750,38 @@ class ReactBrowserProvider {
|
|
|
668
750
|
});
|
|
669
751
|
}
|
|
670
752
|
|
|
753
|
+
class Auth {
|
|
754
|
+
alepha = core.$inject(core.Alepha);
|
|
755
|
+
log = core.$logger();
|
|
756
|
+
client = core.$inject(server.HttpClient);
|
|
757
|
+
api = "/api/_oauth/login";
|
|
758
|
+
start = core.$hook({
|
|
759
|
+
name: "start",
|
|
760
|
+
handler: async () => {
|
|
761
|
+
this.client.on("onError", (err) => {
|
|
762
|
+
if (err.statusCode === 401) {
|
|
763
|
+
this.login();
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
login = (provider) => {
|
|
769
|
+
if (this.alepha.isBrowser()) {
|
|
770
|
+
const browser = this.alepha.get(ReactBrowserProvider);
|
|
771
|
+
const redirect = browser.transitioning ? window.location.origin + browser.transitioning.to : window.location.href;
|
|
772
|
+
window.location.href = `${this.api}?redirect=${redirect}`;
|
|
773
|
+
if (browser.transitioning) {
|
|
774
|
+
throw new RedirectionError(browser.state.pathname);
|
|
775
|
+
}
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
throw new RedirectionError(this.api);
|
|
779
|
+
};
|
|
780
|
+
logout = () => {
|
|
781
|
+
window.location.href = `/api/_oauth/logout?redirect=${encodeURIComponent(window.location.origin)}`;
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
671
785
|
class RouterHookApi {
|
|
672
786
|
constructor(state, layer, browser) {
|
|
673
787
|
this.state = state;
|
|
@@ -777,12 +891,12 @@ class RouterHookApi {
|
|
|
777
891
|
}
|
|
778
892
|
|
|
779
893
|
const useRouter = () => {
|
|
780
|
-
const ctx =
|
|
781
|
-
const layer =
|
|
894
|
+
const ctx = React.useContext(RouterContext);
|
|
895
|
+
const layer = React.useContext(RouterLayerContext);
|
|
782
896
|
if (!ctx || !layer) {
|
|
783
897
|
throw new Error("useRouter must be used within a RouterProvider");
|
|
784
898
|
}
|
|
785
|
-
return
|
|
899
|
+
return React.useMemo(
|
|
786
900
|
() => new RouterHookApi(
|
|
787
901
|
ctx.state,
|
|
788
902
|
layer,
|
|
@@ -792,51 +906,18 @@ const useRouter = () => {
|
|
|
792
906
|
);
|
|
793
907
|
};
|
|
794
908
|
|
|
795
|
-
const
|
|
909
|
+
const Link = (props) => {
|
|
910
|
+
React.useContext(RouterContext);
|
|
796
911
|
const router = useRouter();
|
|
797
|
-
|
|
798
|
-
const layer = react.useContext(RouterLayerContext);
|
|
799
|
-
if (!ctx || !layer) {
|
|
800
|
-
throw new Error("useRouter must be used within a RouterProvider");
|
|
801
|
-
}
|
|
802
|
-
let name;
|
|
803
|
-
if (typeof path === "object" && path.options.name) {
|
|
804
|
-
name = path.options.name;
|
|
805
|
-
}
|
|
806
|
-
const [current, setCurrent] = react.useState(ctx.state.pathname);
|
|
807
|
-
const href = react.useMemo(() => router.createHref(path, layer), [path, layer]);
|
|
808
|
-
const [isPending, setPending] = react.useState(false);
|
|
809
|
-
const isActive = current === href;
|
|
810
|
-
react.useEffect(
|
|
811
|
-
() => ctx.router.on("end", ({ pathname }) => setCurrent(pathname)),
|
|
812
|
-
[]
|
|
813
|
-
);
|
|
814
|
-
return {
|
|
815
|
-
name,
|
|
816
|
-
isPending,
|
|
817
|
-
isActive,
|
|
818
|
-
anchorProps: {
|
|
819
|
-
href,
|
|
820
|
-
onClick: (ev) => {
|
|
821
|
-
ev.stopPropagation();
|
|
822
|
-
ev.preventDefault();
|
|
823
|
-
if (isActive) return;
|
|
824
|
-
if (isPending) return;
|
|
825
|
-
setPending(true);
|
|
826
|
-
router.go(href).then(() => {
|
|
827
|
-
setPending(false);
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
};
|
|
912
|
+
return /* @__PURE__ */ jsxRuntime.jsx("a", { ...router.createAnchorProps(props.to), ...props, children: props.children });
|
|
832
913
|
};
|
|
833
914
|
|
|
834
|
-
const useInject = (
|
|
835
|
-
const ctx =
|
|
915
|
+
const useInject = (clazz) => {
|
|
916
|
+
const ctx = React.useContext(RouterContext);
|
|
836
917
|
if (!ctx) {
|
|
837
918
|
throw new Error("useRouter must be used within a <RouterProvider>");
|
|
838
919
|
}
|
|
839
|
-
return ctx.alepha.get(
|
|
920
|
+
return ctx.alepha.get(clazz);
|
|
840
921
|
};
|
|
841
922
|
|
|
842
923
|
const useClient = () => {
|
|
@@ -844,17 +925,17 @@ const useClient = () => {
|
|
|
844
925
|
};
|
|
845
926
|
|
|
846
927
|
const useQueryParams = (schema, options = {}) => {
|
|
847
|
-
const ctx =
|
|
928
|
+
const ctx = React.useContext(RouterContext);
|
|
848
929
|
if (!ctx) {
|
|
849
930
|
throw new Error("useQueryParams must be used within a RouterProvider");
|
|
850
931
|
}
|
|
851
932
|
const key = options.key ?? "q";
|
|
852
933
|
const router = useRouter();
|
|
853
934
|
const querystring = router.query[key];
|
|
854
|
-
const [queryParams, setQueryParams] =
|
|
935
|
+
const [queryParams, setQueryParams] = React.useState(
|
|
855
936
|
decode(ctx.alepha, schema, router.query[key])
|
|
856
937
|
);
|
|
857
|
-
|
|
938
|
+
React.useEffect(() => {
|
|
858
939
|
setQueryParams(decode(ctx.alepha, schema, querystring));
|
|
859
940
|
}, [querystring]);
|
|
860
941
|
return [
|
|
@@ -882,12 +963,12 @@ const decode = (alepha, schema, data) => {
|
|
|
882
963
|
};
|
|
883
964
|
|
|
884
965
|
const useRouterEvents = (opts = {}) => {
|
|
885
|
-
const ctx =
|
|
886
|
-
const layer =
|
|
966
|
+
const ctx = React.useContext(RouterContext);
|
|
967
|
+
const layer = React.useContext(RouterLayerContext);
|
|
887
968
|
if (!ctx || !layer) {
|
|
888
969
|
throw new Error("useRouter must be used within a RouterProvider");
|
|
889
970
|
}
|
|
890
|
-
|
|
971
|
+
React.useEffect(() => {
|
|
891
972
|
const subs = [];
|
|
892
973
|
const onBegin = opts.onBegin;
|
|
893
974
|
const onEnd = opts.onEnd;
|
|
@@ -910,13 +991,13 @@ const useRouterEvents = (opts = {}) => {
|
|
|
910
991
|
};
|
|
911
992
|
|
|
912
993
|
const useRouterState = () => {
|
|
913
|
-
const ctx =
|
|
914
|
-
const layer =
|
|
994
|
+
const ctx = React.useContext(RouterContext);
|
|
995
|
+
const layer = React.useContext(RouterLayerContext);
|
|
915
996
|
if (!ctx || !layer) {
|
|
916
997
|
throw new Error("useRouter must be used within a RouterProvider");
|
|
917
998
|
}
|
|
918
|
-
const [state, setState] =
|
|
919
|
-
|
|
999
|
+
const [state, setState] = React.useState(ctx.state);
|
|
1000
|
+
React.useEffect(
|
|
920
1001
|
() => ctx.router.on("end", (it) => {
|
|
921
1002
|
setState({ ...it });
|
|
922
1003
|
}),
|
|
@@ -925,17 +1006,77 @@ const useRouterState = () => {
|
|
|
925
1006
|
return state;
|
|
926
1007
|
};
|
|
927
1008
|
|
|
1009
|
+
const useActive = (path) => {
|
|
1010
|
+
const router = useRouter();
|
|
1011
|
+
const ctx = React.useContext(RouterContext);
|
|
1012
|
+
const layer = React.useContext(RouterLayerContext);
|
|
1013
|
+
if (!ctx || !layer) {
|
|
1014
|
+
throw new Error("useRouter must be used within a RouterProvider");
|
|
1015
|
+
}
|
|
1016
|
+
let name;
|
|
1017
|
+
if (typeof path === "object" && path.options.name) {
|
|
1018
|
+
name = path.options.name;
|
|
1019
|
+
}
|
|
1020
|
+
const [current, setCurrent] = React.useState(ctx.state.pathname);
|
|
1021
|
+
const href = React.useMemo(() => router.createHref(path, layer), [path, layer]);
|
|
1022
|
+
const [isPending, setPending] = React.useState(false);
|
|
1023
|
+
const isActive = current === href;
|
|
1024
|
+
React.useEffect(
|
|
1025
|
+
() => ctx.router.on("end", ({ pathname }) => setCurrent(pathname)),
|
|
1026
|
+
[]
|
|
1027
|
+
);
|
|
1028
|
+
return {
|
|
1029
|
+
name,
|
|
1030
|
+
isPending,
|
|
1031
|
+
isActive,
|
|
1032
|
+
anchorProps: {
|
|
1033
|
+
href,
|
|
1034
|
+
onClick: (ev) => {
|
|
1035
|
+
ev.stopPropagation();
|
|
1036
|
+
ev.preventDefault();
|
|
1037
|
+
if (isActive) return;
|
|
1038
|
+
if (isPending) return;
|
|
1039
|
+
setPending(true);
|
|
1040
|
+
router.go(href).then(() => {
|
|
1041
|
+
setPending(false);
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
const useAuth = () => {
|
|
1049
|
+
const ctx = React.useContext(RouterContext);
|
|
1050
|
+
if (!ctx) {
|
|
1051
|
+
throw new Error("useAuth must be used within a RouterContext");
|
|
1052
|
+
}
|
|
1053
|
+
const args = ctx.args ?? {};
|
|
1054
|
+
return {
|
|
1055
|
+
user: args.user,
|
|
1056
|
+
logout: () => {
|
|
1057
|
+
ctx.alepha.get(Auth).logout();
|
|
1058
|
+
},
|
|
1059
|
+
login: (provider) => {
|
|
1060
|
+
ctx.alepha.get(Auth).login();
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
exports.$auth = $auth;
|
|
928
1066
|
exports.$page = $page;
|
|
1067
|
+
exports.Auth = Auth;
|
|
1068
|
+
exports.Link = Link;
|
|
929
1069
|
exports.NestedView = NestedView;
|
|
930
1070
|
exports.PageDescriptorProvider = PageDescriptorProvider;
|
|
931
1071
|
exports.ReactBrowserProvider = ReactBrowserProvider;
|
|
932
|
-
exports.
|
|
1072
|
+
exports.RedirectionError = RedirectionError;
|
|
933
1073
|
exports.Router = Router;
|
|
934
1074
|
exports.RouterContext = RouterContext;
|
|
935
1075
|
exports.RouterHookApi = RouterHookApi;
|
|
936
1076
|
exports.RouterLayerContext = RouterLayerContext;
|
|
937
1077
|
exports.pageDescriptorKey = pageDescriptorKey;
|
|
938
1078
|
exports.useActive = useActive;
|
|
1079
|
+
exports.useAuth = useAuth;
|
|
939
1080
|
exports.useClient = useClient;
|
|
940
1081
|
exports.useInject = useInject;
|
|
941
1082
|
exports.useQueryParams = useQueryParams;
|