@cedx/base 0.1.0 → 0.3.0

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 (40) hide show
  1. package/ReadMe.md +1 -2
  2. package/lib/Date.d.ts +72 -0
  3. package/lib/Date.d.ts.map +1 -0
  4. package/lib/Date.js +125 -0
  5. package/lib/Duration.d.ts +26 -0
  6. package/lib/Duration.d.ts.map +1 -0
  7. package/lib/Duration.js +21 -0
  8. package/lib/Html/AppTheme.d.ts +4 -4
  9. package/lib/Html/AppTheme.d.ts.map +1 -1
  10. package/lib/Html/AppTheme.js +4 -4
  11. package/lib/Html/Context.d.ts +32 -0
  12. package/lib/Html/Context.d.ts.map +1 -0
  13. package/lib/Html/Context.js +34 -0
  14. package/lib/Html/File.d.ts +32 -0
  15. package/lib/Html/File.d.ts.map +1 -0
  16. package/lib/Html/File.js +76 -0
  17. package/lib/UI/LoadingIndicator.d.ts +1 -11
  18. package/lib/UI/LoadingIndicator.d.ts.map +1 -1
  19. package/lib/UI/LoadingIndicator.js +5 -20
  20. package/lib/UI/MenuActivator.d.ts +22 -0
  21. package/lib/UI/MenuActivator.d.ts.map +1 -0
  22. package/lib/UI/MenuActivator.js +27 -0
  23. package/lib/UI/OfflineIndicator.d.ts +1 -7
  24. package/lib/UI/OfflineIndicator.d.ts.map +1 -1
  25. package/lib/UI/OfflineIndicator.js +8 -18
  26. package/lib/UI/ThemeDropdown.d.ts +2 -36
  27. package/lib/UI/ThemeDropdown.d.ts.map +1 -1
  28. package/lib/UI/ThemeDropdown.js +41 -86
  29. package/package.json +3 -3
  30. package/src/Client/Date.ts +138 -0
  31. package/src/Client/Duration.ts +30 -0
  32. package/src/Client/Html/AppTheme.ts +4 -4
  33. package/src/Client/Html/Context.ts +44 -0
  34. package/src/Client/Html/File.ts +85 -0
  35. package/src/Client/Html/ViewportScroller.ts +1 -1
  36. package/src/Client/Html/tsconfig.json +4 -1
  37. package/src/Client/UI/LoadingIndicator.ts +4 -16
  38. package/src/Client/UI/MenuActivator.ts +42 -0
  39. package/src/Client/UI/OfflineIndicator.ts +10 -17
  40. package/src/Client/UI/ThemeDropdown.ts +34 -65
@@ -1,12 +1,9 @@
1
1
  import type {ILoadingIndicator} from "#Abstractions/ILoadingIndicator.js";
2
- import {html, LitElement, type TemplateResult} from "lit";
3
- import {customElement} from "lit/decorators.js";
4
2
 
5
3
  /**
6
4
  * A component that shows up when an HTTP request starts, and hides when all concurrent HTTP requests are completed.
7
5
  */
