@cedx/base 0.2.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.
- package/ReadMe.md +1 -2
- package/lib/Html/AppTheme.d.ts +4 -4
- package/lib/Html/AppTheme.d.ts.map +1 -1
- package/lib/Html/AppTheme.js +4 -4
- package/lib/UI/LoadingIndicator.d.ts +0 -4
- package/lib/UI/LoadingIndicator.d.ts.map +1 -1
- package/lib/UI/LoadingIndicator.js +0 -8
- package/lib/UI/MenuActivator.d.ts +1 -4
- package/lib/UI/MenuActivator.d.ts.map +1 -1
- package/lib/UI/MenuActivator.js +9 -14
- package/lib/UI/OfflineIndicator.d.ts.map +1 -1
- package/lib/UI/OfflineIndicator.js +0 -1
- package/lib/UI/ThemeDropdown.d.ts +2 -36
- package/lib/UI/ThemeDropdown.d.ts.map +1 -1
- package/lib/UI/ThemeDropdown.js +41 -86
- package/package.json +2 -3
- package/src/Client/Html/AppTheme.ts +4 -4
- package/src/Client/Html/ViewportScroller.ts +1 -1
- package/src/Client/UI/LoadingIndicator.ts +0 -9
- package/src/Client/UI/MenuActivator.ts +3 -7
- package/src/Client/UI/OfflineIndicator.ts +0 -1
- package/src/Client/UI/ThemeDropdown.ts +34 -65
package/ReadMe.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# Belin.io Base
|
|
2
|
-
    
|
|
2
|
+
  
