silentium-components 0.0.4 → 0.0.6

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.
Files changed (77) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/silentium-components.cjs +134 -483
  3. package/dist/silentium-components.cjs.map +1 -1
  4. package/dist/silentium-components.d.ts +8 -190
  5. package/dist/silentium-components.js +130 -467
  6. package/dist/silentium-components.js.map +1 -1
  7. package/dist/silentium-components.min.js +1 -1
  8. package/dist/silentium-components.min.mjs +1 -1
  9. package/dist/silentium-components.min.mjs.map +1 -1
  10. package/dist/silentium-components.mjs +130 -467
  11. package/dist/silentium-components.mjs.map +1 -1
  12. package/docs/assets/js/index.mjs +3 -3
  13. package/docs/build.sh +3 -4
  14. package/docs/index.html +3 -3
  15. package/docs/pages/behaviors/deadline.html +60 -0
  16. package/docs/pages/behaviors/dirty.html +4 -4
  17. package/docs/pages/behaviors/loading.html +6 -6
  18. package/docs/pages/behaviors.html +5 -0
  19. package/docs/pages/structures/hash-table.html +4 -4
  20. package/docs/routes.json +1 -1
  21. package/eslint.config.mjs +1 -0
  22. package/package.json +7 -5
  23. package/src/behaviors/Deadline._value.test.ts +27 -0
  24. package/src/behaviors/Deadline.test.ts +28 -0
  25. package/src/behaviors/Deadline.ts +49 -0
  26. package/src/behaviors/Dirty.test.ts +5 -5
  27. package/src/behaviors/Dirty.ts +48 -54
  28. package/src/behaviors/Loading.test.ts +8 -8
  29. package/src/behaviors/Loading.ts +21 -31
  30. package/src/behaviors/Path.index.test.ts +5 -5
  31. package/src/behaviors/Path.nested.test.ts +5 -5
  32. package/src/behaviors/Path.test.ts +5 -5
  33. package/src/behaviors/Path.ts +26 -30
  34. package/src/behaviors/index.ts +1 -1
  35. package/src/controls/GroupActiveClass.test.ts +16 -13
  36. package/src/controls/GroupActiveClass.ts +19 -22
  37. package/src/controls/index.ts +0 -5
  38. package/src/index.ts +0 -2
  39. package/src/navigation/Router.test.ts +47 -0
  40. package/src/navigation/Router.ts +43 -111
  41. package/src/navigation/index.ts +0 -6
  42. package/src/structures/HashTable.test.ts +5 -5
  43. package/src/structures/HashTable.ts +14 -25
  44. package/src/system/RegexpMatched.test.ts +14 -0
  45. package/src/system/RegexpMatched.ts +17 -0
  46. package/src/system/index.ts +1 -0
  47. package/dist/patron-components.cjs +0 -372
  48. package/dist/patron-components.cjs.map +0 -1
  49. package/dist/patron-components.d.ts +0 -158
  50. package/dist/patron-components.js +0 -358
  51. package/dist/patron-components.js.map +0 -1
  52. package/dist/patron-components.min.js +0 -1
  53. package/dist/patron-components.min.mjs +0 -2
  54. package/dist/patron-components.min.mjs.map +0 -1
  55. package/dist/patron-components.mjs +0 -358
  56. package/dist/patron-components.mjs.map +0 -1
  57. package/src/behaviors/Touched.ts +0 -1
  58. package/src/controls/ComputedElement.ts +0 -51
  59. package/src/controls/Input.ts +0 -40
  60. package/src/controls/Link.ts +0 -53
  61. package/src/controls/Text.ts +0 -16
  62. package/src/controls/Visible.ts +0 -16
  63. package/src/jsdom/JSDomDocument.ts +0 -15
  64. package/src/jsdom/JSDomElement.ts +0 -28
  65. package/src/jsdom/JSDomQuerySelector.ts +0 -28
  66. package/src/navigation/CurrentPage.ts +0 -27
  67. package/src/navigation/Navigation.default.test.ts +0 -53
  68. package/src/navigation/Navigation.main.test.ts +0 -46
  69. package/src/navigation/Navigation.ts +0 -92
  70. package/src/navigation/Navigation.wildcard.test.ts +0 -52
  71. package/src/navigation/PageFetchTransport.ts +0 -26
  72. package/src/navigation/RouteDisplay.ts +0 -18
  73. package/src/navigation/RoutePageType.ts +0 -3
  74. package/src/page/EntryPointPage.ts +0 -20
  75. package/src/page/Page.ts +0 -12
  76. package/src/page/PageFake.ts +0 -8
  77. package/src/page/index.ts +0 -2
