@cedx/base 0.8.0 → 0.9.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.
- package/ReadMe.md +1 -1
- package/lib/UI/Components/LoadingIndicator.d.ts +8 -0
- package/lib/UI/Components/LoadingIndicator.d.ts.map +1 -1
- package/lib/UI/Components/LoadingIndicator.js +16 -4
- package/lib/UI/Components/OfflineIndicator.d.ts +7 -2
- package/lib/UI/Components/OfflineIndicator.d.ts.map +1 -1
- package/lib/UI/Components/OfflineIndicator.js +16 -6
- package/lib/UI/Components/TabActivator.d.ts +45 -0
- package/lib/UI/Components/TabActivator.d.ts.map +1 -0
- package/lib/UI/Components/TabActivator.js +67 -0
- package/lib/UI/Components/ThemeDropdown.d.ts +0 -8
- package/lib/UI/Components/ThemeDropdown.d.ts.map +1 -1
- package/lib/UI/Components/ThemeDropdown.js +10 -20
- package/lib/UI/StorageArea.d.ts +18 -0
- package/lib/UI/StorageArea.d.ts.map +1 -0
- package/lib/UI/StorageArea.js +13 -0
- package/package.json +1 -1
- package/src/Client/UI/Components/LoadingIndicator.ts +18 -4
- package/src/Client/UI/Components/OfflineIndicator.ts +19 -6
- package/src/Client/UI/Components/TabActivator.ts +87 -0
- package/src/Client/UI/Components/ThemeDropdown.ts +9 -21
- package/src/Client/UI/StorageArea.ts +20 -0
package/ReadMe.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Belin.io Base
|
|
2
|
-
   
