@cedx/base 0.1.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 (58) hide show
  1. package/License.md +20 -0
  2. package/ReadMe.md +21 -0
  3. package/lib/Abstractions/ILoadingIndicator.d.ts +17 -0
  4. package/lib/Abstractions/ILoadingIndicator.d.ts.map +1 -0
  5. package/lib/Abstractions/ILoadingIndicator.js +1 -0
  6. package/lib/DependencyInjection/Container.d.ts +43 -0
  7. package/lib/DependencyInjection/Container.d.ts.map +1 -0
  8. package/lib/DependencyInjection/Container.js +65 -0
  9. package/lib/Html/AppTheme.d.ts +34 -0
  10. package/lib/Html/AppTheme.d.ts.map +1 -0
  11. package/lib/Html/AppTheme.js +41 -0
  12. package/lib/Html/MenuAlignment.d.ts +18 -0
  13. package/lib/Html/MenuAlignment.d.ts.map +1 -0
  14. package/lib/Html/MenuAlignment.js +13 -0
  15. package/lib/Html/ViewportScroller.d.ts +49 -0
  16. package/lib/Html/ViewportScroller.d.ts.map +1 -0
  17. package/lib/Html/ViewportScroller.js +69 -0
  18. package/lib/Http/HttpClient.d.ts +68 -0
  19. package/lib/Http/HttpClient.d.ts.map +1 -0
  20. package/lib/Http/HttpClient.js +102 -0
  21. package/lib/Http/HttpError.d.ts +33 -0
  22. package/lib/Http/HttpError.d.ts.map +1 -0
  23. package/lib/Http/HttpError.js +66 -0
  24. package/lib/Http/StatusCodes.d.ts +114 -0
  25. package/lib/Http/StatusCodes.d.ts.map +1 -0
  26. package/lib/Http/StatusCodes.js +109 -0
  27. package/lib/Number.d.ts +8 -0
  28. package/lib/Number.d.ts.map +1 -0
  29. package/lib/Number.js +10 -0
  30. package/lib/UI/LoadingIndicator.d.ts +40 -0
  31. package/lib/UI/LoadingIndicator.d.ts.map +1 -0
  32. package/lib/UI/LoadingIndicator.js +50 -0
  33. package/lib/UI/OfflineIndicator.d.ts +39 -0
  34. package/lib/UI/OfflineIndicator.d.ts.map +1 -0
  35. package/lib/UI/OfflineIndicator.js +48 -0
  36. package/lib/UI/ThemeDropdown.d.ts +68 -0
  37. package/lib/UI/ThemeDropdown.d.ts.map +1 -0
  38. package/lib/UI/ThemeDropdown.js +123 -0
  39. package/package.json +59 -0
  40. package/src/Client/Abstractions/ILoadingIndicator.ts +16 -0
  41. package/src/Client/Abstractions/tsconfig.json +13 -0
  42. package/src/Client/Base/tsconfig.json +13 -0
  43. package/src/Client/DependencyInjection/Container.ts +75 -0
  44. package/src/Client/DependencyInjection/tsconfig.json +13 -0
  45. package/src/Client/Html/AppTheme.ts +51 -0
  46. package/src/Client/Html/MenuAlignment.ts +20 -0
  47. package/src/Client/Html/ViewportScroller.ts +89 -0
  48. package/src/Client/Html/tsconfig.json +13 -0
  49. package/src/Client/Http/HttpClient.ts +127 -0
  50. package/src/Client/Http/HttpError.ts +75 -0
  51. package/src/Client/Http/StatusCodes.ts +140 -0
  52. package/src/Client/Http/tsconfig.json +16 -0
  53. package/src/Client/Number.ts +10 -0
  54. package/src/Client/UI/LoadingIndicator.ts +66 -0
  55. package/src/Client/UI/OfflineIndicator.ts +61 -0
  56. package/src/Client/UI/ThemeDropdown.ts +134 -0
  57. package/src/Client/UI/tsconfig.json +19 -0
  58. package/src/Client/tsconfig.json +11 -0
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Provides common HTTP status codes.
3
+ */
4
+ export const StatusCodes = Object.freeze({
5
+
6
+ /**
7
+ * The `OK` status.
8
+ */
9
+ OK: 200,
10
+
11
+ /**
12
+ * The `Created` status.
13
+ */
14
+ Created: 201,
15
+
16
+ /**
17
+ * The `No Content` status.
18
+ */
19
+ NoContent: 204,
20
+
21
+ /**
22
+ * The `Moved Permanently` status.
23
+ */
24
+ MovedPermanently: 301,
25
+
26
+ /**
27
+ * The `Found` status.
28
+ */
29
+ Found: 302,
30
+
31
+ /**
32
+ * The `Not Modified` status.
33
+ */
34
+ NotModified: 304,
35
+
36
+ /**
37
+ * The `Bad Request` status.
38
+ */
39
+ BadRequest: 400,
40
+
41
+ /**
42
+ * The `Unauthorized` status.
43
+ */
44
+ Unauthorized: 401,
45
+
46
+ /**
47
+ * The `Payment Required` status.
48
+ */
49
+ PaymentRequired: 402,
50
+
51
+ /**
52
+ * The `Forbidden` status.
53
+ */
54
+ Forbidden: 403,
55
+
56
+ /**
57
+ * The `Not Found` status.
58
+ */
59
+ NotFound: 404,
60
+
61
+ /**
62
+ * The `Method Not Allowed` status.
63
+ */
64
+ MethodNotAllowed: 405,
65
+
66
+ /**
67
+ * The `Not Acceptable` status.
68
+ */
69
+ NotAcceptable: 406,
70
+
71
+ /**
72
+ * The `Request Timeout` status.
73
+ */
74
+ RequestTimeout: 408,
75
+
76
+ /**
77
+ * The `Conflict` status.
78
+ */
79
+ Conflict: 409,
80
+
81
+ /**
82
+ * The `Payload Too Large` status.
83
+ */
84
+ PayloadTooLarge: 413,
85
+
86
+ /**
87
+ * The `Unsupported Media Type` status.
88
+ */
89
+ UnsupportedMediaType: 415,
90
+
91
+ /**
92
+ * The `Page Expired` status.
93
+ */
94
+ PageExpired: 419,
95
+
96
+ /**
97
+ * The `Unprocessable Content` status.
98
+ */
99
+ UnprocessableContent: 422,
100
+
101
+ /**
102
+ * The `Too Many Requests` status.
103
+ */
104
+ TooManyRequests: 429,
105
+
106
+ /**
107
+ * The `Internal Server Error` status.
108
+ */
109
+ InternalServerError: 500,
110
+
111
+ /**
112
+ * The `Not Implemented` status.
113
+ */
114
+ NotImplemented: 501,
115
+
116
+ /**
117
+ * The `Bad Gateway` status.
118
+ */
119
+ BadGateway: 502,
120
+
121
+ /**
122
+ * The `Service Unavailable` status.
123
+ */
124
+ ServiceUnavailable: 503,
125
+
126
+ /**
127
+ * The `Gateway Timeout` status.
128
+ */
129
+ GatewayTimeout: 504,
130
+
131
+ /**
132
+ * The `Bandwidth Limit Exceeded` status
133
+ */
134
+ BandwidthLimitExceeded: 509
135
+ });
136
+
137
+ /**
138
+ * Provides common HTTP status codes.
139
+ */
140
+ export type StatusCodes = typeof StatusCodes[keyof typeof StatusCodes];
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "include": ["*.ts"],
4
+ "compilerOptions": {
5
+ "composite": true,
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "noEmit": false,
9
+ "outDir": "../../../lib/Http",
10
+ "rootDir": ".",
11
+ "tsBuildInfoFile": "../../../var/Http.tsbuildinfo"
12
+ },
13
+ "references": [
14
+ {"path": "../Abstractions/tsconfig.json"}
15
+ ]
16
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Rounds a floating-point number.
3
+ * @param value The value to round.
4
+ * @param precision The optional number of decimal digits to round to.
5
+ * @returns The value rounded to the given precision.
6
+ */
7
+ export function round(value: number, precision = 0): number {
8
+ const operand = 10 ** precision;
9
+ return Math.round(value * operand) / operand;
10
+ }
@@ -0,0 +1,66 @@
1
+ import type {ILoadingIndicator} from "#Abstractions/ILoadingIndicator.js";
2
+ import {html, LitElement, type TemplateResult} from "lit";
3
+ import {customElement} from "lit/decorators.js";
4
+
5
+ /**
6
+ * A component that shows up when an HTTP request starts, and hides when all concurrent HTTP requests are completed.
7
+ */
8
+ @customElement("loading-indicator")
9
+ export class LoadingIndicator extends LitElement implements ILoadingIndicator {
10
+
11
+ /**
12
+ * The number of concurrent HTTP requests.
13
+ */
14
+ #requestCount = 0;
15
+
16
+ /**
17
+ * Creates a new loading indicator.
18
+ */
19
+ constructor() {
20
+ super();
21
+ this.hidden = true;
22
+ }
23
+
24
+ /**
25
+ * Starts the loading indicator.
26
+ */
27
+ start(): void {
28
+ this.#requestCount++;
29
+ this.hidden = false;
30
+ document.body.classList.add("loading");
31
+ }
32
+
33
+ /**
34
+ * Stops the loading indicator.
35
+ * @param options Value indicating whether to force the loading indicator to stop.
36
+ */
37
+ stop(options: {force?: boolean} = {}): void {
38
+ this.#requestCount--;
39
+ if (options.force || this.#requestCount <= 0) {
40
+ this.#requestCount = 0;
41
+ this.hidden = true;
42
+ document.body.classList.remove("loading");
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Renders this component.
48
+ * @returns The view template.
49
+ */
50
+ protected override render(): TemplateResult {
51
+ return html`<slot></slot>`;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Declaration merging.
57
+ */
58
+ declare global {
59
+
60
+ /**
61
+ * The map of HTML tag names.
62
+ */
63
+ interface HTMLElementTagNameMap {
64
+ "loading-indicator": LoadingIndicator;
65
+ }
66
+ }
@@ -0,0 +1,61 @@
1
+ import {html, LitElement, type TemplateResult} from "lit";
2
+ import {customElement} from "lit/decorators.js";
3
+
4
+ /**
5
+ * A component that shows up when the network is unavailable, and hides when connectivity is restored.
6
+ */
7
+ @customElement("offline-indicator")
8
+ export class OfflineIndicator extends LitElement {
9
+
10
+ /**
11
+ * Creates a new offline indicator.
12
+ */
13
+ constructor() {
14
+ super();
15
+ this.hidden = navigator.onLine;
16
+ }
17
+
18
+ /**
19
+ * Method invoked when this component is connected.
20
+ */
21
+ override connectedCallback(): void {
22
+ super.connectedCallback();
23
+ for (const event of ["online", "offline"]) addEventListener(event, this);
24
+ }
25
+
26
+ /**
27
+ * Method invoked when this component is disconnected.
28
+ */
29
+ override disconnectedCallback(): void {
30
+ for (const event of ["online", "offline"]) removeEventListener(event, this);
31
+ super.disconnectedCallback();
32
+ }
33
+
34
+ /**
35
+ * Handles the events.
36
+ */
37
+ handleEvent(): void {
38
+ this.hidden = navigator.onLine;
39
+ }
40
+
41
+ /**
42
+ * Renders this component.
43
+ * @returns The view template.
44
+ */
45
+ protected override render(): TemplateResult {
46
+ return html`<slot></slot>`;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Declaration merging.
52
+ */
53
+ declare global {
54
+
55
+ /**
56
+ * The map of HTML tag names.
57
+ */
58
+ interface HTMLElementTagNameMap {
59
+ "offline-indicator": OfflineIndicator;
60
+ }
61
+ }
@@ -0,0 +1,134 @@
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";
7
+
8
+ /**
9
+ * A dropdown menu for switching the color mode.
10
+ */
11
+ @customElement("theme-dropdown")
12
+ export class ThemeDropdown extends LitElement {
13
+
14
+ /**
15
+ * The alignment of the dropdown menu.
16
+ */
17
+ @property() alignment: MenuAlignment = MenuAlignment.End;
18
+
19
+ /**
20
+ * The label of the dropdown menu.
21
+ */
22
+ @property() label = "";
23
+
24
+ /**
25
+ * The key of the storage entry providing the saved theme.
26
+ */
27
+ @property() storageKey = "AppTheme";
28
+
29
+ /**
30
+ * The current application theme.
31
+ */
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)");
38
+
39
+ /**
40
+ * Creates a new theme dropdown.
41
+ */
42
+ constructor() {
43
+ super();
44
+ const theme = localStorage.getItem(this.storageKey) as AppTheme;
45
+ this.appTheme = Object.values(AppTheme).includes(theme) ? theme : AppTheme.System;
46
+ }
47
+
48
+ /**
49
+ * The current theme mode.
50
+ */
51
+ get theme(): AppTheme { return this.appTheme; }
52
+ set theme(value: AppTheme) {
53
+ localStorage.setItem(this.storageKey, this.appTheme = value);
54
+ this.#applyTheme();
55
+ }
56
+
57
+ /**
58
+ * Method invoked when this component is connected.
59
+ */
60
+ override connectedCallback(): void {
61
+ super.connectedCallback();
62
+ this.#applyTheme();
63
+ this.#mediaQuery.addEventListener("change", this);
64
+ }
65
+
66
+ /**
67
+ * Method invoked when this component is disconnected.
68
+ */
69
+ override disconnectedCallback(): void {
70
+ this.#mediaQuery.removeEventListener("change", this);
71
+ super.disconnectedCallback();
72
+ }
73
+
74
+ /**
75
+ * Handles the events.
76
+ */
77
+ handleEvent(): void {
78
+ this.#applyTheme();
79
+ }
80
+
81
+ /**
82
+ * Returns the node into which this component should render.
83
+ * @returns The node into which this component should render.
84
+ */
85
+ protected override createRenderRoot(): DocumentFragment|HTMLElement {
86
+ return this;
87
+ }
88
+
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
+ `;
112
+ }
113
+
114
+ /**
115
+ * Applies the theme to the document.
116
+ */
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();
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Declaration merging.
125
+ */
126
+ declare global {
127
+
128
+ /**
129
+ * The map of HTML tag names.
130
+ */
131
+ interface HTMLElementTagNameMap {
132
+ "theme-dropdown": ThemeDropdown;
133
+ }
134
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "include": ["*.ts"],
4
+ "compilerOptions": {
5
+ "composite": true,
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "importHelpers": true,
9
+ "noEmit": false,
10
+ "outDir": "../../../lib/UI",
11
+ "rootDir": ".",
12
+ "tsBuildInfoFile": "../../../var/UI.tsbuildinfo",
13
+ "useDefineForClassFields": false
14
+ },
15
+ "references": [
16
+ {"path": "../Abstractions/tsconfig.json"},
17
+ {"path": "../Html/tsconfig.json"}
18
+ ]
19
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ {"path": "Abstractions/tsconfig.json"},
5
+ {"path": "Base/tsconfig.json"},
6
+ {"path": "DependencyInjection/tsconfig.json"},
7
+ {"path": "Html/tsconfig.json"},
8
+ {"path": "Http/tsconfig.json"},
9
+ {"path": "UI/tsconfig.json"}
10
+ ]
11
+ }