@alepha/react 0.7.4 → 0.7.5

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.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { t, $logger, $inject, Alepha, $hook, OPTIONS, __bind } from '@alepha/core';
2
- import { ServerRouterProvider, ServerTimingProvider, ServerLinksProvider, apiLinksResponseSchema, HttpClient, AlephaServer } from '@alepha/server';
2
+ import { ServerRouterProvider, ServerTimingProvider, ServerLinksProvider, apiLinksResponseSchema, AlephaServer } from '@alepha/server';
3
3
  import { AlephaServerCache } from '@alepha/server-cache';
4
- import { P as PageDescriptorProvider, $ as $page, R as RouterContext, a as RouterLayerContext, b as ReactBrowserProvider, u as useRouterEvents } from './ReactBrowserProvider-ufHSOTmv.js';
5
- export { C as ClientOnly, E as ErrorBoundary, N as NestedView, c as RedirectionError, i as isPageRoute, d as useAlepha } from './ReactBrowserProvider-ufHSOTmv.js';
4
+ import { P as PageDescriptorProvider, $ as $page } from './useRouterState-cCucJfTC.js';
5
+ export { C as ClientOnly, E as ErrorBoundary, L as Link, N as NestedView, l as ReactBrowserProvider, b as RedirectionError, R as RouterContext, c as RouterHookApi, a as RouterLayerContext, k as isPageRoute, u as useActive, d as useAlepha, e as useClient, f as useInject, g as useQueryParams, h as useRouter, i as useRouterEvents, j as useRouterState } from './useRouterState-cCucJfTC.js';
6
6
  import { existsSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { ServerStaticProvider } from '@alepha/server-static';
9
9
  import { renderToString } from 'react-dom/server';
10
- import { jsx } from 'react/jsx-runtime';
11
- import React, { useContext, useMemo, useState, useEffect } from 'react';
10
+ import 'react/jsx-runtime';
11
+ import 'react';
12
12
  import '@alepha/router';
13
13
 
14
14
  class ServerHeadProvider {
@@ -353,239 +353,10 @@ class ReactServerProvider {
353
353
  }
354
354
  }
355
355
 
356
- class RouterHookApi {
357
- constructor(pages, state, layer, browser) {
358
- this.pages = pages;
359
- this.state = state;
360
- this.layer = layer;
361
- this.browser = browser;
362
- }
363
- get current() {
364
- return this.state;
365
- }
366
- get pathname() {
367
- return this.state.pathname;
368
- }
369
- get query() {
370
- const query = {};
371
- for (const [key, value] of new URLSearchParams(
372
- this.state.search
373
- ).entries()) {
374
- query[key] = String(value);
375
- }
376
- return query;
377
- }
378
- async back() {
379
- this.browser?.history.back();
380
- }
381
- async forward() {
382
- this.browser?.history.forward();
383
- }
384
- async invalidate(props) {
385
- await this.browser?.invalidate(props);
386
- }
387
- /**
388
- * Create a valid href for the given pathname.
389
- *
390
- * @param pathname
391
- * @param layer
392
- */
393
- createHref(pathname, layer = this.layer, options = {}) {
394
- if (typeof pathname === "object") {
395
- pathname = pathname.options.path ?? "";
396
- }
397
- if (options.params) {
398
- for (const [key, value] of Object.entries(options.params)) {
399
- pathname = pathname.replace(`:${key}`, String(value));
400
- }
401
- }
402
- return pathname.startsWith("/") ? pathname : `${layer.path}/${pathname}`.replace(/\/\/+/g, "/");
403
- }
404
- async go(path, options) {
405
- for (const page of this.pages) {
406
- if (page.name === path) {
407
- path = page.path ?? "";
408
- break;
409
- }
410
- }
411
- await this.browser?.go(this.createHref(path, this.layer, options), options);
412
- }
413
- anchor(path, options = {}) {
414
- for (const page of this.pages) {
415
- if (page.name === path) {
416
- path = page.path ?? "";
417
- break;
418
- }
419
- }
420
- const href = this.createHref(path, this.layer, options);
421
- return {
422
- href,
423
- onClick: (ev) => {
424
- ev.stopPropagation();
425
- ev.preventDefault();
426
- this.go(path, options).catch(console.error);
427
- }
428
- };
429
- }
430
- /**
431
- * Set query params.
432
- *
433
- * @param record
434
- * @param options
435
- */
436
- setQueryParams(record, options = {}) {
437
- const func = typeof record === "function" ? record : () => record;
438
- const search = new URLSearchParams(func(this.query)).toString();
439
- const state = search ? `${this.pathname}?${search}` : this.pathname;
440
- if (options.push) {
441
- window.history.pushState({}, "", state);
442
- } else {
443
- window.history.replaceState({}, "", state);
444
- }
445
- }
446
- }
447
-
448
- const useRouter = () => {
449
- const ctx = useContext(RouterContext);
450
- const layer = useContext(RouterLayerContext);
451
- if (!ctx || !layer) {
452
- throw new Error("useRouter must be used within a RouterProvider");
453
- }
454
- const pages = useMemo(() => {
455
- return ctx.alepha.get(PageDescriptorProvider).getPages();
456
- }, []);
457
- return useMemo(
458
- () => new RouterHookApi(
459
- pages,
460
- ctx.state,
461
- layer,
462
- ctx.alepha.isBrowser() ? ctx.alepha.get(ReactBrowserProvider) : void 0
463
- ),
464
- [layer]
465
- );
466
- };
467
-
468
- const Link = (props) => {
469
- React.useContext(RouterContext);
470
- const router = useRouter();
471
- const to = typeof props.to === "string" ? props.to : props.to[OPTIONS].path;
472
- if (!to) {
473
- return null;
474
- }
475
- const can = typeof props.to === "string" ? void 0 : props.to[OPTIONS].can;
476
- if (can && !can()) {
477
- return null;
478
- }
479
- const name = typeof props.to === "string" ? void 0 : props.to[OPTIONS].name;
480
- const anchorProps = {
481
- ...props,
482
- to: void 0
483
- };
484
- return /* @__PURE__ */ jsx("a", { ...router.anchor(to), ...anchorProps, children: props.children ?? name });
485
- };
486
-
487
- const useActive = (path) => {
488
- const router = useRouter();
489
- const ctx = useContext(RouterContext);
490
- const layer = useContext(RouterLayerContext);
491
- if (!ctx || !layer) {
492
- throw new Error("useRouter must be used within a RouterProvider");
493
- }
494
- let name;
495
- if (typeof path === "object" && path.options.name) {
496
- name = path.options.name;
497
- }
498
- const [current, setCurrent] = useState(ctx.state.pathname);
499
- const href = useMemo(() => router.createHref(path, layer), [path, layer]);
500
- const [isPending, setPending] = useState(false);
501
- const isActive = current === href;
502
- useRouterEvents({
503
- onEnd: ({ state }) => setCurrent(state.pathname)
504
- });
505
- return {
506
- name,
507
- isPending,
508
- isActive,
509
- anchorProps: {
510
- href,
511
- onClick: (ev) => {
512
- ev.stopPropagation();
513
- ev.preventDefault();
514
- if (isActive) return;
515
- if (isPending) return;
516
- setPending(true);
517
- router.go(href).then(() => {
518
- setPending(false);
519
- });
520
- }
521
- }
522
- };
523
- };
524
-
525
- const useInject = (clazz) => {
526
- const ctx = useContext(RouterContext);
527
- if (!ctx) {
528
- throw new Error("useRouter must be used within a <RouterProvider>");
529
- }
530
- return useMemo(() => ctx.alepha.get(clazz), []);
531
- };
532
-
533
- const useClient = (_scope) => {
534
- return useInject(HttpClient).of();
535
- };
536
-
537
- const useQueryParams = (schema, options = {}) => {
538
- const ctx = useContext(RouterContext);
539
- if (!ctx) {
540
- throw new Error("useQueryParams must be used within a RouterProvider");
541
- }
542
- const key = options.key ?? "q";
543
- const router = useRouter();
544
- const querystring = router.query[key];
545
- const [queryParams, setQueryParams] = useState(
546
- decode(ctx.alepha, schema, router.query[key])
547
- );
548
- useEffect(() => {
549
- setQueryParams(decode(ctx.alepha, schema, querystring));
550
- }, [querystring]);
551
- return [
552
- queryParams,
553
- (queryParams2) => {
554
- setQueryParams(queryParams2);
555
- router.setQueryParams((data) => {
556
- return { ...data, [key]: encode(ctx.alepha, schema, queryParams2) };
557
- });
558
- }
559
- ];
560
- };
561
- const encode = (alepha, schema, data) => {
562
- return btoa(JSON.stringify(alepha.parse(schema, data)));
563
- };
564
- const decode = (alepha, schema, data) => {
565
- try {
566
- return alepha.parse(schema, JSON.parse(atob(decodeURIComponent(data))));
567
- } catch (_error) {
568
- return {};
569
- }
570
- };
571
-
572
- const useRouterState = () => {
573
- const ctx = useContext(RouterContext);
574
- const layer = useContext(RouterLayerContext);
575
- if (!ctx || !layer) {
576
- throw new Error("useRouter must be used within a RouterProvider");
577
- }
578
- const [state, setState] = useState(ctx.state);
579
- useRouterEvents({
580
- onEnd: ({ state: state2 }) => setState({ ...state2 })
581
- });
582
- return state;
583
- };
584
-
585
356
  class AlephaReact {
586
357
  name = "alepha.react";
587
358
  $services = (alepha) => alepha.with(AlephaServer).with(AlephaServerCache).with(ReactServerProvider).with(PageDescriptorProvider);
588
359
  }
589
360
  __bind($page, AlephaReact);
590
361
 
591
- export { $page, AlephaReact, Link, PageDescriptorProvider, ReactBrowserProvider, ReactServerProvider, RouterContext, RouterHookApi, RouterLayerContext, useActive, useClient, useInject, useQueryParams, useRouter, useRouterEvents, useRouterState };
362
+ export { $page, AlephaReact, PageDescriptorProvider, ReactServerProvider };
@@ -1,10 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ var jsxRuntime = require('react/jsx-runtime');
3
4
  var core = require('@alepha/core');
5
+ var React = require('react');
4
6
  var server = require('@alepha/server');
5
7
  var router = require('@alepha/router');
6
- var React = require('react');
7
- var jsxRuntime = require('react/jsx-runtime');
8
8
 
9
9
  const KEY = "PAGE";
10
10
  const $page = (options) => {
@@ -279,7 +279,7 @@ const NestedView = (props) => {
279
279
  };
280
280
 
281
281
  function NotFoundPage() {
282
- return /* @__PURE__ */ jsxRuntime.jsxs(
282
+ return /* @__PURE__ */ jsxRuntime.jsx(
283
283
  "div",
284
284
  {
285
285
  style: {
@@ -292,21 +292,7 @@ function NotFoundPage() {
292
292
  fontFamily: "sans-serif",
293
293
  padding: "1rem"
294
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
- ]
295
+ children: /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "1rem", marginBottom: "0.5rem" }, children: "This page does not exist" })
310
296
  }
311
297
  );
312
298
  }
@@ -945,16 +931,253 @@ class ReactBrowserProvider {
945
931
  });