8
- @customElement("loading-indicator")
9
- export class LoadingIndicator extends LitElement implements ILoadingIndicator {
6
+ export class LoadingIndicator extends HTMLElement implements ILoadingIndicator {
10
7
 
11
8
  /**
12
9
  * The number of concurrent HTTP requests.
@@ -14,11 +11,10 @@ export class LoadingIndicator extends LitElement implements ILoadingIndicator {
14
11
  #requestCount = 0;
15
12
 
16
13
  /**
17
- * Creates a new loading indicator.
14
+ * Registers the component.
18
15
  */
19
- constructor() {
20
- super();
21
- this.hidden = true;
16
+ static {
17
+ customElements.define("loading-indicator", this);
22
18
  }
23
19
 
24
20
  /**
@@ -42,14 +38,6 @@ export class LoadingIndicator extends LitElement implements ILoadingIndicator {
42
38
  document.body.classList.remove("loading");
43
39
  }
44
40
  }
45
-
46
- /**
47
- * Renders this component.
48
- * @returns The view template.
49
- */
50
- protected override render(): TemplateResult {
51
- return html`<slot></slot>`;
52
- }
53
41
  }
54
42
 
55
43
  /**
@@ -0,0 +1,42 @@
1
+ /**
2
+ * A component that activates the items of a menu based on the current document URL.
3
+ */
4
+ export class MenuActivator extends HTMLElement {
5
+
6
+ /**
7
+ * The root element.
8
+ */
9
+ readonly #root = this.firstElementChild!;
10
+
11
+ /**
12
+ * Registers the component.
13
+ */
14
+ static {
15
+ customElements.define("menu-activator", this);
16
+ }
17
+
18
+ /**
19
+ * Method invoked when this component is connected.
20
+ */
21
+ connectedCallback(): void {
22
+ for (const anchor of this.#root.getElementsByTagName("a"))
23
+ if (anchor.href != location.href) anchor.classList.remove("active");
24
+ else {
25
+ anchor.classList.add("active");
26
+ anchor.closest(".nav-item.dropdown")?.querySelector(".nav-link")?.classList.add("active");
27
+ }
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Declaration merging.
33
+ */
34
+ declare global {
35
+
36
+ /**
37
+ * The map of HTML tag names.
38
+ */
39
+ interface HTMLElementTagNameMap {
40
+ "menu-activator": MenuActivator;
41
+ }
42
+ }
@@ -1,11 +1,7 @@
1
- import {html, LitElement, type TemplateResult} from "lit";
2
- import {customElement} from "lit/decorators.js";
3
-
4
1
  /**
5
2
  * A component that shows up when the network is unavailable, and hides when connectivity is restored.
6
3
  */
7
- @customElement("offline-indicator")
8
- export class OfflineIndicator extends LitElement {
4
+ export class OfflineIndicator extends HTMLElement {
9
5
 
10
6
  /**
11
7
  * Creates a new offline indicator.
@@ -15,20 +11,25 @@ export class OfflineIndicator extends LitElement {
15
11
  this.hidden = navigator.onLine;
16
12
  }
17
13
 
14
+ /**
15
+ * Registers the component.
16
+ */
17
+ static {
18
+ customElements.define("offline-indicator", this);
19
+ }
20
+
18
21
  /**
19
22
  * Method invoked when this component is connected.
20
23
  */
21
- override connectedCallback(): void {
22
- super.connectedCallback();
24
+ connectedCallback(): void {
23
25
  for (const event of ["online", "offline"]) addEventListener(event, this);
24
26
  }
25
27
 
26
28
  /**
27
29
  * Method invoked when this component is disconnected.
28
30
  */
29
- override disconnectedCallback(): void {
31
+ disconnectedCallback(): void {
30
32
  for (const event of ["online", "offline"]) removeEventListener(event, this);
31
- super.disconnectedCallback();
32
33
  }
33
34
 
34
35
  /**
@@ -37,14 +38,6 @@ export class OfflineIndicator extends LitElement {
37
38
  handleEvent(): void {
38
39
  this.hidden = navigator.onLine;
39
40
  }
40
-
41
- /**
42
- * Renders this component.
43
- * @returns The view template.
44
- */
45
- protected override render(): TemplateResult {
46
- return html`<slot></slot>`;
47
- }
48
41
  }
49
42
 
50
43
  /**
@@ -1,74 +1,60 @@
1
- import {AppTheme, themeIcon, themeLabel} from "#Html/AppTheme.js";
2
- import {MenuAlignment} from "#Html/MenuAlignment.js";
3
- import {html, LitElement, type TemplateResult} from "lit";
4
- import {customElement, property, state} from "lit/decorators.js";
5
- import {classMap} from "lit/directives/class-map.js";
6
- import {when} from "lit/directives/when.js";
1
+ import {AppTheme, getIcon} from "#Html/AppTheme.js";
7
2
 
8
3
  /**
9
- * A dropdown menu for switching the color mode.
4
+ * A dropdown menu for switching the application theme.
10
5
  */
11
- @customElement("theme-dropdown")
12
- export class ThemeDropdown extends LitElement {
6
+ export class ThemeDropdown extends HTMLElement {
13
7
 
14
8
  /**
15
- * The alignment of the dropdown menu.
9
+ * The media query used to check the application theme.
16
10
  */
17
- @property() alignment: MenuAlignment = MenuAlignment.End;
11
+ readonly #mediaQuery = matchMedia("(prefers-color-scheme: dark)");
18
12
 
19
13
  /**
20
- * The label of the dropdown menu.
14
+ * The root element.
21
15
  */
22
- @property() label = "";
16
+ readonly #root = this.firstElementChild!;
23
17
 
24
18
  /**
25
- * The key of the storage entry providing the saved theme.
19
+ * The key of the storage entry providing the saved theme mode.
26
20
  */
27
- @property() storageKey = "AppTheme";
21
+ readonly #storageKey = this.getAttribute("storageKey") ?? "AppTheme";
28
22
 
29
23
  /**
30
24
  * The current application theme.
31
25
  */
32
- @state() private appTheme: AppTheme;
33
-
34
- /**
35
- * The media query used to check the system theme.
36
- */
37
- readonly #mediaQuery = matchMedia("(prefers-color-scheme: dark)");
26
+ #theme: AppTheme;
38
27
 
39
28
  /**
40
29
  * Creates a new theme dropdown.
41
30
  */
42
31
  constructor() {
43
32
  super();
44
- const theme = localStorage.getItem(this.storageKey) as AppTheme;
45
- this.appTheme = Object.values(AppTheme).includes(theme) ? theme : AppTheme.System;
33
+ const theme = localStorage.getItem(this.#storageKey) as AppTheme;
34
+ this.#theme = Object.values(AppTheme).includes(theme) ? theme : AppTheme.System;
35
+ for (const button of this.#root.querySelectorAll("button")) button.addEventListener("click", this.#setTheme.bind(this));
46
36
  }
47
37
 
48
38
  /**
49
- * The current theme mode.
39
+ * Registers the component.
50
40
  */
51
- get theme(): AppTheme { return this.appTheme; }
52
- set theme(value: AppTheme) {
53
- localStorage.setItem(this.storageKey, this.appTheme = value);
54
- this.#applyTheme();
41
+ static {
42
+ customElements.define("theme-dropdown", this);
55
43
  }
56
44
 
57
45
  /**
58
46
  * Method invoked when this component is connected.
59
47
  */
60
- override connectedCallback(): void {
61
- super.connectedCallback();
62
- this.#applyTheme();
48
+ connectedCallback(): void {
63
49
  this.#mediaQuery.addEventListener("change", this);
50
+ this.#applyTheme();
64
51
  }
65
52
 
66
53
  /**
67
54
  * Method invoked when this component is disconnected.
68
55
  */
69
- override disconnectedCallback(): void {
56
+ disconnectedCallback(): void {
70
57
  this.#mediaQuery.removeEventListener("change", this);
71
- super.disconnectedCallback();
72
58
  }
73
59
 
74
60
  /**
@@ -79,44 +65,27 @@ export class ThemeDropdown extends LitElement {
79
65
  }
80
66
 
81
67
  /**
82
- * Returns the node into which this component should render.
83
- * @returns The node into which this component should render.
68
+ * Applies the theme to the document.
84
69
  */
85
- protected override createRenderRoot(): DocumentFragment|HTMLElement {
86
- return this;
87
- }
70
+ #applyTheme(): void {
71
+ const theme = this.#theme == AppTheme.System ? (this.#mediaQuery.matches ? AppTheme.Dark : AppTheme.Light) : this.#theme;
72
+ document.documentElement.dataset.bsTheme = theme.toLowerCase();
73
+ this.#root.querySelector(".dropdown-toggle > .icon")!.textContent = getIcon(this.#theme);
88
74
 
89
- /**
90
- * Renders this component.
91
- * @returns The view template.
92
- */
93
- protected override render(): TemplateResult {
94
- return html`
95
- <li class="nav-item dropdown">
96
- <a class="dropdown-toggle nav-link" data-bs-toggle="dropdown" href="#">
97
- <i class="icon icon-fill">${themeIcon(this.appTheme)}</i>
98
- ${when(this.label, () => html`<span class="ms-1">${this.label}</span>`)}
99
- </a>
100
- <ul class="dropdown-menu ${classMap({"dropdown-menu-end": this.alignment == MenuAlignment.End})}">
101
- ${Object.values(AppTheme).map(value => html`
102
- <li>
103
- <button class="dropdown-item d-flex align-items-center justify-content-between" @click=${() => this.theme = value}>
104
- <span><i class="icon icon-fill me-1">${themeIcon(value)}</i> ${themeLabel(value)}</span>
105
- ${when(value == this.appTheme, () => html`<i class="icon ms-2">check</i>`)}
106
- </button>
107
- </li>
108
- `)}
109
- </ul>
110
- </li>
111
- `;
75
+ const checkIcon = this.#root.querySelector(".dropdown-item > .icon")!;
76
+ checkIcon.remove();
77
+ const activeButton = this.#root.querySelector(`button[data-theme="${this.#theme}"]`)!
78
+ activeButton.appendChild(checkIcon);
112
79
  }
113
80
 
114
81
  /**
115
- * Applies the theme to the document.
82
+ * Changes the current theme.
83
+ * @param event The dispatched event.
116
84
  */
117
- #applyTheme(): void {
118
- const theme = this.appTheme == AppTheme.System ? (this.#mediaQuery.matches ? AppTheme.Dark : AppTheme.Light) : this.appTheme;
119
- document.documentElement.dataset.bsTheme = theme.toLowerCase();
85
+ #setTheme(event: Event): void {
86
+ const button = (event.target as HTMLElement).closest("button")!;
87
+ localStorage.setItem(this.#storageKey, this.#theme = button.dataset.theme as AppTheme);
88
+ this.#applyTheme();
120
89
  }
121
90
  }
122
91