@@ -1,15 +1,15 @@
1
- import { SourceSync, SourceWithPool } from "silentium";
1
+ import { sourceOf, sourceSync } from "silentium";
2
+ import { loading } from "../behaviors/Loading";
2
3
  import { expect, test } from "vitest";
3
- import { Loading } from "../behaviors/Loading";
4
4
 
5
5
  test("Loading.test", () => {
6
- const loadingStartSource = new SourceWithPool();
7
- const loadingFinishSource = new SourceWithPool();
8
- const loading = new SourceSync(
9
- new Loading(loadingStartSource, loadingFinishSource),
6
+ const loadingStartSource = sourceOf();
7
+ const loadingFinishSource = sourceOf();
8
+ const loadingSrc = sourceSync(
9
+ loading(loadingStartSource, loadingFinishSource),
10
10
  );
11
11
  loadingStartSource.give({});
12
- expect(loading.syncValue()).toBe(true);
12
+ expect(loadingSrc.syncValue()).toBe(true);
13
13
  loadingFinishSource.give({});
14
- expect(loading.syncValue()).toBe(false);
14
+ expect(loadingSrc.syncValue()).toBe(false);
15
15
  });
@@ -1,37 +1,27 @@
1
- import {
2
- GuestCast,
3
- GuestType,
4
- SourceObjectType,
5
- SourceType,
6
- SourceWithPool,
7
- value,
8
- } from "silentium";
1
+ import { patron, sourceOf, SourceType, subSourceMany, value } from "silentium";
9
2
 
10
3
  /**
11
4
  * https://silentium-lab.github.io/silentium-components/#/behaviors/loading
12
5
  */
13
- export class Loading implements SourceObjectType<boolean> {
14
- private loadingSource = new SourceWithPool<boolean>();
6
+ export const loading = (
7
+ loadingStartSource: SourceType<unknown>,
8
+ loadingFinishSource: SourceType<unknown>,
9
+ ) => {
10
+ const loadingSrc = sourceOf<boolean>();
11
+ subSourceMany(loadingSrc, [loadingStartSource, loadingFinishSource]);
15
12
 
16
- public constructor(
17
- private loadingStartSource: SourceType<unknown>,
18
- private loadingFinishSource: SourceType<unknown>,
19
- ) {}
13
+ value(
14
+ loadingStartSource,
15
+ patron(() => {
16
+ loadingSrc.give(true);
17
+ }),
18
+ );
19
+ value(
20
+ loadingFinishSource,
21
+ patron(() => {
22
+ loadingSrc.give(false);
23
+ }),
24
+ );
20
25
 
21
- public value(guest: GuestType<boolean>) {
22
- value(
23
- this.loadingStartSource,
24
- new GuestCast(guest, () => {
25
- this.loadingSource.give(true);
26
- }),
27
- );
28
- value(
29
- this.loadingFinishSource,
30
- new GuestCast(guest, () => {
31
- this.loadingSource.give(false);
32
- }),
33
- );
34
- this.loadingSource.value(guest);
35
- return this;
36
- }
37
- }
26
+ return loadingSrc.value;
27
+ };
@@ -1,16 +1,16 @@
1
- import { sourceOf, SourceSync } from "silentium";
2
- import { Path } from "./Path";
1
+ import { sourceSync } from "silentium";
3
2
  import { expect, test } from "vitest";
3
+ import { path } from "./Path";
4
4
 
5
5
  test("Path.index.test", () => {
6
- const record = sourceOf({
6
+ const record = {
7
7
  name: "Peter",
8
8
  surname: "Parker",
9
9
  colors: ["blue", "red"],
10
10
  type: {
11
11
  name: "spider-man",
12
12
  },
13
- });
14
- const bestColor = new SourceSync(new Path(record, sourceOf("colors.0")));
13
+ };
14
+ const bestColor = sourceSync(path(record, "colors.0"));
15
15
  expect(bestColor.syncValue()).toBe("blue");
16
16
  });
@@ -1,15 +1,15 @@
1
- import { sourceOf, SourceSync } from "silentium";
2
- import { Path } from "./Path";
1
+ import { sourceSync } from "silentium";
3
2
  import { expect, test } from "vitest";
3
+ import { path } from "./Path";
4
4
 
5
5
  test("Path.nested.test", () => {
6
- const record = sourceOf({
6
+ const record = {
7
7
  name: "Peter",
8
8
  surname: "Parker",
9
9
  type: {
10
10
  name: "spider-man",
11
11
  },
12
- });
13
- const typeName = new SourceSync(new Path(record, sourceOf("type.name")));
12
+ };
13
+ const typeName = sourceSync(path(record, "type.name"));
14
14
  expect(typeName.syncValue()).toBe("spider-man");
15
15
  });
@@ -1,12 +1,12 @@
1
- import { sourceOf, SourceSync } from "silentium";
2
- import { Path } from "../behaviors/Path";
1
+ import { sourceSync } from "silentium";
3
2
  import { expect, test } from "vitest";
3
+ import { path } from "../behaviors/Path";
4
4
 
5
5
  test("Path.test", () => {
6
- const record = sourceOf({
6
+ const record = {
7
7
  name: "Peter",
8
8
  surname: "Parker",
9
- });
10
- const name = new SourceSync(new Path(record, sourceOf("name")));
9
+ };
10
+ const name = sourceSync(path(record, "name"));
11
11
  expect(name.syncValue()).toBe("Peter");
12
12
  });
@@ -1,38 +1,34 @@
1
1
  import {
2
2
  give,
3
- GuestCast,
4
- GuestType,
5
- SourceAll,
6
- SourceObjectType,
3
+ patron,
4
+ sourceAll,
5
+ sourceOf,
7
6
  SourceType,
7
+ subSourceMany,
8
8
  value,
9
9
  } from "silentium";
10
10
 
11
- export class Path<T extends Record<string, unknown>, K extends string>
12
- implements SourceObjectType<T[K]>
13
- {
14
- public constructor(
15
- private baseSource: SourceType<T>,
16
- private keyType: SourceType<K>,
17
- ) {}
11
+ export const path = <T extends Record<string, unknown>, K extends string>(
12
+ baseSrc: SourceType<T>,
13
+ keySrc: SourceType<K>,
14
+ ) => {
15
+ const pathSrc = sourceOf<T[K]>();
16
+ subSourceMany(pathSrc, [baseSrc, keySrc]);
18
17
 
19
- public value(guest: GuestType<T[K]>) {
20
- const all = new SourceAll<{ base: T; key: K }>(["base", "key"]);
21
- value(this.baseSource, new GuestCast(guest, all.guestKey("base")));
22
- value(this.keyType, new GuestCast(guest, all.guestKey("key")));
23
- all.value(
24
- new GuestCast(guest, ({ base, key }) => {
25
- const keyChunks = key.split(".");
26
- let value: unknown = base;
27
- keyChunks.forEach((keyChunk) => {
28
- value = (value as T)[keyChunk];
29
- });
18
+ value(
19
+ sourceAll([baseSrc, keySrc]),
20
+ patron(([base, key]) => {
21
+ const keyChunks = key.split(".");
22
+ let value: unknown = base;
23
+ keyChunks.forEach((keyChunk) => {
24
+ value = (value as T)[keyChunk];
25
+ });
30
26
 
31
- if (value !== undefined && value !== base) {
32
- give(value as T[K], guest);
33
- }
34
- }),
35
- );
36
- return this;
37
- }
38
- }
27
+ if (value !== undefined && value !== base) {
28
+ give(value as T[K], pathSrc);
29
+ }
30
+ }),
31
+ );
32
+
33
+ return pathSrc.value;
34
+ };
@@ -1,4 +1,4 @@
1
1
  export * from "./Dirty";
2
2
  export * from "./Loading";
3
- export * from "./Touched";
4
3
  export * from "./Path";
4
+ export * from "./Deadline";
@@ -1,26 +1,29 @@
1
- import { GuestSync } from "silentium";
2
- import { JSDomQuerySelector } from "../jsdom/JSDomQuerySelector";
1
+ import { sourceOf, sourceSync, value } from "silentium";
2
+ import { jsdomDocument } from "silentium-jsdom";
3
+ import { element } from "silentium-web-api";
3
4
  import { expect, test } from "vitest";
4
- import { JSDomDocument } from "../jsdom/JSDomDocument";
5
- import { GroupActiveClass } from "./GroupActiveClass";
5
+ import { path } from "../behaviors";
6
+ import { groupActiveClass } from "./GroupActiveClass";
6
7
 
7
8
  test("GroupActiveClass.test", () => {
8
- const document = new JSDomDocument(`<div class="menu">
9
+ const document = sourceSync(
10
+ jsdomDocument(`<div class="menu">
9
11
  <div id="prev-active" class="menu-link active">One</div>
10
12
  <div class="menu-link">Two</div>
11
13
  <div id="next-active" class="menu-link">Three</div>
12
- </div>`);
13
- const element = new JSDomQuerySelector(document, "#next-active");
14
- const classActive = new GroupActiveClass("active", ".menu-link", document);
15
- const g = new GuestSync<Document>(null as unknown as Document);
14
+ </div>`),
15
+ ).syncValue();
16
+ const groupElements = path(<any>element(".menu", document), "childNodes");
17
+ const activeElement = sourceOf<HTMLElement>();
18
+ sourceSync(groupActiveClass("active", activeElement, groupElements));
16
19
 
17
- element.value(classActive);
18
- document.value(g);
20
+ value(element("#next-active", document), activeElement);
19
21
 
20
- expect(g.value().body.innerHTML).toContain(
22
+ expect(document.body.outerHTML).toContain(
21
23
  'id="next-active" class="menu-link active"',
22
24
  );
23
- expect(g.value().body.innerHTML).toContain(
25
+
26
+ expect(document.body.innerHTML).toContain(
24
27
  'id="prev-active" class="menu-link"',
25
28
  );
26
29
  });
@@ -1,29 +1,26 @@
1
- import { GuestObjectType, PatronOnce, SourceType, value } from "silentium";
1
+ import { patron, sourceAll, SourceType, value } from "silentium";
2
2
 
3
3
  /**
4
4
  * Sets activeClass to one element of group
5
5
  * and resets activeClass on other group elements
6
6
  * suitable for menu active class
7
- *
8
- * @deprecated heavily related to web api needs refactoring
9
7
  */
10
- export class GroupActiveClass implements GuestObjectType<HTMLElement> {
11
- public constructor(
12
- private activeClass: string,
13
- private groupSelector: string,
14
- private document: SourceType<Document>,
15
- ) {}
8
+ export const groupActiveClass = (
9
+ activeClassSrc: SourceType<string>,
10
+ activeElementSrc: SourceType<HTMLElement>,
11
+ groupElementsSrc: SourceType<HTMLElement[]>,
12
+ ) => {
13
+ value(
14
+ sourceAll([activeClassSrc, activeElementSrc, groupElementsSrc]),
15
+ patron(([activeClass, activeElement, groupElements]) => {
16
+ groupElements.forEach((el) => {
17
+ if (el.classList) {
18
+ el.classList.remove(activeClass);
19
+ }
20
+ });
21
+ activeElement.classList.add(activeClass);
22
+ }),
23
+ );
16
24
 
17
- public give(element: HTMLElement): this {
18
- value(
19
- this.document,
20
- new PatronOnce((document) => {
21
- document.querySelectorAll(this.groupSelector).forEach((el) => {
22
- el.classList.remove(this.activeClass);
23
- });
24
- element.classList.add(this.activeClass);
25
- }),
26
- );
27
- return this;
28
- }
29
- }
25
+ return groupElementsSrc;
26
+ };
@@ -1,6 +1 @@
1
- export * from "./Input";
2
- export * from "./Visible";
3
- export * from "./Text";
4
- export * from "./Link";
5
- export * from "./ComputedElement";
6
1
  export * from "./GroupActiveClass";
package/src/index.ts CHANGED
@@ -1,5 +1,3 @@
1
- export * from "./navigation";
2
1
  export * from "./controls";
3
- export * from "./page";
4
2
  export * from "./behaviors";
5
3
  export * from "./structures";
@@ -0,0 +1,47 @@
1
+ import { source, sourceApplied, sourceOf, sourceSync } from "silentium";
2
+ import { expect, test } from "vitest";
3
+ import { router } from "../navigation/Router";
4
+
5
+ const drop = (dropPart: string) => (value: string) => {
6
+ return value.replace(dropPart, "");
7
+ };
8
+
9
+ test("Router.test", () => {
10
+ const urlSrc = sourceOf<string>("http://domain.com/some/url/");
11
+ const urlClearedSrc = sourceApplied(urlSrc, drop("http://domain.com"));
12
+
13
+ const template = sourceSync(
14
+ router(
15
+ urlClearedSrc,
16
+ [
17
+ {
18
+ pattern: "^/$",
19
+ template: source("page/home.html"),
20
+ },
21
+ {
22
+ pattern: "/some/url",
23
+ template: "page/home.html",
24
+ },
25
+ {
26
+ pattern: "/some/contacts",
27
+ template: "page/contacts.html",
28
+ },
29
+ ],
30
+ source("page/404.html"),
31
+ ),
32
+ );
33
+
34
+ expect(template.syncValue()).toBe("page/home.html");
35
+
36
+ urlSrc.give("http://domain.com/some/contacts/");
37
+
38
+ expect(template.syncValue()).toBe("page/contacts.html");
39
+
40
+ urlSrc.give("http://domain.com/some/unknown/");
41
+
42
+ expect(template.syncValue()).toBe("page/404.html");
43
+
44
+ urlSrc.give("http://domain.com/");
45
+
46
+ expect(template.syncValue()).toBe("page/home.html");
47
+ });
@@ -1,118 +1,50 @@
1
1
  import {
2
- Source,
3
- SourceAll,
4
- GuestCast,
5
- Patron,
6
- PrivateClass,
7
- SourceWithPool,
8
- give,
2
+ patron,
3
+ sourceAny,
4
+ sourceChain,
5
+ sourceFiltered,
9
6
  sourceOf,
7
+ SourceType,
8
+ value,
10
9
  } from "silentium";
11
- import { HistoryNewPage, HistoryPoppedPage } from "silentium-web-api";
12
- import { ComputedElement, GroupActiveClass, Link, Visible } from "../controls";
13
- import { CurrentPage } from "../navigation/CurrentPage";
14
- import { Navigation } from "../navigation/Navigation";
15
- import { PageFetchTransport } from "../navigation/PageFetchTransport";
16
- import { RouteDisplay } from "../navigation/RouteDisplay";
10
+ import { regexpMatched } from "../system/RegexpMatched";
17
11
 
18
- type Route = {
19
- url: string;
20
- template: string;
21
- aliases: string[];
22
- page: any;
23
- };
24
-
25
- export class Router {
26
- public constructor(
27
- private loaderSelector: string,
28
- private navigationResultSelector: string,
29
- private menuSelector: string,
30
- ) {}
31
-
32
- routes(
33
- routes: Route[],
34
- currentPage: any,
35
- basePathSource: any,
36
- afterPageLoaded?: () => void,
37
- ) {
38
- if (!currentPage) {
39
- currentPage = new CurrentPage();
40
- }
41
- currentPage.value(new Patron(new HistoryNewPage()));
42
-
43
- const [basePath] = location.href.replace(location.origin, "").split("#");
44
- if (!basePathSource) {
45
- basePathSource = new SourceWithPool(
46
- `${basePath}#`.replace("index.html", "").replace("//", "/"),
47
- );
48
- }
49
-
50
- const pageLoading = new SourceWithPool(false);
51
- pageLoading.value(new Patron(new Visible(this.loaderSelector)));
52
-
53
- const historyPoppedPage = new HistoryPoppedPage(currentPage);
54
- historyPoppedPage.watchPop();
55
-
56
- const navigation = new Navigation(
57
- pageLoading,
58
- basePathSource,
59
- currentPage,
60
- new RouteDisplay(this.navigationResultSelector),
61
- new PrivateClass(PageFetchTransport),
62
- );
63
- navigation.routes(routes);
64
-
65
- const link = new Link(currentPage, basePathSource);
66
- link.watchClick(this.menuSelector);
12
+ export interface Route<T> {
13
+ pattern: string;
14
+ patternFlags?: string;
15
+ template: T | SourceType<T>;
16
+ }
67
17
 
68
- const urlChain = new SourceAll<any>();
69
- basePathSource.value(new Patron(urlChain.guestKey("basePath")));
70
- currentPage.value(new Patron(urlChain.guestKey("page")));
71
- const url = new Source((guest) => {
72
- urlChain.value(
73
- new GuestCast(guest, ({ basePath, page }) => {
74
- give(page.replace(basePath, ""), guest);
75
- }),
18
+ /**
19
+ * Router component what will return template if url matches pattern
20
+ */
21
+ export const router = <T = "string">(
22
+ urlSrc: SourceType<string>,
23
+ routesSrc: SourceType<Route<T>[]>,
24
+ defaultSrc: SourceType<T>,
25
+ ) => {
26
+ const resultSrc = sourceOf<T>();
27
+
28
+ value(
29
+ routesSrc,
30
+ patron((routes) => {
31
+ value(
32
+ sourceAny([
33
+ sourceChain(urlSrc, defaultSrc as T),
34
+ ...routes.map((r) =>
35
+ sourceChain(
36
+ sourceFiltered(
37
+ regexpMatched(r.pattern, urlSrc, r.patternFlags),
38
+ Boolean,
39
+ ),
40
+ r.template,
41
+ ),
42
+ ),
43
+ ]),
44
+ patron(resultSrc),
76
45
  );
77
- });
78
-
79
- const activeLink = new ComputedElement(
80
- [{ source: url, placeholder: "{url}" }],
81
- `${this.menuSelector} a[href="{url}"]`,
82
- );
83
- activeLink.element(
84
- new Patron(
85
- new GroupActiveClass(
86
- "active",
87
- `${this.menuSelector} a`,
88
- sourceOf(document),
89
- ),
90
- ),
91
- );
92
-
93
- pageLoading.value(
94
- new Patron((isInLoading) => {
95
- if (isInLoading) {
96
- return;
97
- }
46
+ }),
47
+ );
98
48
 
99
- if (afterPageLoaded) {
100
- afterPageLoaded();
101
- }
102
-
103
- const divDestination = document.querySelector(
104
- this.navigationResultSelector,
105
- );
106
- if (divDestination) {
107
- // Оживляем script тэги
108
- divDestination.querySelectorAll("script").forEach((x) => {
109
- const sc = document.createElement("script");
110
- sc.setAttribute("type", "module");
111
- sc.appendChild(document.createTextNode(x.innerText));
112
- divDestination.appendChild(sc);
113
- });
114
- }
115
- }),
116
- );
117
- }
118
- }
49
+ return resultSrc.value;
50
+ };
@@ -1,7 +1 @@
1
- export * from "./PageFetchTransport";
2
- export * from "./Navigation";
3
- export * from "./RouteDisplay";
4
- export * from "./RoutePageType";
5
- export * from "./PageFetchTransport";
6
- export * from "./CurrentPage";
7
1
  export * from "./Router";
@@ -1,14 +1,14 @@
1
- import { SourceSync, SourceWithPool } from "silentium";
2
- import { HashTable } from "../structures/HashTable";
1
+ import { sourceOf, sourceSync } from "silentium";
3
2
  import { expect, test } from "vitest";
3
+ import { hashTable } from "../structures/HashTable";
4
4
 
5
5
  test("HashTable.test", () => {
6
- const entrySource = new SourceWithPool<[string, string]>();
7
- const hashTable = new SourceSync(new HashTable(entrySource));
6
+ const entrySource = sourceOf<[string, string]>();
7
+ const hashTableSrc = sourceSync(hashTable(entrySource));
8
8
  entrySource.give(["key-one", "value-one"]);
9
9
  entrySource.give(["key-two", "value-two"]);
10
10
 
11
- expect(hashTable.syncValue()).toStrictEqual({
11
+ expect(hashTableSrc.syncValue()).toStrictEqual({
12
12
  "key-one": "value-one",
13
13
  "key-two": "value-two",
14
14
  });
@@ -1,31 +1,20 @@
1
- import {
2
- GuestType,
3
- Patron,
4
- SourceObjectType,
5
- SourceType,
6
- SourceWithPool,
7
- value,
8
- } from "silentium";
1
+ import { patron, sourceOf, SourceType, subSource, value } from "silentium";
9
2
 
10
3
  /**
11
4
  * https://silentium-lab.github.io/silentium-components/#/structures/hash-table
12
5
  */
13
- export class HashTable implements SourceObjectType<Record<string, unknown>> {
14
- private source = new SourceWithPool<Record<string, unknown>>({});
6
+ export const hashTable = (baseSource: SourceType<[string, unknown]>) => {
7
+ const result = sourceOf<Record<string, unknown>>({});
8
+ subSource(result, baseSource);
15
9
 
16
- public constructor(baseSource: SourceType<[string, unknown]>) {
17
- value(
18
- baseSource,
19
- new Patron(([key, value]) => {
20
- this.source.value((lastRecord) => {
21
- lastRecord[key] = value;
22
- });
23
- }),
24
- );
25
- }
10
+ value(
11
+ baseSource,
12
+ patron(([key, value]) => {
13
+ result.value((lastRecord) => {
14
+ lastRecord[key] = value;
15
+ });
16
+ }),
17
+ );
26
18
 
27
- public value(guest: GuestType<Record<string, unknown>>) {
28
- value(this.source, guest);
29
- return this;
30
- }
31
- }
19
+ return result.value;
20
+ };
@@ -0,0 +1,14 @@
1
+ import { sourceOf, sourceSync } from "silentium";
2
+ import { regexpMatched } from "../system/RegexpMatched";
3
+ import { expect, test } from "vitest";
4
+
5
+ test("RegexpMatched.test", () => {
6
+ const urlSrc = sourceOf<string>("http://domain.com/some/url/");
7
+ const matchedSrc = sourceSync(regexpMatched("/some/url", urlSrc));
8
+
9
+ expect(matchedSrc.syncValue()).toBe(true);
10
+
11
+ urlSrc.give("http://domain.com/changed");
12
+
13
+ expect(matchedSrc.syncValue()).toBe(false);
14
+ });
@@ -0,0 +1,17 @@
1
+ import { give, GuestType, sourceCombined, SourceType } from "silentium";
2
+
3
+ /**
4
+ * Boolean source what checks what string matches pattern
5
+ */
6
+ export const regexpMatched = (
7
+ patternSrc: SourceType<string>,
8
+ valueSrc: SourceType<string>,
9
+ flagsSrc: SourceType<string> = "",
10
+ ): SourceType<boolean> =>
11
+ sourceCombined(
12
+ patternSrc,
13
+ valueSrc,
14
+ flagsSrc,
15
+ )((g: GuestType<boolean>, pattern, value, flags) => {
16
+ give(new RegExp(pattern, flags).test(value), g);
17
+ });
@@ -0,0 +1 @@
1
+ export * from "./RegexpMatched";