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.
- package/CHANGELOG.md +7 -0
- package/dist/silentium-components.cjs +30 -27
- package/dist/silentium-components.cjs.map +1 -1
- package/dist/silentium-components.d.ts +1 -1
- package/dist/silentium-components.js +31 -28
- package/dist/silentium-components.js.map +1 -1
- package/dist/silentium-components.min.js +1 -1
- package/dist/silentium-components.min.mjs +1 -1
- package/dist/silentium-components.min.mjs.map +1 -1
- package/dist/silentium-components.mjs +31 -28
- package/dist/silentium-components.mjs.map +1 -1
- package/package.json +1 -1
- package/src/navigation/Router._firstMatch.test.ts +54 -0
- package/src/navigation/Router.ts +33 -34
|
@@ -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
|
+
});
|
package/src/navigation/Router.ts
CHANGED
|
@@ -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<
|
|
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
|
|
29
|
+
const destroyableList: DestroyableType[] = [];
|
|
30
|
+
const checkDestroyable = (instance: unknown) => {
|
|
31
|
+
if (isDestroyable(instance)) {
|
|
32
|
+
destroyableList.push(instance);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
35
|
const destructor = () => {
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|