|
|
3
3
|
|
|
4
4
|
Base library by [Cédric Belin](https://belin.io), full stack developer,
|
|
5
5
|
implemented in [C#](https://learn.microsoft.com/en-us/dotnet/csharp) and [TypeScript](https://www.typescriptlang.org).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/LoadingIndicator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAW;;IAchD;;OAEG;IACH,KAAK,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/LoadingIndicator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAW;;IAchD;;OAEG;IACH,IAAI,IAAI,IAAI;IAKZ;;OAEG;IACH,IAAI,IAAI,IAAI;IAKZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;;OAGG;IACH,IAAI,CAAC,OAAO,GAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAM,GAAG,IAAI;CAO3C;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,mBAAmB,EAAE,gBAAgB,CAAC;KACtC;CACD"}
|
|
@@ -12,13 +12,26 @@ export class LoadingIndicator extends HTMLElement {
|
|
|
12
12
|
static {
|
|
13
13
|
customElements.define("loading-indicator", this);
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Hides this component.
|
|
17
|
+
*/
|
|
18
|
+
hide() {
|
|
19
|
+
this.hidden = true;
|
|
20
|
+
document.body.classList.remove("loading");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Shows this component.
|
|
24
|
+
*/
|
|
25
|
+
show() {
|
|
26
|
+
this.hidden = false;
|
|
27
|
+
document.body.classList.add("loading");
|
|
28
|
+
}
|
|
15
29
|
/**
|
|
16
30
|
* Starts the loading indicator.
|
|
17
31
|
*/
|
|
18
32
|
start() {
|
|
19
33
|
this.#requestCount++;
|
|
20
|
-
this.
|
|
21
|
-
document.body.classList.add("loading");
|
|
34
|
+
this.show();
|
|
22
35
|
}
|
|
23
36
|
/**
|
|
24
37
|
* Stops the loading indicator.
|
|
@@ -28,8 +41,7 @@ export class LoadingIndicator extends HTMLElement {
|
|
|
28
41
|
this.#requestCount--;
|
|
29
42
|
if (options.force || this.#requestCount <= 0) {
|
|
30
43
|
this.#requestCount = 0;
|
|
31
|
-
this.
|
|
32
|
-
document.body.classList.remove("loading");
|
|
44
|
+
this.hide();
|
|
33
45
|
}
|
|
34
46
|
}
|
|
35
47
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* A component that shows up when the network is unavailable, and hides when connectivity is restored.
|
|
3
3
|
*/
|
|
4
4
|
export declare class OfflineIndicator extends HTMLElement {
|
|
5
|
+
#private;
|
|
5
6
|
/**
|
|
6
7
|
* Creates a new offline indicator.
|
|
7
8
|
*/
|
|
@@ -15,9 +16,13 @@ export declare class OfflineIndicator extends HTMLElement {
|
|
|
15
16
|
*/
|
|
16
17
|
disconnectedCallback(): void;
|
|
17
18
|
/**
|
|
18
|
-
*
|
|
19
|
+
* Hides this component.
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
+
hide(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Shows this component.
|
|
24
|
+
*/
|
|
25
|
+
show(): void;
|
|
21
26
|
}
|
|
22
27
|
/**
|
|
23
28
|
* Declaration merging.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OfflineIndicator.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/OfflineIndicator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAW
|
|
1
|
+
{"version":3,"file":"OfflineIndicator.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/OfflineIndicator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAW;;IAEhD;;OAEG;;IAaH;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAIzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAI5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;OAEG;IACH,IAAI,IAAI,IAAI;CASZ;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,mBAAmB,EAAE,gBAAgB,CAAC;KACtC;CACD"}
|
|
@@ -7,7 +7,7 @@ export class OfflineIndicator extends HTMLElement {
|
|
|
7
7
|
*/
|
|
8
8
|
constructor() {
|
|
9
9
|
super();
|
|
10
|
-
this
|
|
10
|
+
this.#updateHiddenState();
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Registers the component.
|
|
@@ -20,19 +20,29 @@ export class OfflineIndicator extends HTMLElement {
|
|
|
20
20
|
*/
|
|
21
21
|
connectedCallback() {
|
|
22
22
|
for (const event of ["online", "offline"])
|
|
23
|
-
addEventListener(event, this);
|
|
23
|
+
addEventListener(event, this.#updateHiddenState);
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Method invoked when this component is disconnected.
|
|
27
27
|
*/
|
|
28
28
|
disconnectedCallback() {
|
|
29
29
|
for (const event of ["online", "offline"])
|
|
30
|
-
removeEventListener(event, this);
|
|
30
|
+
removeEventListener(event, this.#updateHiddenState);
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* Hides this component.
|
|
34
34
|
*/
|
|
35
|
-
|
|
36
|
-
this.hidden =
|
|
35
|
+
hide() {
|
|
36
|
+
this.hidden = true;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Shows this component.
|
|
40
|
+
*/
|
|
41
|
+
show() {
|
|
42
|
+
this.hidden = false;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Updates the hidden state of this component according to the {@link navigator.onLine} property.
|
|
46
|
+
*/
|
|
47
|
+
#updateHiddenState = () => this.hidden = navigator.onLine;
|
|
38
48
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { StorageArea } from "../StorageArea.js";
|
|
2
|
+
/**
|
|
3
|
+
* A component that activates a tab, based on its index saved in the web storage.
|
|
4
|
+
*/
|
|
5
|
+
export declare class TabActivator extends HTMLElement {
|
|
6
|
+
/**
|
|
7
|
+
* The one-based index of the active tab.
|
|
8
|
+
*/
|
|
9
|
+
get activeTabIndex(): number;
|
|
10
|
+
set activeTabIndex(value: number);
|
|
11
|
+
/**
|
|
12
|
+
* The storage object corresponding to the current {@link storageArea}.
|
|
13
|
+
*/
|
|
14
|
+
get storage(): globalThis.Storage;
|
|
15
|
+
/**
|
|
16
|
+
* The storage area to use.
|
|
17
|
+
*/
|
|
18
|
+
get storageArea(): StorageArea;
|
|
19
|
+
set storageArea(value: StorageArea);
|
|
20
|
+
/**
|
|
21
|
+
* The key of the storage entry providing the active tab index.
|
|
22
|
+
*/
|
|
23
|
+
get storageKey(): string;
|
|
24
|
+
set storageKey(value: string);
|
|
25
|
+
/**
|
|
26
|
+
* The tab list.
|
|
27
|
+
*/
|
|
28
|
+
get tabs(): NodeListOf<HTMLButtonElement>;
|
|
29
|
+
/**
|
|
30
|
+
* Method invoked when this component is connected.
|
|
31
|
+
*/
|
|
32
|
+
connectedCallback(): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Declaration merging.
|
|
36
|
+
*/
|
|
37
|
+
declare global {
|
|
38
|
+
/**
|
|
39
|
+
* The map of HTML tag names.
|
|
40
|
+
*/
|
|
41
|
+
interface HTMLElementTagNameMap {
|
|
42
|
+
"tab-activator": TabActivator;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=TabActivator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabActivator.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/TabActivator.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,mBAAmB,CAAC;AAE9C;;GAEG;AACH,qBAAa,YAAa,SAAQ,WAAW;IAS5C;;OAEG;IACH,IAAI,cAAc,IAAI,MAAM,CAG3B;IACD,IAAI,cAAc,CAAC,KAAK,EAAE,MAAM,EAE/B;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,UAAU,CAAC,OAAO,CAEhC;IAED;;OAEG;IACH,IAAI,WAAW,IAAI,WAAW,CAG7B;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,WAAW,EAEjC;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAGvB;IACD,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,EAE3B;IAED;;OAEG;IACH,IAAI,IAAI,IAAI,UAAU,CAAC,iBAAiB,CAAC,CAExC;IAED;;OAEG;IACH,iBAAiB,IAAI,IAAI;CAQzB;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,eAAe,EAAE,YAAY,CAAC;KAC9B;CACD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Tab } from "bootstrap";
|
|
2
|
+
import { StorageArea } from "../StorageArea.js";
|
|
3
|
+
/**
|
|
4
|
+
* A component that activates a tab, based on its index saved in the web storage.
|
|
5
|
+
*/
|
|
6
|
+
export class TabActivator extends HTMLElement {
|
|
7
|
+
/**
|
|
8
|
+
* Registers the component.
|
|
9
|
+
*/
|
|
10
|
+
static {
|
|
11
|
+
customElements.define("tab-activator", this);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The one-based index of the active tab.
|
|
15
|
+
*/
|
|
16
|
+
get activeTabIndex() {
|
|
17
|
+
const index = Number.parseInt(this.storage.getItem(this.storageKey) ?? "1");
|
|
18
|
+
return Math.max(1, Math.min(this.tabs.length, Number.isNaN(index) ? 1 : index));
|
|
19
|
+
}
|
|
20
|
+
set activeTabIndex(value) {
|
|
21
|
+
this.storage.setItem(this.storageKey, value.toString());
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The storage object corresponding to the current {@link storageArea}.
|
|
25
|
+
*/
|
|
26
|
+
get storage() {
|
|
27
|
+
return this.storageArea == StorageArea.Local ? localStorage : sessionStorage;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The storage area to use.
|
|
31
|
+
*/
|
|
32
|
+
get storageArea() {
|
|
33
|
+
const value = this.getAttribute("storagearea");
|
|
34
|
+
return Object.values(StorageArea).includes(value) ? value : StorageArea.Session;
|
|
35
|
+
}
|
|
36
|
+
set storageArea(value) {
|
|
37
|
+
this.setAttribute("storagearea", value);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The key of the storage entry providing the active tab index.
|
|
41
|
+
*/
|
|
42
|
+
get storageKey() {
|
|
43
|
+
const value = this.getAttribute("storagekey") ?? "";
|
|
44
|
+
return value.trim() || "ActiveTabIndex";
|
|
45
|
+
}
|
|
46
|
+
set storageKey(value) {
|
|
47
|
+
this.setAttribute("storagekey", value);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The tab list.
|
|
51
|
+
*/
|
|
52
|
+
get tabs() {
|
|
53
|
+
return this.querySelectorAll(".nav-tabs button");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Method invoked when this component is connected.
|
|
57
|
+
*/
|
|
58
|
+
connectedCallback() {
|
|
59
|
+
const { activeTabIndex, tabs } = this;
|
|
60
|
+
for (let index = 1; index <= tabs.length; index++) {
|
|
61
|
+
const tab = tabs.item(index - 1);
|
|
62
|
+
tab.addEventListener("click", () => this.activeTabIndex = index);
|
|
63
|
+
if (index == activeTabIndex)
|
|
64
|
+
Tab.getOrCreateInstance(tab).show();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -9,10 +9,6 @@ export declare class ThemeDropdown extends HTMLElement {
|
|
|
9
9
|
* The list of observed attributes.
|
|
10
10
|
*/
|
|
11
11
|
static readonly observedAttributes: string[];
|
|
12
|
-
/**
|
|
13
|
-
* Creates a new theme dropdown.
|
|
14
|
-
*/
|
|
15
|
-
constructor();
|
|
16
12
|
/**
|
|
17
13
|
* The alignment of the dropdown menu.
|
|
18
14
|
*/
|
|
@@ -48,10 +44,6 @@ export declare class ThemeDropdown extends HTMLElement {
|
|
|
48
44
|
* Method invoked when this component is disconnected.
|
|
49
45
|
*/
|
|
50
46
|
disconnectedCallback(): void;
|
|
51
|
-
/**
|
|
52
|
-
* Handles the events.
|
|
53
|
-
*/
|
|
54
|
-
handleEvent(): void;
|
|
55
47
|
}
|
|
56
48
|
/**
|
|
57
49
|
* Declaration merging.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeDropdown.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/ThemeDropdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAU,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAElD;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW;;IAE7C;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,kBAAkB,WAAsC;
|
|
1
|
+
{"version":3,"file":"ThemeDropdown.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/ThemeDropdown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAU,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAElD;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW;;IAE7C;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,kBAAkB,WAAsC;IAcxE;;OAEG;IACH,IAAI,SAAS,IAAI,aAAa,CAG7B;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,aAAa,EAEjC;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,QAAQ,CAGvB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAG3B;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,MAAM,CAGlB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAEtB;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAGvB;IACD,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,EAE3B;IAED;;;;;OAKG;IACH,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAC,IAAI,GAAG,IAAI;IAwB/F;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAQzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;CAuB5B;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
|
|
@@ -12,14 +12,6 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
12
12
|
* The media query used to check the application theme.
|
|
13
13
|
*/
|
|
14
14
|
#mediaQuery = matchMedia("(prefers-color-scheme: dark)");
|
|
15
|
-
/**
|
|
16
|
-
* Creates a new theme dropdown.
|
|
17
|
-
*/
|
|
18
|
-
constructor() {
|
|
19
|
-
super();
|
|
20
|
-
for (const button of this.querySelectorAll("button"))
|
|
21
|
-
button.addEventListener("click", this.#setTheme.bind(this));
|
|
22
|
-
}
|
|
23
15
|
/**
|
|
24
16
|
* Registers the component.
|
|
25
17
|
*/
|
|
@@ -103,7 +95,9 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
103
95
|
* Method invoked when this component is connected.
|
|
104
96
|
*/
|
|
105
97
|
connectedCallback() {
|
|
106
|
-
this
|
|
98
|
+
for (const button of this.querySelectorAll("button"))
|
|
99
|
+
button.addEventListener("click", this.#setTheme);
|
|
100
|
+
this.#mediaQuery.addEventListener("change", this.#applyTheme);
|
|
107
101
|
const appTheme = localStorage.getItem(this.storageKey);
|
|
108
102
|
if (appTheme)
|
|
109
103
|
this.setAttribute("apptheme", appTheme);
|
|
@@ -112,29 +106,25 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
112
106
|
* Method invoked when this component is disconnected.
|
|
113
107
|
*/
|
|
114
108
|
disconnectedCallback() {
|
|
115
|
-
this
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
* Handles the events.
|
|
119
|
-
*/
|
|
120
|
-
handleEvent() {
|
|
121
|
-
this.#applyTheme();
|
|
109
|
+
for (const button of this.querySelectorAll("button"))
|
|
110
|
+
button.removeEventListener("click", this.#setTheme);
|
|
111
|
+
this.#mediaQuery.removeEventListener("change", this.#applyTheme);
|
|
122
112
|
}
|
|
123
113
|
/**
|
|
124
114
|
* Applies the application theme to the document.
|
|
125
115
|
*/
|
|
126
|
-
#applyTheme() {
|
|
116
|
+
#applyTheme = () => {
|
|
127
117
|
const { appTheme } = this;
|
|
128
118
|
const bsTheme = appTheme == AppTheme.System ? (this.#mediaQuery.matches ? AppTheme.Dark : AppTheme.Light) : appTheme;
|
|
129
119
|
document.documentElement.dataset.bsTheme = bsTheme.toLowerCase();
|
|
130
|
-
}
|
|
120
|
+
};
|
|
131
121
|
/**
|
|
132
122
|
* Changes the current application theme.
|
|
133
123
|
* @param event The dispatched event.
|
|
134
124
|
*/
|
|
135
|
-
#setTheme
|
|
125
|
+
#setTheme = event => {
|
|
136
126
|
event.preventDefault();
|
|
137
127
|
const button = event.target.closest("button");
|
|
138
128
|
this.appTheme = button.dataset.theme;
|
|
139
|
-
}
|
|
129
|
+
};
|
|
140
130
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifies the web storage area.
|
|
3
|
+
*/
|
|
4
|
+
export declare const StorageArea: Readonly<{
|
|
5
|
+
/**
|
|
6
|
+
* Indicates the local storage.
|
|
7
|
+
*/
|
|
8
|
+
Local: "Local";
|
|
9
|
+
/**
|
|
10
|
+
* Indicates the session storage.
|
|
11
|
+
*/
|
|
12
|
+
Session: "Session";
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Identifies the web storage area.
|
|
16
|
+
*/
|
|
17
|
+
export type StorageArea = typeof StorageArea[keyof typeof StorageArea];
|
|
18
|
+
//# sourceMappingURL=StorageArea.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StorageArea.d.ts","sourceRoot":"","sources":["../../src/Client/UI/StorageArea.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,WAAW;IAEvB;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -15,13 +15,28 @@ export class LoadingIndicator extends HTMLElement {
|
|
|
15
15
|
customElements.define("loading-indicator", this);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Hides this component.
|
|
20
|
+
*/
|
|
21
|
+
hide(): void {
|
|
22
|
+
this.hidden = true;
|
|
23
|
+
document.body.classList.remove("loading");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Shows this component.
|
|
28
|
+
*/
|
|
29
|
+
show(): void {
|
|
30
|
+
this.hidden = false;
|
|
31
|
+
document.body.classList.add("loading");
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
/**
|
|
19
35
|
* Starts the loading indicator.
|
|
20
36
|
*/
|
|
21
37
|
start(): void {
|
|
22
38
|
this.#requestCount++;
|
|
23
|
-
this.
|
|
24
|
-
document.body.classList.add("loading");
|
|
39
|
+
this.show();
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
/**
|
|
@@ -32,8 +47,7 @@ export class LoadingIndicator extends HTMLElement {
|
|
|
32
47
|
this.#requestCount--;
|
|
33
48
|
if (options.force || this.#requestCount <= 0) {
|
|
34
49
|
this.#requestCount = 0;
|
|
35
|
-
this.
|
|
36
|
-
document.body.classList.remove("loading");
|
|
50
|
+
this.hide();
|
|
37
51
|
}
|
|
38
52
|
}
|
|
39
53
|
}
|
|
@@ -8,7 +8,7 @@ export class OfflineIndicator extends HTMLElement {
|
|
|
8
8
|
*/
|
|
9
9
|
constructor() {
|
|
10
10
|
super();
|
|
11
|
-
this
|
|
11
|
+
this.#updateHiddenState();
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -22,22 +22,35 @@ export class OfflineIndicator extends HTMLElement {
|
|
|
22
22
|
* Method invoked when this component is connected.
|
|
23
23
|
*/
|
|
24
24
|
connectedCallback(): void {
|
|
25
|
-
for (const event of ["online", "offline"]) addEventListener(event, this);
|
|
25
|
+
for (const event of ["online", "offline"]) addEventListener(event, this.#updateHiddenState);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Method invoked when this component is disconnected.
|
|
30
30
|
*/
|
|
31
31
|
disconnectedCallback(): void {
|
|
32
|
-
for (const event of ["online", "offline"]) removeEventListener(event, this);
|
|
32
|
+
for (const event of ["online", "offline"]) removeEventListener(event, this.#updateHiddenState);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Hides this component.
|
|
37
37
|
*/
|
|
38
|
-
|
|
39
|
-
this.hidden =
|
|
38
|
+
hide(): void {
|
|
39
|
+
this.hidden = true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Shows this component.
|
|
44
|
+
*/
|
|
45
|
+
show(): void {
|
|
46
|
+
this.hidden = false;
|
|
40
47
|
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Updates the hidden state of this component according to the {@link navigator.onLine} property.
|
|
51
|
+
*/
|
|
52
|
+
readonly #updateHiddenState: () => void = () =>
|
|
53
|
+
this.hidden = navigator.onLine;
|
|
41
54
|
}
|
|
42
55
|
|
|
43
56
|
/**
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {Tab} from "bootstrap";
|
|
2
|
+
import {StorageArea} from "../StorageArea.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A component that activates a tab, based on its index saved in the web storage.
|
|
6
|
+
*/
|
|
7
|
+
export class TabActivator extends HTMLElement {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Registers the component.
|
|
11
|
+
*/
|
|
12
|
+
static {
|
|
13
|
+
customElements.define("tab-activator", this);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The one-based index of the active tab.
|
|
18
|
+
*/
|
|
19
|
+
get activeTabIndex(): number {
|
|
20
|
+
const index = Number.parseInt(this.storage.getItem(this.storageKey) ?? "1");
|
|
21
|
+
return Math.max(1, Math.min(this.tabs.length, Number.isNaN(index) ? 1 : index));
|
|
22
|
+
}
|
|
23
|
+
set activeTabIndex(value: number) {
|
|
24
|
+
this.storage.setItem(this.storageKey, value.toString());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The storage object corresponding to the current {@link storageArea}.
|
|
29
|
+
*/
|
|
30
|
+
get storage(): globalThis.Storage {
|
|
31
|
+
return this.storageArea == StorageArea.Local ? localStorage : sessionStorage;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The storage area to use.
|
|
36
|
+
*/
|
|
37
|
+
get storageArea(): StorageArea {
|
|
38
|
+
const value = this.getAttribute("storagearea") as StorageArea;
|
|
39
|
+
return Object.values(StorageArea).includes(value) ? value : StorageArea.Session;
|
|
40
|
+
}
|
|
41
|
+
set storageArea(value: StorageArea) {
|
|
42
|
+
this.setAttribute("storagearea", value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The key of the storage entry providing the active tab index.
|
|
47
|
+
*/
|
|
48
|
+
get storageKey(): string {
|
|
49
|
+
const value = this.getAttribute("storagekey") ?? "";
|
|
50
|
+
return value.trim() || "ActiveTabIndex";
|
|
51
|
+
}
|
|
52
|
+
set storageKey(value: string) {
|
|
53
|
+
this.setAttribute("storagekey", value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The tab list.
|
|
58
|
+
*/
|
|
59
|
+
get tabs(): NodeListOf<HTMLButtonElement> {
|
|
60
|
+
return this.querySelectorAll(".nav-tabs button");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Method invoked when this component is connected.
|
|
65
|
+
*/
|
|
66
|
+
connectedCallback(): void {
|
|
67
|
+
const {activeTabIndex, tabs} = this;
|
|
68
|
+
for (let index = 1; index <= tabs.length; index++) {
|
|
69
|
+
const tab = tabs.item(index - 1);
|
|
70
|
+
tab.addEventListener("click", () => this.activeTabIndex = index);
|
|
71
|
+
if (index == activeTabIndex) Tab.getOrCreateInstance(tab).show();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Declaration merging.
|
|
78
|
+
*/
|
|
79
|
+
declare global {
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The map of HTML tag names.
|
|
83
|
+
*/
|
|
84
|
+
interface HTMLElementTagNameMap {
|
|
85
|
+
"tab-activator": TabActivator;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -16,14 +16,6 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
16
16
|
*/
|
|
17
17
|
readonly #mediaQuery = matchMedia("(prefers-color-scheme: dark)");
|
|
18
18
|
|
|
19
|
-
/**
|
|
20
|
-
* Creates a new theme dropdown.
|
|
21
|
-
*/
|
|
22
|
-
constructor() {
|
|
23
|
-
super();
|
|
24
|
-
for (const button of this.querySelectorAll("button")) button.addEventListener("click", this.#setTheme.bind(this));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
19
|
/**
|
|
28
20
|
* Registers the component.
|
|
29
21
|
*/
|
|
@@ -110,7 +102,9 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
110
102
|
* Method invoked when this component is connected.
|
|
111
103
|
*/
|
|
112
104
|
connectedCallback(): void {
|
|
113
|
-
this
|
|
105
|
+
for (const button of this.querySelectorAll("button")) button.addEventListener("click", this.#setTheme);
|
|
106
|
+
this.#mediaQuery.addEventListener("change", this.#applyTheme);
|
|
107
|
+
|
|
114
108
|
const appTheme = localStorage.getItem(this.storageKey) as AppTheme|null;
|
|
115
109
|
if (appTheme) this.setAttribute("apptheme", appTheme);
|
|
116
110
|
}
|
|
@@ -119,34 +113,28 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
119
113
|
* Method invoked when this component is disconnected.
|
|
120
114
|
*/
|
|
121
115
|
disconnectedCallback(): void {
|
|
122
|
-
this
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Handles the events.
|
|
127
|
-
*/
|
|
128
|
-
handleEvent(): void {
|
|
129
|
-
this.#applyTheme();
|
|
116
|
+
for (const button of this.querySelectorAll("button")) button.removeEventListener("click", this.#setTheme);
|
|
117
|
+
this.#mediaQuery.removeEventListener("change", this.#applyTheme);
|
|
130
118
|
}
|
|
131
119
|
|
|
132
120
|
/**
|
|
133
121
|
* Applies the application theme to the document.
|
|
134
122
|
*/
|
|
135
|
-
|
|
123
|
+
readonly #applyTheme: () => void = () => {
|
|
136
124
|
const {appTheme} = this;
|
|
137
125
|
const bsTheme = appTheme == AppTheme.System ? (this.#mediaQuery.matches ? AppTheme.Dark : AppTheme.Light) : appTheme;
|
|
138
126
|
document.documentElement.dataset.bsTheme = bsTheme.toLowerCase();
|
|
139
|
-
}
|
|
127
|
+
};
|
|
140
128
|
|
|
141
129
|
/**
|
|
142
130
|
* Changes the current application theme.
|
|
143
131
|
* @param event The dispatched event.
|
|
144
132
|
*/
|
|
145
|
-
#setTheme(event: Event)
|
|
133
|
+
readonly #setTheme: (event: Event) => void = event => {
|
|
146
134
|
event.preventDefault();
|
|
147
135
|
const button = (event.target as HTMLElement).closest("button")!;
|
|
148
136
|
this.appTheme = button.dataset.theme! as AppTheme;
|
|
149
|
-
}
|
|
137
|
+
};
|
|
150
138
|
}
|
|
151
139
|
|
|
152
140
|
/**
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifies the web storage area.
|
|
3
|
+
*/
|
|
4
|
+
export const StorageArea = Object.freeze({
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Indicates the local storage.
|
|
8
|
+
*/
|
|
9
|
+
Local: "Local",
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Indicates the session storage.
|
|
13
|
+
*/
|
|
14
|
+
Session: "Session"
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Identifies the web storage area.
|
|
19
|
+
*/
|
|
20
|
+
export type StorageArea = typeof StorageArea[keyof typeof StorageArea];
|