@cedx/base 0.10.1 → 0.11.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/Data/Pagination.d.ts +3 -3
- package/lib/Data/Pagination.js +10 -10
- package/lib/UI/Components/KeyboardAccelerator.d.ts +11 -0
- package/lib/UI/Components/KeyboardAccelerator.d.ts.map +1 -1
- package/lib/UI/Components/KeyboardAccelerator.js +1 -1
- package/lib/UI/Components/OfflineIndicator.d.ts +0 -4
- package/lib/UI/Components/OfflineIndicator.d.ts.map +1 -1
- package/lib/UI/Components/OfflineIndicator.js +1 -7
- package/lib/UI/Components/TabActivator.d.ts +1 -1
- package/lib/UI/Components/TabActivator.js +6 -6
- package/lib/UI/Components/ThemeDropdown.d.ts +4 -0
- package/lib/UI/Components/ThemeDropdown.d.ts.map +1 -1
- package/lib/UI/Components/ThemeDropdown.js +9 -5
- package/lib/UI/ElementExtensions.d.ts +13 -0
- package/lib/UI/ElementExtensions.d.ts.map +1 -0
- package/lib/UI/ElementExtensions.js +18 -0
- package/lib/UI/Size.d.ts +34 -0
- package/lib/UI/Size.d.ts.map +1 -0
- package/lib/UI/Size.js +29 -0
- package/lib/UI/Variant.d.ts +26 -0
- package/lib/UI/Variant.d.ts.map +1 -0
- package/lib/UI/Variant.js +21 -0
- package/lib/UI/ViewportScroller.js +1 -1
- package/package.json +5 -5
- package/src/Client/Data/Pagination.ts +11 -11
- package/src/Client/UI/Components/KeyboardAccelerator.ts +14 -1
- package/src/Client/UI/Components/MessageBox.old +242 -0
- package/src/Client/UI/Components/OfflineIndicator.ts +1 -8
- package/src/Client/UI/Components/TabActivator.ts +6 -6
- package/src/Client/UI/Components/ThemeDropdown.ts +9 -4
- package/src/Client/UI/Components/Toaster.old +0 -0
- package/src/Client/UI/ElementExtensions.ts +19 -0
- package/src/Client/UI/Size.ts +40 -0
- package/src/Client/UI/Variant.ts +30 -0
- package/src/Client/UI/ViewportScroller.ts +1 -1
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).
|
package/lib/Data/Pagination.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class Pagination {
|
|
|
9
9
|
*/
|
|
10
10
|
constructor(options?: PaginationOptions);
|
|
11
11
|
/**
|
|
12
|
-
* The
|
|
12
|
+
* The current page index.
|
|
13
13
|
*/
|
|
14
14
|
get currentPageIndex(): number;
|
|
15
15
|
set currentPageIndex(value: number);
|
|
@@ -27,7 +27,7 @@ export declare class Pagination {
|
|
|
27
27
|
get itemsPerPage(): number;
|
|
28
28
|
set itemsPerPage(value: number);
|
|
29
29
|
/**
|
|
30
|
-
* The
|
|
30
|
+
* The last page index.
|
|
31
31
|
*/
|
|
32
32
|
get lastPageIndex(): number;
|
|
33
33
|
/**
|
|
@@ -59,7 +59,7 @@ export declare class Pagination {
|
|
|
59
59
|
*/
|
|
60
60
|
export type PaginationOptions = Partial<{
|
|
61
61
|
/**
|
|
62
|
-
* The
|
|
62
|
+
* The current page index.
|
|
63
63
|
*/
|
|
64
64
|
currentPageIndex: number;
|
|
65
65
|
/**
|
package/lib/Data/Pagination.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class Pagination {
|
|
5
5
|
/**
|
|
6
|
-
* The
|
|
6
|
+
* The current page index.
|
|
7
7
|
*/
|
|
8
8
|
#currentPageIndex;
|
|
9
9
|
/**
|
|
@@ -19,18 +19,18 @@ export class Pagination {
|
|
|
19
19
|
* @param options An object providing values to initialize this instance.
|
|
20
20
|
*/
|
|
21
21
|
constructor(options = {}) {
|
|
22
|
-
this.currentPageIndex = options.currentPageIndex ??
|
|
22
|
+
this.currentPageIndex = options.currentPageIndex ?? 0;
|
|
23
23
|
this.itemsPerPage = options.itemsPerPage ?? 25;
|
|
24
24
|
this.totalItemCount = options.totalItemCount ?? 0;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
-
* The
|
|
27
|
+
* The current page index.
|
|
28
28
|
*/
|
|
29
29
|
get currentPageIndex() {
|
|
30
30
|
return this.#currentPageIndex;
|
|
31
31
|
}
|
|
32
32
|
set currentPageIndex(value) {
|
|
33
|
-
this.#currentPageIndex = Math.max(
|
|
33
|
+
this.#currentPageIndex = Math.max(0, value);
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Value indicating whether a next page exists.
|
|
@@ -42,7 +42,7 @@ export class Pagination {
|
|
|
42
42
|
* Value indicating whether a previous page exists.
|
|
43
43
|
*/
|
|
44
44
|
get hasPreviousPage() {
|
|
45
|
-
return this.#currentPageIndex >
|
|
45
|
+
return this.#currentPageIndex > 0;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
48
|
* The number of items per page.
|
|
@@ -54,10 +54,10 @@ export class Pagination {
|
|
|
54
54
|
this.#itemsPerPage = Math.max(1, Math.min(1000, value));
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* The
|
|
57
|
+
* The last page index.
|
|
58
58
|
*/
|
|
59
59
|
get lastPageIndex() {
|
|
60
|
-
return this.#totalItemCount > 0 ? Math.ceil(this.#totalItemCount / this.#itemsPerPage) :
|
|
60
|
+
return this.#totalItemCount > 0 ? Math.ceil(this.#totalItemCount / this.#itemsPerPage) - 1 : 0;
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* The data limit.
|
|
@@ -69,13 +69,13 @@ export class Pagination {
|
|
|
69
69
|
* The data offset.
|
|
70
70
|
*/
|
|
71
71
|
get offset() {
|
|
72
|
-
return
|
|
72
|
+
return this.#currentPageIndex * this.#itemsPerPage;
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* The search parameters corresponding to this object.
|
|
76
76
|
*/
|
|
77
77
|
get searchParams() {
|
|
78
|
-
return new URLSearchParams({ page: this.#currentPageIndex.toString(), perPage: this.#itemsPerPage.toString() });
|
|
78
|
+
return new URLSearchParams({ page: (this.#currentPageIndex + 1).toString(), perPage: this.#itemsPerPage.toString() });
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* The total number of items.
|
|
@@ -93,7 +93,7 @@ export class Pagination {
|
|
|
93
93
|
*/
|
|
94
94
|
static fromResponse(response) {
|
|
95
95
|
return new this({
|
|
96
|
-
currentPageIndex: Number(response.headers.get("X-Pagination-Current-Page") ?? "1"),
|
|
96
|
+
currentPageIndex: Number(response.headers.get("X-Pagination-Current-Page") ?? "1") - 1,
|
|
97
97
|
itemsPerPage: Number(response.headers.get("X-Pagination-Per-Page") ?? "25"),
|
|
98
98
|
totalItemCount: Number(response.headers.get("X-Pagination-Total-Count") ?? "0")
|
|
99
99
|
});
|
|
@@ -22,4 +22,15 @@ export declare class KeyboardAccelerator extends HTMLElement {
|
|
|
22
22
|
*/
|
|
23
23
|
disconnectedCallback(): void;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Declaration merging.
|
|
27
|
+
*/
|
|
28
|
+
declare global {
|
|
29
|
+
/**
|
|
30
|
+
* The map of HTML tag names.
|
|
31
|
+
*/
|
|
32
|
+
interface HTMLElementTagNameMap {
|
|
33
|
+
"keyboard-accelerator": KeyboardAccelerator;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
25
36
|
//# sourceMappingURL=KeyboardAccelerator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeyboardAccelerator.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/KeyboardAccelerator.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;;IAcnD;;OAEG;IACH,IAAI,GAAG,IAAI,MAAM,CAEhB;IACD,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,EAEpB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAItB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAK1B;IAED;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAIzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;CAuB5B"}
|
|
1
|
+
{"version":3,"file":"KeyboardAccelerator.d.ts","sourceRoot":"","sources":["../../../src/Client/UI/Components/KeyboardAccelerator.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,WAAW;;IAcnD;;OAEG;IACH,IAAI,GAAG,IAAI,MAAM,CAEhB;IACD,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,EAEpB;IAED;;OAEG;IACH,IAAI,SAAS,IAAI,MAAM,CAItB;IACD,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAK1B;IAED;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAIzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;CAuB5B;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,sBAAsB,EAAE,mBAAmB,CAAC;KAC5C;CACD"}
|
|
@@ -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;;IAShD;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAKzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;CAS5B;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,mBAAmB,EAAE,gBAAgB,CAAC;KACtC;CACD"}
|
|
@@ -2,13 +2,6 @@
|
|
|
2
2
|
* A component that shows up when the network is unavailable, and hides when connectivity is restored.
|
|
3
3
|
*/
|
|
4
4
|
export class OfflineIndicator extends HTMLElement {
|
|
5
|
-
/**
|
|
6
|
-
* Creates a new offline indicator.
|
|
7
|
-
*/
|
|
8
|
-
constructor() {
|
|
9
|
-
super();
|
|
10
|
-
this.#updateHiddenState();
|
|
11
|
-
}
|
|
12
5
|
/**
|
|
13
6
|
* Registers the component.
|
|
14
7
|
*/
|
|
@@ -19,6 +12,7 @@ export class OfflineIndicator extends HTMLElement {
|
|
|
19
12
|
* Method invoked when this component is connected.
|
|
20
13
|
*/
|
|
21
14
|
connectedCallback() {
|
|
15
|
+
this.#updateHiddenState();
|
|
22
16
|
for (const event of ["online", "offline"])
|
|
23
17
|
addEventListener(event, this.#updateHiddenState);
|
|
24
18
|
}
|
|
@@ -4,7 +4,7 @@ import { StorageArea } from "../StorageArea.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class TabActivator extends HTMLElement {
|
|
6
6
|
/**
|
|
7
|
-
* The
|
|
7
|
+
* The index of the active tab.
|
|
8
8
|
*/
|
|
9
9
|
get activeTabIndex(): number;
|
|
10
10
|
set activeTabIndex(value: number);
|
|
@@ -11,14 +11,14 @@ export class TabActivator extends HTMLElement {
|
|
|
11
11
|
customElements.define("tab-activator", this);
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* The
|
|
14
|
+
* The index of the active tab.
|
|
15
15
|
*/
|
|
16
16
|
get activeTabIndex() {
|
|
17
|
-
const index = Number
|
|
18
|
-
return Math.max(
|
|
17
|
+
const index = Number(this.storage.getItem(this.storageKey) ?? "1");
|
|
18
|
+
return Math.max(0, Math.min(this.tabs.length, Number.isNaN(index) ? 0 : index - 1));
|
|
19
19
|
}
|
|
20
20
|
set activeTabIndex(value) {
|
|
21
|
-
this.storage.setItem(this.storageKey, value.toString());
|
|
21
|
+
this.storage.setItem(this.storageKey, (value + 1).toString());
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* The storage object corresponding to the current {@link storageArea}.
|
|
@@ -57,8 +57,8 @@ export class TabActivator extends HTMLElement {
|
|
|
57
57
|
*/
|
|
58
58
|
connectedCallback() {
|
|
59
59
|
const { activeTabIndex, tabs } = this;
|
|
60
|
-
for (let index =
|
|
61
|
-
const tab = tabs.item(index
|
|
60
|
+
for (let index = 0; index < tabs.length - 1; index++) {
|
|
61
|
+
const tab = tabs.item(index);
|
|
62
62
|
tab.addEventListener("click", () => this.activeTabIndex = index);
|
|
63
63
|
if (index == activeTabIndex)
|
|
64
64
|
Tab.getOrCreateInstance(tab).show();
|
|
@@ -9,6 +9,10 @@ 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();
|
|
12
16
|
/**
|
|
13
17
|
* The alignment of the dropdown menu.
|
|
14
18
|
*/
|
|
@@ -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;IAOxE;;OAEG;;IAaH;;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;IAMzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;CAsB5B;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
|
|
@@ -12,6 +12,14 @@ 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);
|
|
22
|
+
}
|
|
15
23
|
/**
|
|
16
24
|
* Registers the component.
|
|
17
25
|
*/
|
|
@@ -95,19 +103,15 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
95
103
|
* Method invoked when this component is connected.
|
|
96
104
|
*/
|
|
97
105
|
connectedCallback() {
|
|
98
|
-
for (const button of this.querySelectorAll("button"))
|
|
99
|
-
button.addEventListener("click", this.#setTheme);
|
|
100
|
-
this.#mediaQuery.addEventListener("change", this.#applyTheme);
|
|
101
106
|
const appTheme = localStorage.getItem(this.storageKey);
|
|
102
107
|
if (appTheme)
|
|
103
108
|
this.setAttribute("apptheme", appTheme);
|
|
109
|
+
this.#mediaQuery.addEventListener("change", this.#applyTheme);
|
|
104
110
|
}
|
|
105
111
|
/**
|
|
106
112
|
* Method invoked when this component is disconnected.
|
|
107
113
|
*/
|
|
108
114
|
disconnectedCallback() {
|
|
109
|
-
for (const button of this.querySelectorAll("button"))
|
|
110
|
-
button.removeEventListener("click", this.#setTheme);
|
|
111
115
|
this.#mediaQuery.removeEventListener("change", this.#applyTheme);
|
|
112
116
|
}
|
|
113
117
|
/**
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a document fragment from the specified HTML string.
|
|
3
|
+
* @param childContent The HTML string providing the child content.
|
|
4
|
+
* @returns The document fragment corresponding to the specified HTML string.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createDocumentFragment(childContent: string): DocumentFragment;
|
|
7
|
+
/**
|
|
8
|
+
* Returns a promise that resolves when the specified element has finished all its animations.
|
|
9
|
+
* @param element The target element.
|
|
10
|
+
* @returns The element animations.
|
|
11
|
+
*/
|
|
12
|
+
export declare function waitForAnimations(element: Element): Promise<Array<PromiseSettledResult<Animation>>>;
|
|
13
|
+
//# sourceMappingURL=ElementExtensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementExtensions.d.ts","sourceRoot":"","sources":["../../src/Client/UI/ElementExtensions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,CAEnG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a document fragment from the specified HTML string.
|
|
3
|
+
* @param childContent The HTML string providing the child content.
|
|
4
|
+
* @returns The document fragment corresponding to the specified HTML string.
|
|
5
|
+
*/
|
|
6
|
+
export function createDocumentFragment(childContent) {
|
|
7
|
+
const template = document.createElement("template");
|
|
8
|
+
template.innerHTML = childContent;
|
|
9
|
+
return template.content;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns a promise that resolves when the specified element has finished all its animations.
|
|
13
|
+
* @param element The target element.
|
|
14
|
+
* @returns The element animations.
|
|
15
|
+
*/
|
|
16
|
+
export function waitForAnimations(element) {
|
|
17
|
+
return Promise.allSettled(element.getAnimations().map(animation => animation.finished));
|
|
18
|
+
}
|
package/lib/UI/Size.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the size of a component.
|
|
3
|
+
*/
|
|
4
|
+
export declare const Size: Readonly<{
|
|
5
|
+
/**
|
|
6
|
+
* An extra small size.
|
|
7
|
+
*/
|
|
8
|
+
ExtraSmall: "xs";
|
|
9
|
+
/**
|
|
10
|
+
* A small size.
|
|
11
|
+
*/
|
|
12
|
+
Small: "sm";
|
|
13
|
+
/**
|
|
14
|
+
* A medium size.
|
|
15
|
+
*/
|
|
16
|
+
Medium: "md";
|
|
17
|
+
/**
|
|
18
|
+
* A large size.
|
|
19
|
+
*/
|
|
20
|
+
Large: "lg";
|
|
21
|
+
/**
|
|
22
|
+
* An extra large size.
|
|
23
|
+
*/
|
|
24
|
+
ExtraLarge: "xl";
|
|
25
|
+
/**
|
|
26
|
+
* An extra extra large size.
|
|
27
|
+
*/
|
|
28
|
+
ExtraExtraLarge: "xxl";
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Defines the size of a component.
|
|
32
|
+
*/
|
|
33
|
+
export type Size = typeof Size[keyof typeof Size];
|
|
34
|
+
//# sourceMappingURL=Size.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Size.d.ts","sourceRoot":"","sources":["../../src/Client/UI/Size.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,IAAI;IAEhB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC"}
|
package/lib/UI/Size.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the size of a component.
|
|
3
|
+
*/
|
|
4
|
+
export const Size = Object.freeze({
|
|
5
|
+
/**
|
|
6
|
+
* An extra small size.
|
|
7
|
+
*/
|
|
8
|
+
ExtraSmall: "xs",
|
|
9
|
+
/**
|
|
10
|
+
* A small size.
|
|
11
|
+
*/
|
|
12
|
+
Small: "sm",
|
|
13
|
+
/**
|
|
14
|
+
* A medium size.
|
|
15
|
+
*/
|
|
16
|
+
Medium: "md",
|
|
17
|
+
/**
|
|
18
|
+
* A large size.
|
|
19
|
+
*/
|
|
20
|
+
Large: "lg",
|
|
21
|
+
/**
|
|
22
|
+
* An extra large size.
|
|
23
|
+
*/
|
|
24
|
+
ExtraLarge: "xl",
|
|
25
|
+
/**
|
|
26
|
+
* An extra extra large size.
|
|
27
|
+
*/
|
|
28
|
+
ExtraExtraLarge: "xxl"
|
|
29
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines tone variants.
|
|
3
|
+
*/
|
|
4
|
+
export declare const Variant: Readonly<{
|
|
5
|
+
/**
|
|
6
|
+
* A dark variant.
|
|
7
|
+
*/
|
|
8
|
+
Dark: "dark";
|
|
9
|
+
/**
|
|
10
|
+
* A light variant.
|
|
11
|
+
*/
|
|
12
|
+
Light: "light";
|
|
13
|
+
/**
|
|
14
|
+
* A primary variant.
|
|
15
|
+
*/
|
|
16
|
+
Primary: "primary";
|
|
17
|
+
/**
|
|
18
|
+
* A secondary variant.
|
|
19
|
+
*/
|
|
20
|
+
Secondary: "secondary";
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Defines tone variants.
|
|
24
|
+
*/
|
|
25
|
+
export type Variant = typeof Variant[keyof typeof Variant];
|
|
26
|
+
//# sourceMappingURL=Variant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Variant.d.ts","sourceRoot":"","sources":["../../src/Client/UI/Variant.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,OAAO;IAEnB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines tone variants.
|
|
3
|
+
*/
|
|
4
|
+
export const Variant = Object.freeze({
|
|
5
|
+
/**
|
|
6
|
+
* A dark variant.
|
|
7
|
+
*/
|
|
8
|
+
Dark: "dark",
|
|
9
|
+
/**
|
|
10
|
+
* A light variant.
|
|
11
|
+
*/
|
|
12
|
+
Light: "light",
|
|
13
|
+
/**
|
|
14
|
+
* A primary variant.
|
|
15
|
+
*/
|
|
16
|
+
Primary: "primary",
|
|
17
|
+
/**
|
|
18
|
+
* A secondary variant.
|
|
19
|
+
*/
|
|
20
|
+
Secondary: "secondary"
|
|
21
|
+
});
|
|
@@ -27,7 +27,7 @@ export class ViewportScroller {
|
|
|
27
27
|
const navbar = document.body.querySelector(".navbar");
|
|
28
28
|
this.#scrollOffset += (navbar?.offsetHeight ?? 0);
|
|
29
29
|
}
|
|
30
|
-
const actionBar = document.body.querySelector("action-bar
|
|
30
|
+
const actionBar = document.body.querySelector("action-bar");
|
|
31
31
|
return this.#scrollOffset + (actionBar?.offsetHeight ?? 0);
|
|
32
32
|
}
|
|
33
33
|
/**
|
package/package.json
CHANGED
|
@@ -7,23 +7,23 @@
|
|
|
7
7
|
"name": "@cedx/base",
|
|
8
8
|
"repository": "cedx/base",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.11.0",
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@playwright/browser-chromium": "^1.
|
|
12
|
+
"@playwright/browser-chromium": "^1.55.0",
|
|
13
13
|
"@types/bootstrap": "^5.2.10",
|
|
14
14
|
"@types/chai": "^5.2.2",
|
|
15
15
|
"@types/mocha": "^10.0.10",
|
|
16
16
|
"@types/node": "^24.3.0",
|
|
17
17
|
"@types/serve-handler": "^6.1.4",
|
|
18
|
-
"chai": "^
|
|
18
|
+
"chai": "^6.0.0",
|
|
19
19
|
"esbuild": "^0.25.9",
|
|
20
20
|
"globals": "^16.3.0",
|
|
21
21
|
"mocha": "^11.7.1",
|
|
22
|
-
"playwright": "^1.
|
|
22
|
+
"playwright": "^1.55.0",
|
|
23
23
|
"serve-handler": "^6.1.6",
|
|
24
24
|
"typedoc": "^0.28.10",
|
|
25
25
|
"typescript": "^5.9.2",
|
|
26
|
-
"typescript-eslint": "^8.
|
|
26
|
+
"typescript-eslint": "^8.40.0"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">=24.0.0"
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
export class Pagination {
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* The
|
|
7
|
+
* The current page index.
|
|
8
8
|
*/
|
|
9
9
|
#currentPageIndex!: number;
|
|
10
10
|
|
|
@@ -23,19 +23,19 @@ export class Pagination {
|
|
|
23
23
|
* @param options An object providing values to initialize this instance.
|
|
24
24
|
*/
|
|
25
25
|
constructor(options: PaginationOptions = {}) {
|
|
26
|
-
this.currentPageIndex = options.currentPageIndex ??
|
|
26
|
+
this.currentPageIndex = options.currentPageIndex ?? 0;
|
|
27
27
|
this.itemsPerPage = options.itemsPerPage ?? 25;
|
|
28
28
|
this.totalItemCount = options.totalItemCount ?? 0;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* The
|
|
32
|
+
* The current page index.
|
|
33
33
|
*/
|
|
34
34
|
get currentPageIndex(): number {
|
|
35
35
|
return this.#currentPageIndex;
|
|
36
36
|
}
|
|
37
37
|
set currentPageIndex(value: number) {
|
|
38
|
-
this.#currentPageIndex = Math.max(
|
|
38
|
+
this.#currentPageIndex = Math.max(0, value);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
@@ -49,7 +49,7 @@ export class Pagination {
|
|
|
49
49
|
* Value indicating whether a previous page exists.
|
|
50
50
|
*/
|
|
51
51
|
get hasPreviousPage(): boolean {
|
|
52
|
-
return this.#currentPageIndex >
|
|
52
|
+
return this.#currentPageIndex > 0;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
@@ -63,10 +63,10 @@ export class Pagination {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* The
|
|
66
|
+
* The last page index.
|
|
67
67
|
*/
|
|
68
68
|
get lastPageIndex(): number {
|
|
69
|
-
return this.#totalItemCount > 0 ? Math.ceil(this.#totalItemCount / this.#itemsPerPage) :
|
|
69
|
+
return this.#totalItemCount > 0 ? Math.ceil(this.#totalItemCount / this.#itemsPerPage) - 1 : 0;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -80,14 +80,14 @@ export class Pagination {
|
|
|
80
80
|
* The data offset.
|
|
81
81
|
*/
|
|
82
82
|
get offset(): number {
|
|
83
|
-
return
|
|
83
|
+
return this.#currentPageIndex * this.#itemsPerPage;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* The search parameters corresponding to this object.
|
|
88
88
|
*/
|
|
89
89
|
get searchParams(): URLSearchParams {
|
|
90
|
-
return new URLSearchParams({page: this.#currentPageIndex.toString(), perPage: this.#itemsPerPage.toString()});
|
|
90
|
+
return new URLSearchParams({page: (this.#currentPageIndex + 1).toString(), perPage: this.#itemsPerPage.toString()});
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
@@ -107,7 +107,7 @@ export class Pagination {
|
|
|
107
107
|
*/
|
|
108
108
|
static fromResponse(response: Response): Pagination {
|
|
109
109
|
return new this({
|
|
110
|
-
currentPageIndex: Number(response.headers.get("X-Pagination-Current-Page") ?? "1"),
|
|
110
|
+
currentPageIndex: Number(response.headers.get("X-Pagination-Current-Page") ?? "1") - 1,
|
|
111
111
|
itemsPerPage: Number(response.headers.get("X-Pagination-Per-Page") ?? "25"),
|
|
112
112
|
totalItemCount: Number(response.headers.get("X-Pagination-Total-Count") ?? "0")
|
|
113
113
|
});
|
|
@@ -120,7 +120,7 @@ export class Pagination {
|
|
|
120
120
|
export type PaginationOptions = Partial<{
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
|
-
* The
|
|
123
|
+
* The current page index.
|
|
124
124
|
*/
|
|
125
125
|
currentPageIndex: number;
|
|
126
126
|
|
|
@@ -73,6 +73,19 @@ export class KeyboardAccelerator extends HTMLElement {
|
|
|
73
73
|
if (!(modifiers & KeyboardModifiers.Meta) && event.metaKey) return;
|
|
74
74
|
|
|
75
75
|
event.preventDefault();
|
|
76
|
-
(this.firstElementChild as HTMLElement)
|
|
76
|
+
(this.firstElementChild as HTMLElement|null)?.click();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Declaration merging.
|
|
82
|
+
*/
|
|
83
|
+
declare global {
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The map of HTML tag names.
|
|
87
|
+
*/
|
|
88
|
+
interface HTMLElementTagNameMap {
|
|
89
|
+
"keyboard-accelerator": KeyboardAccelerator;
|
|
77
90
|
}
|
|
78
91
|
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import {Context, getIcon} from "../Context.js";
|
|
2
|
+
import {Variant} from "../Variant.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Specifies the return value of a message box.
|
|
6
|
+
*/
|
|
7
|
+
export const MessageBoxResult = Object.freeze({
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The message box does not return any value.
|
|
11
|
+
*/
|
|
12
|
+
None: "",
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The return value of the message box is "OK".
|
|
16
|
+
*/
|
|
17
|
+
OK: "OK",
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The return value of the message box is "Cancel".
|
|
21
|
+
*/
|
|
22
|
+
Cancel: "Cancel"
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Specifies the return value of a message box.
|
|
27
|
+
*/
|
|
28
|
+
export type MessageBoxResult = typeof MessageBoxResult[keyof typeof MessageBoxResult];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Displays a message window, also known as dialog box, which presents a message to the user.
|
|
32
|
+
*/
|
|
33
|
+
export class MessageBox extends HTMLElement {
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Value indicating whether to vertically center this message box.
|
|
37
|
+
*/
|
|
38
|
+
@property({type: Boolean}) centered = false;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The buttons displayed in the footer.
|
|
42
|
+
*/
|
|
43
|
+
@state() private buttons: MessageBoxButton[] = [];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The title displayed in the header.
|
|
47
|
+
*/
|
|
48
|
+
@state() private caption = "";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The message displayed in the body.
|
|
52
|
+
*/
|
|
53
|
+
@state() private content: TemplateResult = html``;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* A contextual modifier.
|
|
57
|
+
*/
|
|
58
|
+
@state() private context: Context|null = null;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The icon displayed next to the body.
|
|
62
|
+
*/
|
|
63
|
+
@state() private icon = "";
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The root element.
|
|
67
|
+
*/
|
|
68
|
+
@query("dialog", true) private readonly root!: HTMLDialogElement;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The function invoked to return the dialog box result.
|
|
72
|
+
*/
|
|
73
|
+
#resolve: (value: string) => void = () => { /* Noop */ };
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Opens an alert dialog with the specified message and an "OK" button.
|
|
77
|
+
* @param caption The title displayed in the header.
|
|
78
|
+
* @param message The message to show.
|
|
79
|
+
* @param options The message box options.
|
|
80
|
+
* @returns Resolves with the value of the clicked button.
|
|
81
|
+
*/
|
|
82
|
+
alert(caption: string, message: TemplateResult, options: MessageBoxOptions = {}): Promise<string> {
|
|
83
|
+
const context = options.context ?? Context.Warning;
|
|
84
|
+
return this.show(message, {
|
|
85
|
+
buttons: options.buttons ?? [{label: "OK", value: MessageBoxResult.OK}],
|
|
86
|
+
caption,
|
|
87
|
+
context,
|
|
88
|
+
icon: options.icon ?? getIcon(context)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Closes the message box.
|
|
94
|
+
* @param result The message box result.
|
|
95
|
+
*/
|
|
96
|
+
close(result: MessageBoxResult = MessageBoxResult.None): void {
|
|
97
|
+
this.root.close(result);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Opens a confirmation dialog with the specified message and two buttons, "OK" and "Cancel".
|
|
102
|
+
* @param caption The title displayed in the header.
|
|
103
|
+
* @param message The message to show.
|
|
104
|
+
* @param options The message box options.
|
|
105
|
+
* @returns Resolves with the value of the clicked button.
|
|
106
|
+
*/
|
|
107
|
+
confirm(caption: string, message: TemplateResult, options: MessageBoxOptions = {}): Promise<string> {
|
|
108
|
+
const context = options.context ?? Context.Warning;
|
|
109
|
+
return this.show(message, {
|
|
110
|
+
caption,
|
|
111
|
+
context,
|
|
112
|
+
icon: options.icon ?? getIcon(context),
|
|
113
|
+
buttons: options.buttons ?? [
|
|
114
|
+
{label: "OK", value: MessageBoxResult.OK},
|
|
115
|
+
{label: "Annuler", value: MessageBoxResult.Cancel, variant: Variant.Secondary}
|
|
116
|
+
]
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Opens a modal dialog with the specified message and options.
|
|
122
|
+
* @param message The message to show.
|
|
123
|
+
* @param options The message box options.
|
|
124
|
+
* @returns Resolves with the value of the clicked button.
|
|
125
|
+
*/
|
|
126
|
+
show(message: TemplateResult, options: MessageBoxOptions = {}): Promise<string> {
|
|
127
|
+
this.buttons = options.buttons ?? [];
|
|
128
|
+
this.caption = options.caption ?? "";
|
|
129
|
+
this.content = message;
|
|
130
|
+
this.context = options.context ?? null;
|
|
131
|
+
this.icon = options.icon ?? "";
|
|
132
|
+
|
|
133
|
+
this.root.returnValue = MessageBoxResult.None;
|
|
134
|
+
this.root.showModal();
|
|
135
|
+
this.root.querySelector<HTMLButtonElement>(".btn-close")?.blur();
|
|
136
|
+
|
|
137
|
+
const {promise, resolve} = Promise.withResolvers<string>();
|
|
138
|
+
this.#resolve = resolve;
|
|
139
|
+
return promise;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Renders this component.
|
|
144
|
+
* @returns The view template.
|
|
145
|
+
*/
|
|
146
|
+
protected override render(): TemplateResult {
|
|
147
|
+
const centered = classMap({"modal-dialog-centered": this.centered});
|
|
148
|
+
return html`
|
|
149
|
+
<dialog class="modal modal-dialog modal-dialog-scrollable ${centered}" @click=${this.#onDialogClick} @close=${this.#onDialogClose}>
|
|
150
|
+
<form class="modal-content" method="dialog">
|
|
151
|
+
<div class="modal-header user-select-none">
|
|
152
|
+
${when(this.caption, () => html`<h1 class="modal-title fs-5">${this.caption}</h1>`)}
|
|
153
|
+
<button class="btn-close"></button>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="modal-body d-flex">
|
|
156
|
+
${when(this.icon, () => html`
|
|
157
|
+
<i class="icon icon-fill fs-1 fw-semibold me-2 ${classMap({[`text-${this.context}`]: this.context ?? ""})}">${this.icon}</i>
|
|
158
|
+
`)}
|
|
159
|
+
<div class="flex-grow-1">${this.content}</div>
|
|
160
|
+
</div>
|
|
161
|
+
${when(this.buttons.length, () => html`
|
|
162
|
+
<div class="modal-footer user-select-none">
|
|
163
|
+
${this.buttons.map(button => html`
|
|
164
|
+
<button class="btn btn-${button.variant ?? Variant.Primary}" value=${button.value ?? MessageBoxResult.None}>
|
|
165
|
+
${when(button.icon, () => html`<i class="icon icon-fill ${classMap({"me-1": button.label ?? ""})}">${button.icon}</i>`)}
|
|
166
|
+
${button.label}
|
|
167
|
+
</button>
|
|
168
|
+
`)}
|
|
169
|
+
</div>
|
|
170
|
+
`)}
|
|
171
|
+
</form>
|
|
172
|
+
</dialog>
|
|
173
|
+
`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Handles the `click` event.
|
|
178
|
+
* @param event The dispatched event.
|
|
179
|
+
*/
|
|
180
|
+
#onDialogClick(event: Event): void {
|
|
181
|
+
if (event.target == this.root) this.close();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Handles the `close` event.
|
|
186
|
+
*/
|
|
187
|
+
#onDialogClose(): void {
|
|
188
|
+
this.#resolve(this.root.returnValue);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* A message box button.
|
|
194
|
+
*/
|
|
195
|
+
export type MessageBoxButton = Partial<{
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The button icon.
|
|
199
|
+
*/
|
|
200
|
+
icon: string;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The button label.
|
|
204
|
+
*/
|
|
205
|
+
label: string;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The button value.
|
|
209
|
+
*/
|
|
210
|
+
value: string;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* A tone variant.
|
|
214
|
+
*/
|
|
215
|
+
variant: Context|Variant;
|
|
216
|
+
}>;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Defines the options of a {@link MessageBox} instance.
|
|
220
|
+
*/
|
|
221
|
+
export type MessageBoxOptions = Partial<{
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* The buttons displayed in the footer.
|
|
225
|
+
*/
|
|
226
|
+
buttons: MessageBoxButton[];
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The title displayed in the header.
|
|
230
|
+
*/
|
|
231
|
+
caption: string;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* A contextual modifier.
|
|
235
|
+
*/
|
|
236
|
+
context: Context;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* The icon displayed next to the body.
|
|
240
|
+
*/
|
|
241
|
+
icon: string;
|
|
242
|
+
}>;
|
|
@@ -3,14 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class OfflineIndicator extends HTMLElement {
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Creates a new offline indicator.
|
|
8
|
-
*/
|
|
9
|
-
constructor() {
|
|
10
|
-
super();
|
|
11
|
-
this.#updateHiddenState();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
6
|
/**
|
|
15
7
|
* Registers the component.
|
|
16
8
|
*/
|
|
@@ -22,6 +14,7 @@ export class OfflineIndicator extends HTMLElement {
|
|
|
22
14
|
* Method invoked when this component is connected.
|
|
23
15
|
*/
|
|
24
16
|
connectedCallback(): void {
|
|
17
|
+
this.#updateHiddenState();
|
|
25
18
|
for (const event of ["online", "offline"]) addEventListener(event, this.#updateHiddenState);
|
|
26
19
|
}
|
|
27
20
|
|
|
@@ -14,14 +14,14 @@ export class TabActivator extends HTMLElement {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* The
|
|
17
|
+
* The index of the active tab.
|
|
18
18
|
*/
|
|
19
19
|
get activeTabIndex(): number {
|
|
20
|
-
const index = Number
|
|
21
|
-
return Math.max(
|
|
20
|
+
const index = Number(this.storage.getItem(this.storageKey) ?? "1");
|
|
21
|
+
return Math.max(0, Math.min(this.tabs.length, Number.isNaN(index) ? 0 : index - 1));
|
|
22
22
|
}
|
|
23
23
|
set activeTabIndex(value: number) {
|
|
24
|
-
this.storage.setItem(this.storageKey, value.toString());
|
|
24
|
+
this.storage.setItem(this.storageKey, (value + 1).toString());
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -65,8 +65,8 @@ export class TabActivator extends HTMLElement {
|
|
|
65
65
|
*/
|
|
66
66
|
connectedCallback(): void {
|
|
67
67
|
const {activeTabIndex, tabs} = this;
|
|
68
|
-
for (let index =
|
|
69
|
-
const tab = tabs.item(index
|
|
68
|
+
for (let index = 0; index < tabs.length - 1; index++) {
|
|
69
|
+
const tab = tabs.item(index);
|
|
70
70
|
tab.addEventListener("click", () => this.activeTabIndex = index);
|
|
71
71
|
if (index == activeTabIndex) Tab.getOrCreateInstance(tab).show();
|
|
72
72
|
}
|
|
@@ -16,6 +16,14 @@ 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);
|
|
25
|
+
}
|
|
26
|
+
|
|
19
27
|
/**
|
|
20
28
|
* Registers the component.
|
|
21
29
|
*/
|
|
@@ -102,18 +110,15 @@ export class ThemeDropdown extends HTMLElement {
|
|
|
102
110
|
* Method invoked when this component is connected.
|
|
103
111
|
*/
|
|
104
112
|
connectedCallback(): void {
|
|
105
|
-
for (const button of this.querySelectorAll("button")) button.addEventListener("click", this.#setTheme);
|
|
106
|
-
this.#mediaQuery.addEventListener("change", this.#applyTheme);
|
|
107
|
-
|
|
108
113
|
const appTheme = localStorage.getItem(this.storageKey) as AppTheme|null;
|
|
109
114
|
if (appTheme) this.setAttribute("apptheme", appTheme);
|
|
115
|
+
this.#mediaQuery.addEventListener("change", this.#applyTheme);
|
|
110
116
|
}
|
|
111
117
|
|
|
112
118
|
/**
|
|
113
119
|
* Method invoked when this component is disconnected.
|
|
114
120
|
*/
|
|
115
121
|
disconnectedCallback(): void {
|
|
116
|
-
for (const button of this.querySelectorAll("button")) button.removeEventListener("click", this.#setTheme);
|
|
117
122
|
this.#mediaQuery.removeEventListener("change", this.#applyTheme);
|
|
118
123
|
}
|
|
119
124
|
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a document fragment from the specified HTML string.
|
|
3
|
+
* @param childContent The HTML string providing the child content.
|
|
4
|
+
* @returns The document fragment corresponding to the specified HTML string.
|
|
5
|
+
*/
|
|
6
|
+
export function createDocumentFragment(childContent: string): DocumentFragment {
|
|
7
|
+
const template = document.createElement("template");
|
|
8
|
+
template.innerHTML = childContent;
|
|
9
|
+
return template.content;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns a promise that resolves when the specified element has finished all its animations.
|
|
14
|
+
* @param element The target element.
|
|
15
|
+
* @returns The element animations.
|
|
16
|
+
*/
|
|
17
|
+
export function waitForAnimations(element: Element): Promise<Array<PromiseSettledResult<Animation>>> {
|
|
18
|
+
return Promise.allSettled(element.getAnimations().map(animation => animation.finished));
|
|
19
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the size of a component.
|
|
3
|
+
*/
|
|
4
|
+
export const Size = Object.freeze({
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An extra small size.
|
|
8
|
+
*/
|
|
9
|
+
ExtraSmall: "xs",
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A small size.
|
|
13
|
+
*/
|
|
14
|
+
Small: "sm",
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A medium size.
|
|
18
|
+
*/
|
|
19
|
+
Medium: "md",
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A large size.
|
|
23
|
+
*/
|
|
24
|
+
Large: "lg",
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* An extra large size.
|
|
28
|
+
*/
|
|
29
|
+
ExtraLarge: "xl",
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* An extra extra large size.
|
|
33
|
+
*/
|
|
34
|
+
ExtraExtraLarge: "xxl"
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Defines the size of a component.
|
|
39
|
+
*/
|
|
40
|
+
export type Size = typeof Size[keyof typeof Size];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines tone variants.
|
|
3
|
+
*/
|
|
4
|
+
export const Variant = Object.freeze({
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A dark variant.
|
|
8
|
+
*/
|
|
9
|
+
Dark: "dark",
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A light variant.
|
|
13
|
+
*/
|
|
14
|
+
Light: "light",
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A primary variant.
|
|
18
|
+
*/
|
|
19
|
+
Primary: "primary",
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A secondary variant.
|
|
23
|
+
*/
|
|
24
|
+
Secondary: "secondary"
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Defines tone variants.
|
|
29
|
+
*/
|
|
30
|
+
export type Variant = typeof Variant[keyof typeof Variant];
|
|
@@ -44,7 +44,7 @@ export class ViewportScroller {
|
|
|
44
44
|
this.#scrollOffset += (navbar?.offsetHeight ?? 0);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const actionBar = document.body.querySelector<HTMLElement>("action-bar
|
|
47
|
+
const actionBar = document.body.querySelector<HTMLElement>("action-bar");
|
|
48
48
|
return this.#scrollOffset + (actionBar?.offsetHeight ?? 0);
|
|
49
49
|
}
|
|
50
50
|
|