946
932
  }
947
933
 
934
+ class RouterHookApi {
935
+ constructor(pages, state, layer, browser) {
936
+ this.pages = pages;
937
+ this.state = state;
938
+ this.layer = layer;
939
+ this.browser = browser;
940
+ }
941
+ get current() {
942
+ return this.state;
943
+ }
944
+ get pathname() {
945
+ return this.state.pathname;
946
+ }
947
+ get query() {
948
+ const query = {};
949
+ for (const [key, value] of new URLSearchParams(
950
+ this.state.search
951
+ ).entries()) {
952
+ query[key] = String(value);
953
+ }
954
+ return query;
955
+ }
956
+ async back() {
957
+ this.browser?.history.back();
958
+ }
959
+ async forward() {
960
+ this.browser?.history.forward();
961
+ }
962
+ async invalidate(props) {
963
+ await this.browser?.invalidate(props);
964
+ }
965
+ /**
966
+ * Create a valid href for the given pathname.
967
+ *
968
+ * @param pathname
969
+ * @param layer
970
+ */
971
+ createHref(pathname, layer = this.layer, options = {}) {
972
+ if (typeof pathname === "object") {
973
+ pathname = pathname.options.path ?? "";
974
+ }
975
+ if (options.params) {
976
+ for (const [key, value] of Object.entries(options.params)) {
977
+ pathname = pathname.replace(`:${key}`, String(value));
978
+ }
979
+ }
980
+ return pathname.startsWith("/") ? pathname : `${layer.path}/${pathname}`.replace(/\/\/+/g, "/");
981
+ }
982
+ async go(path, options) {
983
+ for (const page of this.pages) {
984
+ if (page.name === path) {
985
+ path = page.path ?? "";
986
+ break;
987
+ }
988
+ }
989
+ await this.browser?.go(this.createHref(path, this.layer, options), options);
990
+ }
991
+ anchor(path, options = {}) {
992
+ for (const page of this.pages) {
993
+ if (page.name === path) {
994
+ path = page.path ?? "";
995
+ break;
996
+ }
997
+ }
998
+ const href = this.createHref(path, this.layer, options);
999
+ return {
1000
+ href,
1001
+ onClick: (ev) => {
1002
+ ev.stopPropagation();
1003
+ ev.preventDefault();
1004
+ this.go(path, options).catch(console.error);
1005
+ }
1006
+ };
1007
+ }
1008
+ /**
1009
+ * Set query params.
1010
+ *
1011
+ * @param record
1012
+ * @param options
1013
+ */
1014
+ setQueryParams(record, options = {}) {
1015
+ const func = typeof record === "function" ? record : () => record;
1016
+ const search = new URLSearchParams(func(this.query)).toString();
1017
+ const state = search ? `${this.pathname}?${search}` : this.pathname;
1018
+ if (options.push) {
1019
+ window.history.pushState({}, "", state);
1020
+ } else {
1021
+ window.history.replaceState({}, "", state);
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+ const useRouter = () => {
1027
+ const ctx = React.useContext(RouterContext);
1028
+ const layer = React.useContext(RouterLayerContext);
1029
+ if (!ctx || !layer) {
1030
+ throw new Error("useRouter must be used within a RouterProvider");
1031
+ }
1032
+ const pages = React.useMemo(() => {
1033
+ return ctx.alepha.get(PageDescriptorProvider).getPages();
1034
+ }, []);
1035
+ return React.useMemo(
1036
+ () => new RouterHookApi(
1037
+ pages,
1038
+ ctx.state,
1039
+ layer,
1040
+ ctx.alepha.isBrowser() ? ctx.alepha.get(ReactBrowserProvider) : void 0
1041
+ ),
1042
+ [layer]
1043
+ );
1044
+ };
1045
+
1046
+ const Link = (props) => {
1047
+ React.useContext(RouterContext);
1048
+ const router = useRouter();
1049
+ const to = typeof props.to === "string" ? props.to : props.to[core.OPTIONS].path;
1050
+ if (!to) {
1051
+ return null;
1052
+ }
1053
+ const can = typeof props.to === "string" ? void 0 : props.to[core.OPTIONS].can;
1054
+ if (can && !can()) {
1055
+ return null;
1056
+ }
1057
+ const name = typeof props.to === "string" ? void 0 : props.to[core.OPTIONS].name;
1058
+ const anchorProps = {
1059
+ ...props,
1060
+ to: void 0
1061
+ };
1062
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { ...router.anchor(to), ...anchorProps, children: props.children ?? name });
1063
+ };
1064
+
1065
+ const useActive = (path) => {
1066
+ const router = useRouter();
1067
+ const ctx = React.useContext(RouterContext);
1068
+ const layer = React.useContext(RouterLayerContext);
1069
+ if (!ctx || !layer) {
1070
+ throw new Error("useRouter must be used within a RouterProvider");
1071
+ }
1072
+ let name;
1073
+ if (typeof path === "object" && path.options.name) {
1074
+ name = path.options.name;
1075
+ }
1076
+ const [current, setCurrent] = React.useState(ctx.state.pathname);
1077
+ const href = React.useMemo(() => router.createHref(path, layer), [path, layer]);
1078
+ const [isPending, setPending] = React.useState(false);
1079
+ const isActive = current === href;
1080
+ useRouterEvents({
1081
+ onEnd: ({ state }) => setCurrent(state.pathname)
1082
+ });
1083
+ return {
1084
+ name,
1085
+ isPending,
1086
+ isActive,
1087
+ anchorProps: {
1088
+ href,
1089
+ onClick: (ev) => {
1090
+ ev.stopPropagation();
1091
+ ev.preventDefault();
1092
+ if (isActive) return;
1093
+ if (isPending) return;
1094
+ setPending(true);
1095
+ router.go(href).then(() => {
1096
+ setPending(false);
1097
+ });
1098
+ }
1099
+ }
1100
+ };
1101
+ };
1102
+
1103
+ const useInject = (clazz) => {
1104
+ const ctx = React.useContext(RouterContext);
1105
+ if (!ctx) {
1106
+ throw new Error("useRouter must be used within a <RouterProvider>");
1107
+ }
1108
+ return React.useMemo(() => ctx.alepha.get(clazz), []);
1109
+ };
1110
+
1111
+ const useClient = (_scope) => {
1112
+ return useInject(server.HttpClient).of();
1113
+ };
1114
+
1115
+ const useQueryParams = (schema, options = {}) => {
1116
+ const ctx = React.useContext(RouterContext);
1117
+ if (!ctx) {
1118
+ throw new Error("useQueryParams must be used within a RouterProvider");
1119
+ }
1120
+ const key = options.key ?? "q";
1121
+ const router = useRouter();
1122
+ const querystring = router.query[key];
1123
+ const [queryParams, setQueryParams] = React.useState(
1124
+ decode(ctx.alepha, schema, router.query[key])
1125
+ );
1126
+ React.useEffect(() => {
1127
+ setQueryParams(decode(ctx.alepha, schema, querystring));
1128
+ }, [querystring]);
1129
+ return [
1130
+ queryParams,
1131
+ (queryParams2) => {
1132
+ setQueryParams(queryParams2);
1133
+ router.setQueryParams((data) => {
1134
+ return { ...data, [key]: encode(ctx.alepha, schema, queryParams2) };
1135
+ });
1136
+ }
1137
+ ];
1138
+ };
1139
+ const encode = (alepha, schema, data) => {
1140
+ return btoa(JSON.stringify(alepha.parse(schema, data)));
1141
+ };
1142
+ const decode = (alepha, schema, data) => {
1143
+ try {
1144
+ return alepha.parse(schema, JSON.parse(atob(decodeURIComponent(data))));
1145
+ } catch (_error) {
1146
+ return {};
1147
+ }
1148
+ };
1149
+
1150
+ const useRouterState = () => {
1151
+ const ctx = React.useContext(RouterContext);
1152
+ const layer = React.useContext(RouterLayerContext);
1153
+ if (!ctx || !layer) {
1154
+ throw new Error("useRouter must be used within a RouterProvider");
1155
+ }
1156
+ const [state, setState] = React.useState(ctx.state);
1157
+ useRouterEvents({
1158
+ onEnd: ({ state: state2 }) => setState({ ...state2 })
1159
+ });
1160
+ return state;
1161
+ };
1162
+
948
1163
  exports.$page = $page;
949
1164
  exports.BrowserRouterProvider = BrowserRouterProvider;
950
1165
  exports.ClientOnly = ClientOnly;
951
1166
  exports.ErrorBoundary = ErrorBoundary;
1167
+ exports.Link = Link;
952
1168
  exports.NestedView = NestedView;
953
1169
  exports.PageDescriptorProvider = PageDescriptorProvider;
954
1170
  exports.ReactBrowserProvider = ReactBrowserProvider;
955
1171
  exports.RedirectionError = RedirectionError;
956
1172
  exports.RouterContext = RouterContext;
1173
+ exports.RouterHookApi = RouterHookApi;
957
1174
  exports.RouterLayerContext = RouterLayerContext;
958
1175
  exports.isPageRoute = isPageRoute;
1176
+ exports.useActive = useActive;
959
1177
  exports.useAlepha = useAlepha;
1178
+ exports.useClient = useClient;
1179
+ exports.useInject = useInject;
1180
+ exports.useQueryParams = useQueryParams;
1181
+ exports.useRouter = useRouter;
960
1182
  exports.useRouterEvents = useRouterEvents;
1183
+ exports.useRouterState = useRouterState;