@alepha/react 0.7.0 → 0.7.2
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 +1 -1
- package/dist/index.browser.cjs +59 -22
- package/dist/index.browser.js +42 -6
- package/dist/index.cjs +152 -85
- package/dist/index.d.ts +317 -206
- package/dist/index.js +130 -64
- package/dist/{useActive-DjpZBEuB.cjs → useRouterState-C2uo0jXu.cjs} +319 -140
- package/dist/{useActive-BX41CqY8.js → useRouterState-D5__ZcUV.js} +321 -143
- package/package.json +11 -10
- package/src/components/ClientOnly.tsx +35 -0
- package/src/components/ErrorBoundary.tsx +1 -1
- package/src/components/ErrorViewer.tsx +161 -0
- package/src/components/Link.tsx +9 -3
- package/src/components/NestedView.tsx +18 -3
- package/src/components/NotFound.tsx +30 -0
- package/src/descriptors/$page.ts +141 -30
- package/src/errors/RedirectionError.ts +4 -1
- package/src/hooks/RouterHookApi.ts +42 -24
- package/src/hooks/useAlepha.ts +12 -0
- package/src/hooks/useClient.ts +8 -6
- package/src/hooks/useInject.ts +2 -2
- package/src/hooks/useQueryParams.ts +1 -1
- package/src/hooks/useRouter.ts +6 -0
- package/src/index.browser.ts +4 -2
- package/src/index.shared.ts +11 -5
- package/src/index.ts +3 -4
- package/src/providers/BrowserRouterProvider.ts +4 -3
- package/src/providers/PageDescriptorProvider.ts +91 -20
- package/src/providers/ReactBrowserProvider.ts +6 -59
- package/src/providers/ReactBrowserRenderer.ts +72 -0
- package/src/providers/ReactServerProvider.ts +200 -81
- package/dist/index.browser.cjs.map +0 -1
- package/dist/index.browser.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/useActive-BX41CqY8.js.map +0 -1
- package/dist/useActive-DjpZBEuB.cjs.map +0 -1
|
@@ -4,7 +4,6 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
4
4
|
var core = require('@alepha/core');
|
|
5
5
|
var React = require('react');
|
|
6
6
|
var server = require('@alepha/server');
|
|
7
|
-
var client = require('react-dom/client');
|
|
8
7
|
var router = require('@alepha/router');
|
|
9
8
|
|
|
10
9
|
const KEY = "PAGE";
|
|
@@ -28,27 +27,161 @@ const $page = (options) => {
|
|
|
28
27
|
[core.OPTIONS]: options,
|
|
29
28
|
render: () => {
|
|
30
29
|
throw new core.NotImplementedError(KEY);
|
|
31
|
-
},
|
|
32
|
-
go: () => {
|
|
33
|
-
throw new core.NotImplementedError(KEY);
|
|
34
|
-
},
|
|
35
|
-
createAnchorProps: () => {
|
|
36
|
-
throw new core.NotImplementedError(KEY);
|
|
37
|
-
},
|
|
38
|
-
can: () => {
|
|
39
|
-
if (options.can) {
|
|
40
|
-
return options.can();
|
|
41
|
-
}
|
|
42
|
-
return true;
|
|
43
30
|
}
|
|
44
31
|
};
|
|
45
32
|
};
|
|
46
33
|
$page[core.KIND] = KEY;
|
|
47
34
|
|
|
35
|
+
const ClientOnly = (props) => {
|
|
36
|
+
const [mounted, setMounted] = React.useState(false);
|
|
37
|
+
React.useEffect(() => setMounted(true), []);
|
|
38
|
+
if (props.disabled) {
|
|
39
|
+
return props.children;
|
|
40
|
+
}
|
|
41
|
+
return mounted ? props.children : props.fallback;
|
|
42
|
+
};
|
|
43
|
+
|
|
48
44
|
const RouterContext = React.createContext(
|
|
49
45
|
void 0
|
|
50
46
|
);
|
|
51
47
|
|
|
48
|
+
const useAlepha = () => {
|
|
49
|
+
const routerContext = React.useContext(RouterContext);
|
|
50
|
+
if (!routerContext) {
|
|
51
|
+
throw new Error("useAlepha must be used within a RouterProvider");
|
|
52
|
+
}
|
|
53
|
+
return routerContext.alepha;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const ErrorViewer = ({ error }) => {
|
|
57
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
58
|
+
const isProduction = useAlepha().isProduction();
|
|
59
|
+
if (isProduction) {
|
|
60
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ErrorViewerProduction, {});
|
|
61
|
+
}
|
|
62
|
+
const stackLines = error.stack?.split("\n") ?? [];
|
|
63
|
+
const previewLines = stackLines.slice(0, 5);
|
|
64
|
+
const hiddenLineCount = stackLines.length - previewLines.length;
|
|
65
|
+
const copyToClipboard = (text) => {
|
|
66
|
+
navigator.clipboard.writeText(text).catch((err) => {
|
|
67
|
+
console.error("Clipboard error:", err);
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const styles = {
|
|
71
|
+
container: {
|
|
72
|
+
padding: "24px",
|
|
73
|
+
backgroundColor: "#FEF2F2",
|
|
74
|
+
color: "#7F1D1D",
|
|
75
|
+
border: "1px solid #FECACA",
|
|
76
|
+
borderRadius: "16px",
|
|
77
|
+
boxShadow: "0 8px 24px rgba(0,0,0,0.05)",
|
|
78
|
+
fontFamily: "monospace",
|
|
79
|
+
maxWidth: "768px",
|
|
80
|
+
margin: "40px auto"
|
|
81
|
+
},
|
|
82
|
+
heading: {
|
|
83
|
+
fontSize: "20px",
|
|
84
|
+
fontWeight: "bold",
|
|
85
|
+
marginBottom: "4px"
|
|
86
|
+
},
|
|
87
|
+
name: {
|
|
88
|
+
fontSize: "16px",
|
|
89
|
+
fontWeight: 600
|
|
90
|
+
},
|
|
91
|
+
message: {
|
|
92
|
+
fontSize: "14px",
|
|
93
|
+
marginBottom: "16px"
|
|
94
|
+
},
|
|
95
|
+
sectionHeader: {
|
|
96
|
+
display: "flex",
|
|
97
|
+
justifyContent: "space-between",
|
|
98
|
+
alignItems: "center",
|
|
99
|
+
fontSize: "12px",
|
|
100
|
+
marginBottom: "4px",
|
|
101
|
+
color: "#991B1B"
|
|
102
|
+
},
|
|
103
|
+
copyButton: {
|
|
104
|
+
fontSize: "12px",
|
|
105
|
+
color: "#DC2626",
|
|
106
|
+
background: "none",
|
|
107
|
+
border: "none",
|
|
108
|
+
cursor: "pointer",
|
|
109
|
+
textDecoration: "underline"
|
|
110
|
+
},
|
|
111
|
+
stackContainer: {
|
|
112
|
+
backgroundColor: "#FEE2E2",
|
|
113
|
+
padding: "12px",
|
|
114
|
+
borderRadius: "8px",
|
|
115
|
+
fontSize: "13px",
|
|
116
|
+
lineHeight: "1.4",
|
|
117
|
+
overflowX: "auto",
|
|
118
|
+
whiteSpace: "pre-wrap"
|
|
119
|
+
},
|
|
120
|
+
expandLine: {
|
|
121
|
+
color: "#F87171",
|
|
122
|
+
cursor: "pointer",
|
|
123
|
+
marginTop: "8px"
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.container, children: [
|
|
127
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
128
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.heading, children: "\u{1F525} Error" }),
|
|
129
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.name, children: error.name }),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.message, children: error.message })
|
|
131
|
+
] }),
|
|
132
|
+
stackLines.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
133
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.sectionHeader, children: [
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Stack trace" }),
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
136
|
+
"button",
|
|
137
|
+
{
|
|
138
|
+
onClick: () => copyToClipboard(error.stack),
|
|
139
|
+
style: styles.copyButton,
|
|
140
|
+
children: "Copy all"
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
] }),
|
|
144
|
+
/* @__PURE__ */ jsxRuntime.jsxs("pre", { style: styles.stackContainer, children: [
|
|
145
|
+
(expanded ? stackLines : previewLines).map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: line }, i)),
|
|
146
|
+
!expanded && hiddenLineCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.expandLine, onClick: () => setExpanded(true), children: [
|
|
147
|
+
"+ ",
|
|
148
|
+
hiddenLineCount,
|
|
149
|
+
" more lines..."
|
|
150
|
+
] })
|
|
151
|
+
] })
|
|
152
|
+
] })
|
|
153
|
+
] });
|
|
154
|
+
};
|
|
155
|
+
const ErrorViewerProduction = () => {
|
|
156
|
+
const styles = {
|
|
157
|
+
container: {
|
|
158
|
+
padding: "24px",
|
|
159
|
+
backgroundColor: "#FEF2F2",
|
|
160
|
+
color: "#7F1D1D",
|
|
161
|
+
border: "1px solid #FECACA",
|
|
162
|
+
borderRadius: "16px",
|
|
163
|
+
boxShadow: "0 8px 24px rgba(0,0,0,0.05)",
|
|
164
|
+
fontFamily: "monospace",
|
|
165
|
+
maxWidth: "768px",
|
|
166
|
+
margin: "40px auto",
|
|
167
|
+
textAlign: "center"
|
|
168
|
+
},
|
|
169
|
+
heading: {
|
|
170
|
+
fontSize: "20px",
|
|
171
|
+
fontWeight: "bold",
|
|
172
|
+
marginBottom: "8px"
|
|
173
|
+
},
|
|
174
|
+
message: {
|
|
175
|
+
fontSize: "14px",
|
|
176
|
+
opacity: 0.85
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles.container, children: [
|
|
180
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.heading, children: "\u{1F6A8} An error occurred" }),
|
|
181
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles.message, children: "Something went wrong. Please try again later." })
|
|
182
|
+
] });
|
|
183
|
+
};
|
|
184
|
+
|
|
52
185
|
const RouterLayerContext = React.createContext(void 0);
|
|
53
186
|
|
|
54
187
|
const useRouterEvents = (opts = {}, deps = []) => {
|
|
@@ -145,15 +278,53 @@ const NestedView = (props) => {
|
|
|
145
278
|
return /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { fallback: app.context.onError, children: element });
|
|
146
279
|
};
|
|
147
280
|
|
|
281
|
+
function NotFoundPage() {
|
|
282
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
283
|
+
"div",
|
|
284
|
+
{
|
|
285
|
+
style: {
|
|
286
|
+
height: "100vh",
|
|
287
|
+
display: "flex",
|
|
288
|
+
flexDirection: "column",
|
|
289
|
+
justifyContent: "center",
|
|
290
|
+
alignItems: "center",
|
|
291
|
+
textAlign: "center",
|
|
292
|
+
fontFamily: "sans-serif",
|
|
293
|
+
padding: "1rem"
|
|
294
|
+
},
|
|
295
|
+
children: [
|
|
296
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "1rem", marginBottom: "0.5rem" }, children: "This page does not exist" }),
|
|
297
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
298
|
+
"a",
|
|
299
|
+
{
|
|
300
|
+
href: "/",
|
|
301
|
+
style: {
|
|
302
|
+
fontSize: "0.7rem",
|
|
303
|
+
color: "#007bff",
|
|
304
|
+
textDecoration: "none"
|
|
305
|
+
},
|
|
306
|
+
children: "\u2190 Back to home"
|
|
307
|
+
}
|
|
308
|
+
)
|
|
309
|
+
]
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
148
314
|
class RedirectionError extends Error {
|
|
315
|
+
page;
|
|
149
316
|
constructor(page) {
|
|
150
317
|
super("Redirection");
|
|
151
318
|
this.page = page;
|
|
152
319
|
}
|
|
153
320
|
}
|
|
154
321
|
|
|
322
|
+
const envSchema = core.t.object({
|
|
323
|
+
REACT_STRICT_MODE: core.t.boolean({ default: true })
|
|
324
|
+
});
|
|
155
325
|
class PageDescriptorProvider {
|
|
156
326
|
log = core.$logger();
|
|
327
|
+
env = core.$inject(envSchema);
|
|
157
328
|
alepha = core.$inject(core.Alepha);
|
|
158
329
|
pages = [];
|
|
159
330
|
getPages() {
|
|
@@ -167,8 +338,25 @@ class PageDescriptorProvider {
|
|
|
167
338
|
}
|
|
168
339
|
throw new Error(`Page ${name} not found`);
|
|
169
340
|
}
|
|
341
|
+
url(name, options = {}) {
|
|
342
|
+
const page = this.page(name);
|
|
343
|
+
if (!page) {
|
|
344
|
+
throw new Error(`Page ${name} not found`);
|
|
345
|
+
}
|
|
346
|
+
let url = page.path ?? "";
|
|
347
|
+
let parent = page.parent;
|
|
348
|
+
while (parent) {
|
|
349
|
+
url = `${parent.path ?? ""}/${url}`;
|
|
350
|
+
parent = parent.parent;
|
|
351
|
+
}
|
|
352
|
+
url = this.compile(url, options.params ?? {});
|
|
353
|
+
return new URL(
|
|
354
|
+
url.replace(/\/\/+/g, "/") || "/",
|
|
355
|
+
options.base ?? `http://localhost`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
170
358
|
root(state, context) {
|
|
171
|
-
|
|
359
|
+
const root = React.createElement(
|
|
172
360
|
RouterContext.Provider,
|
|
173
361
|
{
|
|
174
362
|
value: {
|
|
@@ -179,13 +367,17 @@ class PageDescriptorProvider {
|
|
|
179
367
|
},
|
|
180
368
|
React.createElement(NestedView, {}, state.layers[0]?.element)
|
|
181
369
|
);
|
|
370
|
+
if (this.env.REACT_STRICT_MODE) {
|
|
371
|
+
return React.createElement(React.StrictMode, {}, root);
|
|
372
|
+
}
|
|
373
|
+
return root;
|
|
182
374
|
}
|
|
183
375
|
async createLayers(route, request) {
|
|
184
376
|
const { pathname, search } = request.url;
|
|
185
377
|
const layers = [];
|
|
186
378
|
let context = {};
|
|
187
379
|
const stack = [{ route }];
|
|
188
|
-
|
|
380
|
+
request.onError = (error) => this.renderError(error);
|
|
189
381
|
let parent = route.parent;
|
|
190
382
|
while (parent) {
|
|
191
383
|
stack.unshift({ route: parent });
|
|
@@ -285,23 +477,26 @@ class PageDescriptorProvider {
|
|
|
285
477
|
const path = acc.replace(/\/+/, "/");
|
|
286
478
|
const localErrorHandler = this.getErrorHandler(it.route);
|
|
287
479
|
if (localErrorHandler) {
|
|
288
|
-
onError = localErrorHandler;
|
|
480
|
+
request.onError = localErrorHandler;
|
|
289
481
|
}
|
|
290
482
|
if (it.error) {
|
|
291
|
-
|
|
483
|
+
let element2 = await request.onError(it.error);
|
|
484
|
+
if (element2 === null) {
|
|
485
|
+
element2 = this.renderError(it.error);
|
|
486
|
+
}
|
|
292
487
|
layers.push({
|
|
293
488
|
props,
|
|
294
489
|
error: it.error,
|
|
295
490
|
name: it.route.name,
|
|
296
491
|
part: it.route.path,
|
|
297
492
|
config: it.config,
|
|
298
|
-
element: this.renderView(i + 1, path,
|
|
493
|
+
element: this.renderView(i + 1, path, element2, it.route),
|
|
299
494
|
index: i + 1,
|
|
300
495
|
path
|
|
301
496
|
});
|
|
302
497
|
break;
|
|
303
498
|
}
|
|
304
|
-
const
|
|
499
|
+
const element = await this.createElement(it.route, {
|
|
305
500
|
...props,
|
|
306
501
|
...context
|
|
307
502
|
});
|
|
@@ -310,7 +505,7 @@ class PageDescriptorProvider {
|
|
|
310
505
|
props,
|
|
311
506
|
part: it.route.path,
|
|
312
507
|
config: it.config,
|
|
313
|
-
element: this.renderView(i + 1, path,
|
|
508
|
+
element: this.renderView(i + 1, path, element, it.route),
|
|
314
509
|
index: i + 1,
|
|
315
510
|
path
|
|
316
511
|
});
|
|
@@ -366,8 +561,8 @@ class PageDescriptorProvider {
|
|
|
366
561
|
ctx.head.meta = [...ctx.head.meta ?? [], ...head.meta ?? []];
|
|
367
562
|
}
|
|
368
563
|
}
|
|
369
|
-
renderError(
|
|
370
|
-
return React.createElement(
|
|
564
|
+
renderError(error) {
|
|
565
|
+
return React.createElement(ErrorViewer, { error });
|
|
371
566
|
}
|
|
372
567
|
renderEmptyView() {
|
|
373
568
|
return React.createElement(NestedView, {});
|
|
@@ -392,7 +587,13 @@ class PageDescriptorProvider {
|
|
|
392
587
|
}
|
|
393
588
|
return path;
|
|
394
589
|
}
|
|
395
|
-
renderView(index, path, view
|
|
590
|
+
renderView(index, path, view, page) {
|
|
591
|
+
view ??= this.renderEmptyView();
|
|
592
|
+
const element = page.client ? React.createElement(
|
|
593
|
+
ClientOnly,
|
|
594
|
+
typeof page.client === "object" ? page.client : {},
|
|
595
|
+
view
|
|
596
|
+
) : view;
|
|
396
597
|
return React.createElement(
|
|
397
598
|
RouterLayerContext.Provider,
|
|
398
599
|
{
|
|
@@ -401,29 +602,41 @@ class PageDescriptorProvider {
|
|
|
401
602
|
path
|
|
402
603
|
}
|
|
403
604
|
},
|
|
404
|
-
|
|
605
|
+
element
|
|
405
606
|
);
|
|
406
607
|
}
|
|
407
608
|
configure = core.$hook({
|
|
408
609
|
name: "configure",
|
|
409
610
|
handler: () => {
|
|
611
|
+
let hasNotFoundHandler = false;
|
|
410
612
|
const pages = this.alepha.getDescriptorValues($page);
|
|
411
613
|
for (const { value, key } of pages) {
|
|
412
614
|
value[core.OPTIONS].name ??= key;
|
|
615
|
+
}
|
|
616
|
+
for (const { value } of pages) {
|
|
413
617
|
if (value[core.OPTIONS].parent) {
|
|
414
618
|
continue;
|
|
415
619
|
}
|
|
620
|
+
if (value[core.OPTIONS].path === "/*") {
|
|
621
|
+
hasNotFoundHandler = true;
|
|
622
|
+
}
|
|
416
623
|
this.add(this.map(pages, value));
|
|
417
624
|
}
|
|
625
|
+
if (!hasNotFoundHandler && pages.length > 0) {
|
|
626
|
+
this.add({
|
|
627
|
+
path: "/*",
|
|
628
|
+
name: "notFound",
|
|
629
|
+
cache: true,
|
|
630
|
+
component: NotFoundPage,
|
|
631
|
+
afterHandler: ({ reply }) => {
|
|
632
|
+
reply.status = 404;
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
}
|
|
418
636
|
}
|
|
419
637
|
});
|
|
420
638
|
map(pages, target) {
|
|
421
639
|
const children = target[core.OPTIONS].children ?? [];
|
|
422
|
-
for (const it of pages) {
|
|
423
|
-
if (it.value[core.OPTIONS].parent === target) {
|
|
424
|
-
children.push(it.value);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
640
|
return {
|
|
428
641
|
...target[core.OPTIONS],
|
|
429
642
|
parent: void 0,
|
|
@@ -572,7 +785,7 @@ class BrowserRouterProvider extends router.RouterProvider {
|
|
|
572
785
|
if (state.layers.length === 0) {
|
|
573
786
|
state.layers.push({
|
|
574
787
|
name: "not-found",
|
|
575
|
-
element:
|
|
788
|
+
element: React.createElement(NotFoundPage),
|
|
576
789
|
index: 0,
|
|
577
790
|
path: "/"
|
|
578
791
|
});
|
|
@@ -613,16 +826,12 @@ class BrowserRouterProvider extends router.RouterProvider {
|
|
|
613
826
|
}
|
|
614
827
|
}
|
|
615
828
|
|
|
616
|
-
const envSchema = core.t.object({
|
|
617
|
-
REACT_ROOT_ID: core.t.string({ default: "root" })
|
|
618
|
-
});
|
|
619
829
|
class ReactBrowserProvider {
|
|
620
830
|
log = core.$logger();
|
|
621
831
|
client = core.$inject(server.HttpClient);
|
|
622
832
|
alepha = core.$inject(core.Alepha);
|
|
623
833
|
router = core.$inject(BrowserRouterProvider);
|
|
624
834
|
headProvider = core.$inject(BrowserHeadProvider);
|
|
625
|
-
env = core.$inject(envSchema);
|
|
626
835
|
root;
|
|
627
836
|
transitioning;
|
|
628
837
|
state = {
|
|
@@ -660,11 +869,6 @@ class ReactBrowserProvider {
|
|
|
660
869
|
}
|
|
661
870
|
await this.render({ previous });
|
|
662
871
|
}
|
|
663
|
-
/**
|
|
664
|
-
*
|
|
665
|
-
* @param url
|
|
666
|
-
* @param options
|
|
667
|
-
*/
|
|
668
872
|
async go(url, options = {}) {
|
|
669
873
|
const result = await this.render({
|
|
670
874
|
url
|
|
@@ -698,8 +902,6 @@ class ReactBrowserProvider {
|
|
|
698
902
|
}
|
|
699
903
|
/**
|
|
700
904
|
* Get embedded layers from the server.
|
|
701
|
-
*
|
|
702
|
-
* @protected
|
|
703
905
|
*/
|
|
704
906
|
getHydrationState() {
|
|
705
907
|
try {
|
|
@@ -710,32 +912,14 @@ class ReactBrowserProvider {
|
|
|
710
912
|
console.error(error);
|
|
711
913
|
}
|
|
712
914
|
}
|
|
713
|
-
/**
|
|
714
|
-
*
|
|
715
|
-
* @protected
|
|
716
|
-
*/
|
|
717
|
-
getRootElement() {
|
|
718
|
-
const root = this.document.getElementById(this.env.REACT_ROOT_ID);
|
|
719
|
-
if (root) {
|
|
720
|
-
return root;
|
|
721
|
-
}
|
|
722
|
-
const div = this.document.createElement("div");
|
|
723
|
-
div.id = this.env.REACT_ROOT_ID;
|
|
724
|
-
this.document.body.prepend(div);
|
|
725
|
-
return div;
|
|
726
|
-
}
|
|
727
915
|
// -------------------------------------------------------------------------------------------------------------------
|
|
728
|
-
/**
|
|
729
|
-
*
|
|
730
|
-
* @protected
|
|
731
|
-
*/
|
|
732
916
|
ready = core.$hook({
|
|
733
917
|
name: "ready",
|
|
734
918
|
handler: async () => {
|
|
735
919
|
const hydration = this.getHydrationState();
|
|
736
920
|
const previous = hydration?.layers ?? [];
|
|
737
921
|
if (hydration?.links) {
|
|
738
|
-
for (const link of hydration.links) {
|
|
922
|
+
for (const link of hydration.links.links) {
|
|
739
923
|
this.client.pushLink(link);
|
|
740
924
|
}
|
|
741
925
|
}
|
|
@@ -748,15 +932,6 @@ class ReactBrowserProvider {
|
|
|
748
932
|
context,
|
|
749
933
|
hydration
|
|
750
934
|
});
|
|
751
|
-
const element = this.router.root(this.state, context);
|
|
752
|
-
if (previous.length > 0) {
|
|
753
|
-
this.root = client.hydrateRoot(this.getRootElement(), element);
|
|
754
|
-
this.log.info("Hydrated root element");
|
|
755
|
-
} else {
|
|
756
|
-
this.root ??= client.createRoot(this.getRootElement());
|
|
757
|
-
this.root.render(element);
|
|
758
|
-
this.log.info("Created root element");
|
|
759
|
-
}
|
|
760
935
|
window.addEventListener("popstate", () => {
|
|
761
936
|
this.render();
|
|
762
937
|
});
|
|
@@ -771,26 +946,18 @@ class ReactBrowserProvider {
|
|
|
771
946
|
}
|
|
772
947
|
|
|
773
948
|
class RouterHookApi {
|
|
774
|
-
constructor(state, layer, browser) {
|
|
949
|
+
constructor(pages, state, layer, browser) {
|
|
950
|
+
this.pages = pages;
|
|
775
951
|
this.state = state;
|
|
776
952
|
this.layer = layer;
|
|
777
953
|
this.browser = browser;
|
|
778
954
|
}
|
|
779
|
-
/**
|
|
780
|
-
*
|
|
781
|
-
*/
|
|
782
955
|
get current() {
|
|
783
956
|
return this.state;
|
|
784
957
|
}
|
|
785
|
-
/**
|
|
786
|
-
*
|
|
787
|
-
*/
|
|
788
958
|
get pathname() {
|
|
789
959
|
return this.state.pathname;
|
|
790
960
|
}
|
|
791
|
-
/**
|
|
792
|
-
*
|
|
793
|
-
*/
|
|
794
961
|
get query() {
|
|
795
962
|
const query = {};
|
|
796
963
|
for (const [key, value] of new URLSearchParams(
|
|
@@ -800,22 +967,12 @@ class RouterHookApi {
|
|
|
800
967
|
}
|
|
801
968
|
return query;
|
|
802
969
|
}
|
|
803
|
-
/**
|
|
804
|
-
*
|
|
805
|
-
*/
|
|
806
970
|
async back() {
|
|
807
971
|
this.browser?.history.back();
|
|
808
972
|
}
|
|
809
|
-
/**
|
|
810
|
-
*
|
|
811
|
-
*/
|
|
812
973
|
async forward() {
|
|
813
974
|
this.browser?.history.forward();
|
|
814
975
|
}
|
|
815
|
-
/**
|
|
816
|
-
*
|
|
817
|
-
* @param props
|
|
818
|
-
*/
|
|
819
976
|
async invalidate(props) {
|
|
820
977
|
await this.browser?.invalidate(props);
|
|
821
978
|
}
|
|
@@ -825,23 +982,40 @@ class RouterHookApi {
|
|
|
825
982
|
* @param pathname
|
|
826
983
|
* @param layer
|
|
827
984
|
*/
|
|
828
|
-
createHref(pathname, layer = this.layer) {
|
|
985
|
+
createHref(pathname, layer = this.layer, options = {}) {
|
|
829
986
|
if (typeof pathname === "object") {
|
|
830
987
|
pathname = pathname.options.path ?? "";
|
|
831
988
|
}
|
|
989
|
+
if (options.params) {
|
|
990
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
991
|
+
pathname = pathname.replace(`:${key}`, String(value));
|
|
992
|
+
}
|
|
993
|
+
}
|
|
832
994
|
return pathname.startsWith("/") ? pathname : `${layer.path}/${pathname}`.replace(/\/\/+/g, "/");
|
|
833
995
|
}
|
|
834
996
|
async go(path, options) {
|
|
835
|
-
|
|
997
|
+
for (const page of this.pages) {
|
|
998
|
+
if (page.name === path) {
|
|
999
|
+
path = page.path ?? "";
|
|
1000
|
+
break;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
await this.browser?.go(this.createHref(path, this.layer, options), options);
|
|
836
1004
|
}
|
|
837
|
-
anchor(path) {
|
|
838
|
-
const
|
|
1005
|
+
anchor(path, options = {}) {
|
|
1006
|
+
for (const page of this.pages) {
|
|
1007
|
+
if (page.name === path) {
|
|
1008
|
+
path = page.path ?? "";
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
const href = this.createHref(path, this.layer, options);
|
|
839
1013
|
return {
|
|
840
1014
|
href,
|
|
841
1015
|
onClick: (ev) => {
|
|
842
1016
|
ev.stopPropagation();
|
|
843
1017
|
ev.preventDefault();
|
|
844
|
-
this.go(path).catch(console.error);
|
|
1018
|
+
this.go(path, options).catch(console.error);
|
|
845
1019
|
}
|
|
846
1020
|
};
|
|
847
1021
|
}
|
|
@@ -869,8 +1043,12 @@ const useRouter = () => {
|
|
|
869
1043
|
if (!ctx || !layer) {
|
|
870
1044
|
throw new Error("useRouter must be used within a RouterProvider");
|
|
871
1045
|
}
|
|
1046
|
+
const pages = React.useMemo(() => {
|
|
1047
|
+
return ctx.alepha.get(PageDescriptorProvider).getPages();
|
|
1048
|
+
}, []);
|
|
872
1049
|
return React.useMemo(
|
|
873
1050
|
() => new RouterHookApi(
|
|
1051
|
+
pages,
|
|
874
1052
|
ctx.state,
|
|
875
1053
|
layer,
|
|
876
1054
|
ctx.alepha.isBrowser() ? ctx.alepha.get(ReactBrowserProvider) : void 0
|
|
@@ -881,6 +1059,7 @@ const useRouter = () => {
|
|
|
881
1059
|
|
|
882
1060
|
const Link = (props) => {
|
|
883
1061
|
React.useContext(RouterContext);
|
|
1062
|
+
const router = useRouter();
|
|
884
1063
|
const to = typeof props.to === "string" ? props.to : props.to[core.OPTIONS].path;
|
|
885
1064
|
if (!to) {
|
|
886
1065
|
return null;
|
|
@@ -890,8 +1069,49 @@ const Link = (props) => {
|
|
|
890
1069
|
return null;
|
|
891
1070
|
}
|
|
892
1071
|
const name = typeof props.to === "string" ? void 0 : props.to[core.OPTIONS].name;
|
|
1072
|
+
const anchorProps = {
|
|
1073
|
+
...props,
|
|
1074
|
+
to: void 0
|
|
1075
|
+
};
|
|
1076
|
+
return /* @__PURE__ */ jsxRuntime.jsx("a", { ...router.anchor(to), ...anchorProps, children: props.children ?? name });
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
const useActive = (path) => {
|
|
893
1080
|
const router = useRouter();
|
|
894
|
-
|
|
1081
|
+
const ctx = React.useContext(RouterContext);
|
|
1082
|
+
const layer = React.useContext(RouterLayerContext);
|
|
1083
|
+
if (!ctx || !layer) {
|
|
1084
|
+
throw new Error("useRouter must be used within a RouterProvider");
|
|
1085
|
+
}
|
|
1086
|
+
let name;
|
|
1087
|
+
if (typeof path === "object" && path.options.name) {
|
|
1088
|
+
name = path.options.name;
|
|
1089
|
+
}
|
|
1090
|
+
const [current, setCurrent] = React.useState(ctx.state.pathname);
|
|
1091
|
+
const href = React.useMemo(() => router.createHref(path, layer), [path, layer]);
|
|
1092
|
+
const [isPending, setPending] = React.useState(false);
|
|
1093
|
+
const isActive = current === href;
|
|
1094
|
+
useRouterEvents({
|
|
1095
|
+
onEnd: ({ state }) => setCurrent(state.pathname)
|
|
1096
|
+
});
|
|
1097
|
+
return {
|
|
1098
|
+
name,
|
|
1099
|
+
isPending,
|
|
1100
|
+
isActive,
|
|
1101
|
+
anchorProps: {
|
|
1102
|
+
href,
|
|
1103
|
+
onClick: (ev) => {
|
|
1104
|
+
ev.stopPropagation();
|
|
1105
|
+
ev.preventDefault();
|
|
1106
|
+
if (isActive) return;
|
|
1107
|
+
if (isPending) return;
|
|
1108
|
+
setPending(true);
|
|
1109
|
+
router.go(href).then(() => {
|
|
1110
|
+
setPending(false);
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
895
1115
|
};
|
|
896
1116
|
|
|
897
1117
|
const useInject = (clazz) => {
|
|
@@ -902,10 +1122,7 @@ const useInject = (clazz) => {
|
|
|
902
1122
|
return React.useMemo(() => ctx.alepha.get(clazz), []);
|
|
903
1123
|
};
|
|
904
1124
|
|
|
905
|
-
const useClient = () => {
|
|
906
|
-
return useInject(server.HttpClient);
|
|
907
|
-
};
|
|
908
|
-
const useApi = () => {
|
|
1125
|
+
const useClient = (_scope) => {
|
|
909
1126
|
return useInject(server.HttpClient).of();
|
|
910
1127
|
};
|
|
911
1128
|
|
|
@@ -939,7 +1156,7 @@ const encode = (alepha, schema, data) => {
|
|
|
939
1156
|
const decode = (alepha, schema, data) => {
|
|
940
1157
|
try {
|
|
941
1158
|
return alepha.parse(schema, JSON.parse(atob(decodeURIComponent(data))));
|
|
942
|
-
} catch (
|
|
1159
|
+
} catch (_error) {
|
|
943
1160
|
return {};
|
|
944
1161
|
}
|
|
945
1162
|
};
|
|
@@ -957,46 +1174,9 @@ const useRouterState = () => {
|
|
|
957
1174
|
return state;
|
|
958
1175
|
};
|
|
959
1176
|
|
|
960
|
-
const useActive = (path) => {
|
|
961
|
-
const router = useRouter();
|
|
962
|
-
const ctx = React.useContext(RouterContext);
|
|
963
|
-
const layer = React.useContext(RouterLayerContext);
|
|
964
|
-
if (!ctx || !layer) {
|
|
965
|
-
throw new Error("useRouter must be used within a RouterProvider");
|
|
966
|
-
}
|
|
967
|
-
let name;
|
|
968
|
-
if (typeof path === "object" && path.options.name) {
|
|
969
|
-
name = path.options.name;
|
|
970
|
-
}
|
|
971
|
-
const [current, setCurrent] = React.useState(ctx.state.pathname);
|
|
972
|
-
const href = React.useMemo(() => router.createHref(path, layer), [path, layer]);
|
|
973
|
-
const [isPending, setPending] = React.useState(false);
|
|
974
|
-
const isActive = current === href;
|
|
975
|
-
useRouterEvents({
|
|
976
|
-
onEnd: ({ state }) => setCurrent(state.pathname)
|
|
977
|
-
});
|
|
978
|
-
return {
|
|
979
|
-
name,
|
|
980
|
-
isPending,
|
|
981
|
-
isActive,
|
|
982
|
-
anchorProps: {
|
|
983
|
-
href,
|
|
984
|
-
onClick: (ev) => {
|
|
985
|
-
ev.stopPropagation();
|
|
986
|
-
ev.preventDefault();
|
|
987
|
-
if (isActive) return;
|
|
988
|
-
if (isPending) return;
|
|
989
|
-
setPending(true);
|
|
990
|
-
router.go(href).then(() => {
|
|
991
|
-
setPending(false);
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
};
|
|
996
|
-
};
|
|
997
|
-
|
|
998
1177
|
exports.$page = $page;
|
|
999
1178
|
exports.BrowserRouterProvider = BrowserRouterProvider;
|
|
1179
|
+
exports.ClientOnly = ClientOnly;
|
|
1000
1180
|
exports.ErrorBoundary = ErrorBoundary;
|
|
1001
1181
|
exports.Link = Link;
|
|
1002
1182
|
exports.NestedView = NestedView;
|
|
@@ -1008,11 +1188,10 @@ exports.RouterHookApi = RouterHookApi;
|
|
|
1008
1188
|
exports.RouterLayerContext = RouterLayerContext;
|
|
1009
1189
|
exports.isPageRoute = isPageRoute;
|
|
1010
1190
|
exports.useActive = useActive;
|
|
1011
|
-
exports.
|
|
1191
|
+
exports.useAlepha = useAlepha;
|
|
1012
1192
|
exports.useClient = useClient;
|
|
1013
1193
|
exports.useInject = useInject;
|
|
1014
1194
|
exports.useQueryParams = useQueryParams;
|
|
1015
1195
|
exports.useRouter = useRouter;
|
|
1016
1196
|
exports.useRouterEvents = useRouterEvents;
|
|
1017
1197
|
exports.useRouterState = useRouterState;
|
|
1018
|
-
//# sourceMappingURL=useActive-DjpZBEuB.cjs.map
|