silentium-components 0.0.77 → 0.0.78

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.
@@ -0,0 +1,54 @@
1
+ import {
2
+ Applied,
3
+ Late,
4
+ Of,
5
+ Shared,
6
+ Transport,
7
+ TransportEvent,
8
+ } from "silentium";
9
+ import { Router } from "../navigation/Router";
10
+ import { expect, test, vi } from "vitest";
11
+
12
+ const drop = (dropPart: string) => (value: string) => {
13
+ return value.replace(dropPart, "");
14
+ };
15
+
16
+ test("Router first matching route responds, not subsequent matches", () => {
17
+ const $url = Late<string>("http://domain.com/page/general");
18
+ const $urlPath = Shared(Applied($url, drop("http://domain.com")));
19
+ const g = vi.fn();
20
+ $urlPath.event(Transport(g));
21
+
22
+ const firstRouteMock = vi.fn(() => Of("first-route-response"));
23
+ const secondRouteMock = vi.fn(() => Of("second-route-response"));
24
+
25
+ const $router = Router(
26
+ $urlPath,
27
+ Of([
28
+ {
29
+ pattern: "^/page/.*", // This matches /page/anything
30
+ event: TransportEvent(firstRouteMock),
31
+ },
32
+ {
33
+ pattern: "^/page/specific$", // This also matches /page/specific
34
+ event: TransportEvent(secondRouteMock),
35
+ },
36
+ ]),
37
+ TransportEvent(() => Of<string>("page/404.html")),
38
+ );
39
+ const g2 = vi.fn();
40
+ $router.event(Transport(g2));
41
+
42
+ // Initial URL should match first route
43
+ expect(g2).toHaveBeenLastCalledWith("first-route-response");
44
+ expect(firstRouteMock).toHaveBeenCalledTimes(1);
45
+ expect(secondRouteMock).toHaveBeenCalledTimes(0);
46
+
47
+ // Change URL to /page/specific, which matches both patterns
48
+ // But only the first matching route should respond
49
+ $url.use("http://domain.com/page/specific");
50
+
51
+ expect(g2).toHaveBeenLastCalledWith("first-route-response");
52
+ expect(firstRouteMock).toHaveBeenCalledTimes(2); // Called again
53
+ expect(secondRouteMock).toHaveBeenCalledTimes(0); // Never called
54
+ });
@@ -1,26 +1,21 @@
1
1
  import {
2
2
  All,
3
- Applied,
4
3
  DestroyableType,
5
4
  Event,
6
5
  EventType,
6
+ isDestroyable,
7
7
  Of,
8
8
  Transport,
9
- TransportDestroyable,
10
- TransportEvent,
11
9
  TransportType,
12
10
  } from "silentium";
13
- import { BranchLazy } from "../behaviors";
14
11
  import { RegexpMatched } from "../system";
15
12
 
16
13
  export interface Route<T> {
17
14
  pattern: string;
18
15
  patternFlags?: string;
19
- event: TransportType<[], EventType<T>>;
16
+ event: TransportType<void, EventType<T>>;
20
17
  }
21
18
 
22
- const $empty = TransportEvent(() => Of(false));
23
-
24
19
  /**
25
20
  * Router component what will return template if url matches pattern
26
21
  * https://silentium-lab.github.io/silentium-components/#/navigation/router
@@ -31,41 +26,45 @@ export function Router<T = "string">(
31
26
  $default: TransportType<void, EventType<T>>,
32
27
  ): EventType<T> & DestroyableType {
33
28
  return Event<T>((transport) => {
34
- const destructors: DestroyableType[] = [];
29
+ const destroyableList: DestroyableType[] = [];
30
+ const checkDestroyable = (instance: unknown) => {
31
+ if (isDestroyable(instance)) {
32
+ destroyableList.push(instance);
33
+ }
34
+ };
35
35
  const destructor = () => {
36
- destructors.forEach((d) => d.destroy());
37
- destructors.length = 0;
36
+ destroyableList.forEach((d) => d.destroy());
37
+ destroyableList.length = 0;
38
38
  };
39
39
  All($routes, $url).event(
40
40
  Transport(([routes, url]) => {
41
41
  destructor();
42
- const instance = All(
43
- $default.use(),
44
- All(
45
- ...routes.map((r) => {
46
- const $template = TransportDestroyable(r.event);
47
- destructors.push($template);
48
- return BranchLazy(
49
- RegexpMatched(
50
- Of(r.pattern),
51
- Of(url),
52
- r.patternFlags ? Of(r.patternFlags) : undefined,
53
- ),
54
- $template,
55
- $empty,
56
- );
57
- }),
42
+ const $matches = All(
43
+ ...routes.map((r) =>
44
+ RegexpMatched(
45
+ Of(r.pattern),
46
+ Of(url),
47
+ r.patternFlags ? Of(r.patternFlags) : undefined,
48
+ ),
58
49
  ),
59
50
  );
51
+ $matches.event(
52
+ Transport((matches) => {
53
+ const index = matches.findIndex((v) => v === true);
60
54
 
61
- // Return first not false or default
62
- Applied(instance, (r) => {
63
- const first = r[1].find((r: unknown) => r !== false);
64
- if (first) {
65
- return first as T;
66
- }
67
- return r[0];
68
- }).event(transport);
55
+ if (index === -1) {
56
+ const instance = $default.use();
57
+ checkDestroyable(instance);
58
+ instance.event(transport);
59
+ }
60
+
61
+ if (index > -1) {
62
+ const instance = routes[index].event.use();
63
+ checkDestroyable(instance);
64
+ instance.event(transport);
65
+ }
66
+ }),
67
+ );
69
68
  }),
70
69
  );
71
70