|
|
4
3
|
|
|
5
4
|
Base library by [Cédric Belin](https://belin.io), full stack developer,
|
|
6
5
|
implemented in [C#](https://learn.microsoft.com/en-us/dotnet/csharp) and [TypeScript](https://www.typescriptlang.org).
|
package/lib/Html/AppTheme.d.ts
CHANGED
|
@@ -21,14 +21,14 @@ export declare const AppTheme: Readonly<{
|
|
|
21
21
|
export type AppTheme = typeof AppTheme[keyof typeof AppTheme];
|
|
22
22
|
/**
|
|
23
23
|
* Gets the icon corresponding to the specified theme.
|
|
24
|
-
* @param theme The theme
|
|
24
|
+
* @param theme The application theme.
|
|
25
25
|
* @returns The icon corresponding to the specified theme.
|
|
26
26
|
*/
|
|
27
|
-
export declare function
|
|
27
|
+
export declare function getIcon(theme: AppTheme): string;
|
|
28
28
|
/**
|
|
29
29
|
* Gets the label corresponding to the specified theme.
|
|
30
|
-
* @param theme The theme
|
|
30
|
+
* @param theme The application theme.
|
|
31
31
|
* @returns The label corresponding to the specified theme.
|
|
32
32
|
*/
|
|
33
|
-
export declare function
|
|
33
|
+
export declare function getLabel(theme: AppTheme): string;
|
|
34
34
|
//# sourceMappingURL=AppTheme.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppTheme.d.ts","sourceRoot":"","sources":["../../src/Client/Html/AppTheme.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,QAAQ;IAEpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,
|
|
1
|
+
{"version":3,"file":"AppTheme.d.ts","sourceRoot":"","sources":["../../src/Client/Html/AppTheme.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,QAAQ;IAEpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAE9D;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAM/C;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAMhD"}
|
package/lib/Html/AppTheme.js
CHANGED
|
@@ -17,10 +17,10 @@ export const AppTheme = Object.freeze({
|
|
|
17
17
|
});
|
|
18
18
|
/**
|
|
19
19
|
* Gets the icon corresponding to the specified theme.
|
|
20
|
-
* @param theme The theme
|
|
20
|
+
* @param theme The application theme.
|
|
21
21
|
* @returns The icon corresponding to the specified theme.
|
|
22
22
|
*/
|
|
23
|
-
export function
|
|
23
|
+
export function getIcon(theme) {
|
|
24
24
|
switch (theme) {
|
|
25
25
|
case AppTheme.Dark: return "dark_mode";
|
|
26
26
|
case AppTheme.Light: return "light_mode";
|
|
@@ -29,10 +29,10 @@ export function themeIcon(theme) {
|
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* Gets the label corresponding to the specified theme.
|
|
32
|
-
* @param theme The theme
|
|
32
|
+
* @param theme The application theme.
|
|
33
33
|
* @returns The label corresponding to the specified theme.
|
|
34
34
|
*/
|
|
35
|
-
export function
|
|
35
|
+
export function getLabel(theme) {
|
|
36
36
|
switch (theme) {
|
|
37
37
|
case AppTheme.Dark: return "Sombre";
|
|
38
38
|
case AppTheme.Light: return "Clair";
|
|
@@ -4,10 +4,6 @@ import type { ILoadingIndicator } from "#Abstractions/ILoadingIndicator.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class LoadingIndicator extends HTMLElement implements ILoadingIndicator {
|
|
6
6
|
#private;
|
|
7
|
-
/**
|
|
8
|
-
* Creates a new loading indicator.
|
|
9
|
-
*/
|
|
10
|
-
constructor();
|
|
11
7
|
/**
|
|
12
8
|
* Starts the loading indicator.
|
|
13
9
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/Client/UI/LoadingIndicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oCAAoC,CAAC;AAE1E;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAY,YAAW,iBAAiB;;
|
|
1
|
+
{"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/Client/UI/LoadingIndicator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oCAAoC,CAAC;AAE1E;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAY,YAAW,iBAAiB;;IAc7E;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;;OAGG;IACH,IAAI,CAAC,OAAO,GAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAM,GAAG,IAAI;CAQ3C;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,mBAAmB,EAAE,gBAAgB,CAAC;KACtC;CACD"}
|
|
@@ -6,14 +6,6 @@ export class LoadingIndicator extends HTMLElement {
|
|
|
6
6
|
* The number of concurrent HTTP requests.
|
|
7
7
|
*/
|
|
8
8
|
#requestCount = 0;
|
|
9
|
-
/**
|
|
10
|
-
* Creates a new loading indicator.
|
|
11
|
-
*/
|
|
12
|
-
constructor() {
|
|
13
|
-
super();
|
|
14
|
-
this.hidden = true;
|
|
15
|
-
this.attachShadow({ mode: "open" }).appendChild(document.createElement("slot"));
|
|
16
|
-
}
|
|
17
9
|
/**
|
|
18
10
|
* Registers the component.
|
|
19
11
|
*/
|
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
* A component that activates the items of a menu based on the current document URL.
|
|
3
3
|
*/
|
|
4
4
|
export declare class MenuActivator extends HTMLElement {
|
|
5
|
-
|
|
6
|
-
* Creates a new menu activator.
|
|
7
|
-
*/
|
|
8
|
-
constructor();
|
|
5
|
+
#private;
|
|
9
6
|
/**
|
|
10
7
|
* Method invoked when this component is connected.
|
|
11
8
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MenuActivator.d.ts","sourceRoot":"","sources":["../../src/Client/UI/MenuActivator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW
|
|
1
|
+
{"version":3,"file":"MenuActivator.d.ts","sourceRoot":"","sources":["../../src/Client/UI/MenuActivator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW;;IAc7C;;OAEG;IACH,iBAAiB,IAAI,IAAI;CAQzB;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
|
package/lib/UI/MenuActivator.js
CHANGED
|
@@ -3,12 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export class MenuActivator extends HTMLElement {
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* The root element.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
super();
|
|
10
|
-
this.attachShadow({ mode: "open" }).appendChild(document.createElement("slot"));
|
|
11
|
-
}
|
|
8
|
+
#root = this.firstElementChild;
|
|
12
9
|
/**
|
|
13
10
|
* Registers the component.
|
|
14
11
|
*/
|
|
@@ -19,14 +16,12 @@ export class MenuActivator extends HTMLElement {
|
|
|
19
16
|
* Method invoked when this component is connected.
|
|
20
17
|
*/
|
|
21
18
|
connectedCallback() {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
anchor.closest(".nav-item.dropdown")?.querySelector(".nav-link")?.classList.add("active");
|
|
30
|
-
}
|
|
19
|
+
for (const anchor of this.#root.getElementsByTagName("a"))
|
|
20
|
+
if (anchor.href != location.href)
|
|
21
|
+
anchor.classList.remove("active");
|
|
22
|
+
else {
|
|
23
|
+
anchor.classList.add("active");
|
|
24
|
+
anchor.closest(".nav-item.dropdown")?.querySelector(".nav-link")?.classList.add("active");
|
|
25
|
+
}
|
|
31
26
|
}
|
|
32
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OfflineIndicator.d.ts","sourceRoot":"","sources":["../../src/Client/UI/OfflineIndicator.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,WAAW;IAEhD;;OAEG;;
|
|
1
|
+
{"version":3,"file":"OfflineIndicator.d.ts","sourceRoot":"","sources":["../../src/Client/UI/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,WAAW,IAAI,IAAI;CAGnB;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,mBAAmB,EAAE,gBAAgB,CAAC;KACtC;CACD"}
|
|
@@ -1,36 +1,12 @@
|
|
|
1
|
-
import { AppTheme } from "#Html/AppTheme.js";
|
|
2
|
-
import { MenuAlignment } from "#Html/MenuAlignment.js";
|
|
3
|
-
import { LitElement, type TemplateResult } from "lit";
|
|
4
1
|
/**
|
|
5
|
-
* A dropdown menu for switching the
|
|
2
|
+
* A dropdown menu for switching the application theme.
|
|
6
3
|
*/
|
|
7
|
-
export declare class ThemeDropdown extends
|
|
4
|
+
export declare class ThemeDropdown extends HTMLElement {
|
|
8
5
|
#private;
|
|
9
|
-
/**
|
|
10
|
-
* The alignment of the dropdown menu.
|
|
11
|
-
*/
|
|
12
|
-
alignment: MenuAlignment;
|
|
13
|
-
/**
|
|
14
|
-
* The label of the dropdown menu.
|
|
15
|
-
*/
|
|
16
|
-
label: string;
|
|
17
|
-
/**
|
|
18
|
-
* The key of the storage entry providing the saved theme.
|
|
19
|
-
*/
|
|
20
|
-
storageKey: string;
|
|
21
|
-
/**
|
|
22
|
-
* The current application theme.
|
|
23
|
-
*/
|
|
24
|
-
private appTheme;
|
|
25
6
|
/**
|
|
26
7
|
* Creates a new theme dropdown.
|
|
27
8
|
*/
|
|
28
9
|
constructor();
|
|
29
|
-
/**
|
|
30
|
-
* The current theme mode.
|
|
31
|
-
*/
|
|
32
|
-
get theme(): AppTheme;
|
|
33
|
-
set theme(value: AppTheme);
|
|
34
10
|
/**
|
|
35
11
|
* Method invoked when this component is connected.
|
|
36
12
|
*/
|
|
@@ -43,16 +19,6 @@ export declare class ThemeDropdown extends LitElement {
|
|
|
43
19
|
* Handles the events.
|
|
44
20
|
*/
|
|
45
21
|
handleEvent(): void;
|
|
46
|
-
/**
|
|
47
|
-
* Returns the node into which this component should render.
|
|
48
|
-
* @returns The node into which this component should render.
|
|
49
|
-
*/
|
|
50
|
-
protected createRenderRoot(): DocumentFragment | HTMLElement;
|
|
51
|
-
/**
|
|
52
|
-
* Renders this component.
|
|
53
|
-
* @returns The view template.
|
|
54
|
-
*/
|
|
55
|
-
protected render(): TemplateResult;
|
|
56
22
|
}
|
|
57
23
|
/**
|
|
58
24
|
* Declaration merging.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeDropdown.d.ts","sourceRoot":"","sources":["../../src/Client/UI/ThemeDropdown.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ThemeDropdown.d.ts","sourceRoot":"","sources":["../../src/Client/UI/ThemeDropdown.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW;;IAsB7C;;OAEG;;IAeH;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAKzB;;OAEG;IACH,oBAAoB,IAAI,IAAI;IAI5B;;OAEG;IACH,WAAW,IAAI,IAAI;CA2BnB;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
|
package/lib/UI/ThemeDropdown.js
CHANGED
|
@@ -1,64 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AppTheme, themeIcon, themeLabel } from "#Html/AppTheme.js";
|
|
3
|
-
import { MenuAlignment } from "#Html/MenuAlignment.js";
|
|
4
|
-
import { html, LitElement } from "lit";
|
|
5
|
-
import { customElement, property, state } from "lit/decorators.js";
|
|
6
|
-
import { classMap } from "lit/directives/class-map.js";
|
|
7
|
-
import { when } from "lit/directives/when.js";
|
|
1
|
+
import { AppTheme, getIcon } from "#Html/AppTheme.js";
|
|
8
2
|
/**
|
|
9
|
-
* A dropdown menu for switching the
|
|
3
|
+
* A dropdown menu for switching the application theme.
|
|
10
4
|
*/
|
|
11
|
-
|
|
5
|
+
export class ThemeDropdown extends HTMLElement {
|
|
12
6
|
/**
|
|
13
|
-
* The media query used to check the
|
|
7
|
+
* The media query used to check the application theme.
|
|
14
8
|
*/
|
|
15
|
-
#mediaQuery;
|
|
9
|
+
#mediaQuery = matchMedia("(prefers-color-scheme: dark)");
|
|
10
|
+
/**
|
|
11
|
+
* The root element.
|
|
12
|
+
*/
|
|
13
|
+
#root = this.firstElementChild;
|
|
14
|
+
/**
|
|
15
|
+
* The key of the storage entry providing the saved theme mode.
|
|
16
|
+
*/
|
|
17
|
+
#storageKey = this.getAttribute("storageKey") ?? "AppTheme";
|
|
18
|
+
/**
|
|
19
|
+
* The current application theme.
|
|
20
|
+
*/
|
|
21
|
+
#theme;
|
|
16
22
|
/**
|
|
17
23
|
* Creates a new theme dropdown.
|
|
18
24
|
*/
|
|
19
25
|
constructor() {
|
|
20
26
|
super();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The label of the dropdown menu.
|
|
27
|
-
*/
|
|
28
|
-
this.label = "";
|
|
29
|
-
/**
|
|
30
|
-
* The key of the storage entry providing the saved theme.
|
|
31
|
-
*/
|
|
32
|
-
this.storageKey = "AppTheme";
|
|
33
|
-
/**
|
|
34
|
-
* The media query used to check the system theme.
|
|
35
|
-
*/
|
|
36
|
-
this.#mediaQuery = matchMedia("(prefers-color-scheme: dark)");
|
|
37
|
-
const theme = localStorage.getItem(this.storageKey);
|
|
38
|
-
this.appTheme = Object.values(AppTheme).includes(theme) ? theme : AppTheme.System;
|
|
27
|
+
const theme = localStorage.getItem(this.#storageKey);
|
|
28
|
+
this.#theme = Object.values(AppTheme).includes(theme) ? theme : AppTheme.System;
|
|
29
|
+
for (const button of this.#root.querySelectorAll("button"))
|
|
30
|
+
button.addEventListener("click", this.#setTheme.bind(this));
|
|
39
31
|
}
|
|
40
32
|
/**
|
|
41
|
-
*
|
|
33
|
+
* Registers the component.
|
|
42
34
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
localStorage.setItem(this.storageKey, this.appTheme = value);
|
|
46
|
-
this.#applyTheme();
|
|
35
|
+
static {
|
|
36
|
+
customElements.define("theme-dropdown", this);
|
|
47
37
|
}
|
|
48
38
|
/**
|
|
49
39
|
* Method invoked when this component is connected.
|
|
50
40
|
*/
|
|
51
41
|
connectedCallback() {
|
|
52
|
-
super.connectedCallback();
|
|
53
|
-
this.#applyTheme();
|
|
54
42
|
this.#mediaQuery.addEventListener("change", this);
|
|
43
|
+
this.#applyTheme();
|
|
55
44
|
}
|
|
56
45
|
/**
|
|
57
46
|
* Method invoked when this component is disconnected.
|
|
58
47
|
*/
|
|
59
48
|
disconnectedCallback() {
|
|
60
49
|
this.#mediaQuery.removeEventListener("change", this);
|
|
61
|
-
super.disconnectedCallback();
|
|
62
50
|
}
|
|
63
51
|
/**
|
|
64
52
|
* Handles the events.
|
|
@@ -66,58 +54,25 @@ let ThemeDropdown = class ThemeDropdown extends LitElement {
|
|
|
66
54
|
handleEvent() {
|
|
67
55
|
this.#applyTheme();
|
|
68
56
|
}
|
|
69
|
-
/**
|
|
70
|
-
* Returns the node into which this component should render.
|
|
71
|
-
* @returns The node into which this component should render.
|
|
72
|
-
*/
|
|
73
|
-
createRenderRoot() {
|
|
74
|
-
return this;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Renders this component.
|
|
78
|
-
* @returns The view template.
|
|
79
|
-
*/
|
|
80
|
-
render() {
|
|
81
|
-
return html `
|
|
82
|
-
<li class="nav-item dropdown">
|
|
83
|
-
<a class="dropdown-toggle nav-link" data-bs-toggle="dropdown" href="#">
|
|
84
|
-
<i class="icon icon-fill">${themeIcon(this.appTheme)}</i>
|
|
85
|
-
${when(this.label, () => html `<span class="ms-1">${this.label}</span>`)}
|
|
86
|
-
</a>
|
|
87
|
-
<ul class="dropdown-menu ${classMap({ "dropdown-menu-end": this.alignment == MenuAlignment.End })}">
|
|
88
|
-
${Object.values(AppTheme).map(value => html `
|
|
89
|
-
<li>
|
|
90
|
-
<button class="dropdown-item d-flex align-items-center justify-content-between" @click=${() => this.theme = value}>
|
|
91
|
-
<span><i class="icon icon-fill me-1">${themeIcon(value)}</i> ${themeLabel(value)}</span>
|
|
92
|
-
${when(value == this.appTheme, () => html `<i class="icon ms-2">check</i>`)}
|
|
93
|
-
</button>
|
|
94
|
-
</li>
|
|
95
|
-
`)}
|
|
96
|
-
</ul>
|
|
97
|
-
</li>
|
|
98
|
-
`;
|
|
99
|
-
}
|
|
100
57
|
/**
|
|
101
58
|
* Applies the theme to the document.
|
|
102
59
|
*/
|
|
103
60
|
#applyTheme() {
|
|
104
|
-
const theme = this
|
|
61
|
+
const theme = this.#theme == AppTheme.System ? (this.#mediaQuery.matches ? AppTheme.Dark : AppTheme.Light) : this.#theme;
|
|
105
62
|
document.documentElement.dataset.bsTheme = theme.toLowerCase();
|
|
63
|
+
this.#root.querySelector(".dropdown-toggle > .icon").textContent = getIcon(this.#theme);
|
|
64
|
+
const checkIcon = this.#root.querySelector(".dropdown-item > .icon");
|
|
65
|
+
checkIcon.remove();
|
|
66
|
+
const activeButton = this.#root.querySelector(`button[data-theme="${this.#theme}"]`);
|
|
67
|
+
activeButton.appendChild(checkIcon);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Changes the current theme.
|
|
71
|
+
* @param event The dispatched event.
|
|
72
|
+
*/
|
|
73
|
+
#setTheme(event) {
|
|
74
|
+
const button = event.target.closest("button");
|
|
75
|
+
localStorage.setItem(this.#storageKey, this.#theme = button.dataset.theme);
|
|
76
|
+
this.#applyTheme();
|
|
106
77
|
}
|
|
107
|
-
}
|
|
108
|
-
__decorate([
|
|
109
|
-
property()
|
|
110
|
-
], ThemeDropdown.prototype, "alignment", void 0);
|
|
111
|
-
__decorate([
|
|
112
|
-
property()
|
|
113
|
-
], ThemeDropdown.prototype, "label", void 0);
|
|
114
|
-
__decorate([
|
|
115
|
-
property()
|
|
116
|
-
], ThemeDropdown.prototype, "storageKey", void 0);
|
|
117
|
-
__decorate([
|
|
118
|
-
state()
|
|
119
|
-
], ThemeDropdown.prototype, "appTheme", void 0);
|
|
120
|
-
ThemeDropdown = __decorate([
|
|
121
|
-
customElement("theme-dropdown")
|
|
122
|
-
], ThemeDropdown);
|
|
123
|
-
export { ThemeDropdown };
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"name": "@cedx/base",
|
|
8
8
|
"repository": "cedx/base",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.3.0",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@playwright/browser-chromium": "^1.54.2",
|
|
13
13
|
"@types/bootstrap": "^5.2.10",
|
|
14
14
|
"@types/chai": "^5.2.2",
|
|
15
15
|
"@types/mocha": "^10.0.10",
|
|
16
|
-
"@types/node": "^24.2.
|
|
16
|
+
"@types/node": "^24.2.1",
|
|
17
17
|
"@types/serve-handler": "^6.1.4",
|
|
18
18
|
"chai": "^5.2.1",
|
|
19
19
|
"esbuild": "^0.25.8",
|
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
],
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"bootstrap": ">=5.3.0",
|
|
54
|
-
"lit": ">=3.3.0",
|
|
55
54
|
"tslib": ">=2.8.0"
|
|
56
55
|
},
|
|
57
56
|
"publishConfig": {
|
|
@@ -26,10 +26,10 @@ export type AppTheme = typeof AppTheme[keyof typeof AppTheme];
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Gets the icon corresponding to the specified theme.
|
|
29
|
-
* @param theme The theme
|
|
29
|
+
* @param theme The application theme.
|
|
30
30
|
* @returns The icon corresponding to the specified theme.
|
|
31
31
|
*/
|
|
32
|
-
export function
|
|
32
|
+
export function getIcon(theme: AppTheme): string {
|
|
33
33
|
switch (theme) {
|
|
34
34
|
case AppTheme.Dark: return "dark_mode";
|
|
35
35
|
case AppTheme.Light: return "light_mode";
|
|
@@ -39,10 +39,10 @@ export function themeIcon(theme: AppTheme): string {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Gets the label corresponding to the specified theme.
|
|
42
|
-
* @param theme The theme
|
|
42
|
+
* @param theme The application theme.
|
|
43
43
|
* @returns The label corresponding to the specified theme.
|
|
44
44
|
*/
|
|
45
|
-
export function
|
|
45
|
+
export function getLabel(theme: AppTheme): string {
|
|
46
46
|
switch (theme) {
|
|
47
47
|
case AppTheme.Dark: return "Sombre";
|
|
48
48
|
case AppTheme.Light: return "Clair";
|
|
@@ -44,7 +44,7 @@ export class ViewportScroller {
|
|
|
44
44
|
this.#scrollOffset += Number.isNaN(navbarHeight) ? 0 : navbarHeight;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
const actionBar
|
|
47
|
+
const actionBar = document.body.querySelector<HTMLElement>("action-bar");
|
|
48
48
|
return this.#scrollOffset + (actionBar?.offsetHeight ?? 0);
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -10,15 +10,6 @@ export class LoadingIndicator extends HTMLElement implements ILoadingIndicator {
|
|
|
10
10
|
*/
|
|
11
11
|
#requestCount = 0;
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Creates a new loading indicator.
|
|
15
|
-
*/
|
|
16
|
-
constructor() {
|
|
17
|
-
super();
|
|
18
|
-
this.hidden = true;
|
|
19
|
-
this.attachShadow({mode: "open"}).appendChild(document.createElement("slot"));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
13
|
/**
|
|
23
14
|
* Registers the component.
|
|
24
15
|
*/
|
|
@@ -4,12 +4,9 @@
|
|
|
4
4
|
export class MenuActivator extends HTMLElement {
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* The root element.
|
|
8
8
|
*/
|
|
9
|
-
|
|
10
|
-
super();
|
|
11
|
-
this.attachShadow({mode: "open"}).appendChild(document.createElement("slot"));
|
|
12
|
-
}
|
|
9
|
+
readonly #root = this.firstElementChild!;
|
|
13
10
|
|
|
14
11
|
/**
|
|
15
12
|
* Registers the component.
|
|
@@ -22,8 +19,7 @@ export class MenuActivator extends HTMLElement {
|
|
|
22
19
|
* Method invoked when this component is connected.
|
|
23
20
|
*/
|
|
24
21
|
connectedCallback(): void {
|
|
25
|
-
const
|
|
26
|
-
if (menu) for (const anchor of menu.getElementsByTagName("a"))
|
|
22
|
+
for (const anchor of this.#root.getElementsByTagName("a"))
|
|
27
23
|
if (anchor.href != location.href) anchor.classList.remove("active");
|
|
28
24
|
else {
|
|
29
25
|
anchor.classList.add("active");
|
|
@@ -1,74 +1,60 @@
|
|
|
1
|
-
import {AppTheme,
|
|
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
|
|
4
|
+
* A dropdown menu for switching the application theme.
|
|
10
5
|
*/
|
|
11
|
-
|
|
12
|
-
export class ThemeDropdown extends LitElement {
|
|
6
|
+
export class ThemeDropdown extends HTMLElement {
|
|
13
7
|
|
|
14
8
|
/**
|
|
15
|
-
* The
|
|
9
|
+
* The media query used to check the application theme.
|
|
16
10
|
*/
|
|
17
|
-
|
|
11
|
+
readonly #mediaQuery = matchMedia("(prefers-color-scheme: dark)");
|
|
18
12
|
|
|
19
13
|
/**
|
|
20
|
-
* The
|
|
14
|
+
* The root element.
|
|
21
15
|
*/
|
|
22
|
-
|
|
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
|
-
|
|
21
|
+
readonly #storageKey = this.getAttribute("storageKey") ?? "AppTheme";
|
|
28
22
|
|
|
29
23
|
/**
|
|
30
24
|
* The current application theme.
|
|
31
25
|
*/
|
|
32
|
-
|
|
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
|
|
45
|
-
this
|
|
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
|
-
*
|
|
39
|
+
* Registers the component.
|
|
50
40
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
83
|
-
* @returns The node into which this component should render.
|
|
68
|
+
* Applies the theme to the document.
|
|
84
69
|
*/
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
*
|
|
82
|
+
* Changes the current theme.
|
|
83
|
+
* @param event The dispatched event.
|
|
116
84
|
*/
|
|
117
|
-
#
|
|
118
|
-
const
|
|
119
|
-
|
|
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
|
|