@cedx/base 0.11.0 → 0.13.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.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Defines the position of an element.
3
+ */
4
+ export const Position = Object.freeze({
5
+ /**
6
+ * Top left.
7
+ */
8
+ TopStart: "TopStart",
9
+ /**
10
+ * Top center.
11
+ */
12
+ TopCenter: "TopCenter",
13
+ /**
14
+ * Top right.
15
+ */
16
+ TopEnd: "TopEnd",
17
+ /**
18
+ * Middle left.
19
+ */
20
+ MiddleStart: "MiddleStart",
21
+ /**
22
+ * Middle center.
23
+ */
24
+ MiddleCenter: "MiddleCenter",
25
+ /**
26
+ * Middle right.
27
+ */
28
+ MiddleEnd: "MiddleEnd",
29
+ /**
30
+ * Bottom left.
31
+ */
32
+ BottomStart: "BottomStart",
33
+ /**
34
+ * Bottom center.
35
+ */
36
+ BottomCenter: "BottomCenter",
37
+ /**
38
+ * Bottom right.
39
+ */
40
+ BottomEnd: "BottomEnd"
41
+ });
42
+ /**
43
+ * Returns the CSS representation of the specified position.
44
+ * @param position The position.
45
+ * @returns The CSS representation of the specified position.
46
+ */
47
+ export function toCss(position) {
48
+ switch (position) {
49
+ case Position.TopCenter: return "top-0 start-50 translate-middle-x";
50
+ case Position.TopEnd: return "top-0 end-0";
51
+ case Position.MiddleStart: return "top-50 start-0 translate-middle-y";
52
+ case Position.MiddleCenter: return "top-50 start-50 translate-middle";
53
+ case Position.MiddleEnd: return "top-50 end-0 translate-middle-y";
54
+ case Position.BottomStart: return "bottom-0 start-0";
55
+ case Position.BottomCenter: return "bottom-0 start-50 translate-middle-x";
56
+ case Position.BottomEnd: return "bottom-0 end-0";
57
+ default: return "top-0 start-0";
58
+ }
59
+ }
package/lib/UI/Size.d.ts CHANGED
@@ -1,34 +1,40 @@
1
1
  /**
2
- * Defines the size of a component.
2
+ * Defines the size of an element.
3
3
  */
4
4
  export declare const Size: Readonly<{
5
5
  /**
6
6
  * An extra small size.
7
7
  */
8
- ExtraSmall: "xs";
8
+ ExtraSmall: "ExtraSmall";
9
9
  /**
10
10
  * A small size.
11
11
  */
12
- Small: "sm";
12
+ Small: "Small";
13
13
  /**
14
14
  * A medium size.
15
15
  */
16
- Medium: "md";
16
+ Medium: "Medium";
17
17
  /**
18
18
  * A large size.
19
19
  */
20
- Large: "lg";
20
+ Large: "Large";
21
21
  /**
22
22
  * An extra large size.
23
23
  */
24
- ExtraLarge: "xl";
24
+ ExtraLarge: "ExtraLarge";
25
25
  /**
26
26
  * An extra extra large size.
27
27
  */
28
- ExtraExtraLarge: "xxl";
28
+ ExtraExtraLarge: "ExtraExtraLarge";
29
29
  }>;
30
30
  /**
31
- * Defines the size of a component.
31
+ * Defines the size of an element.
32
32
  */
33
33
  export type Size = typeof Size[keyof typeof Size];
34
+ /**
35
+ * Returns the CSS representation of the specified size.
36
+ * @param size The size.
37
+ * @returns The CSS representation of the specified size.
38
+ */
39
+ export declare function toCss(size: Size): string;
34
40
  //# sourceMappingURL=Size.d.ts.map
@@ -1 +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"}
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;AAElD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CASxC"}
package/lib/UI/Size.js CHANGED
@@ -1,29 +1,44 @@
1
1
  /**
2
- * Defines the size of a component.
2
+ * Defines the size of an element.
3
3
  */
4
4
  export const Size = Object.freeze({
5
5
  /**
6
6
  * An extra small size.
7
7
  */
8
- ExtraSmall: "xs",
8
+ ExtraSmall: "ExtraSmall",
9
9
  /**
10
10
  * A small size.
11
11
  */
12
- Small: "sm",
12
+ Small: "Small",
13
13
  /**
14
14
  * A medium size.
15
15
  */
16
- Medium: "md",
16
+ Medium: "Medium",
17
17
  /**
18
18
  * A large size.
19
19
  */
20
- Large: "lg",
20
+ Large: "Large",
21
21
  /**
22
22
  * An extra large size.
23
23
  */
24
- ExtraLarge: "xl",
24
+ ExtraLarge: "ExtraLarge",
25
25
  /**
26
26
  * An extra extra large size.
27
27
  */
28
- ExtraExtraLarge: "xxl"
28
+ ExtraExtraLarge: "ExtraExtraLarge"
29
29
  });
30
+ /**
31
+ * Returns the CSS representation of the specified size.
32
+ * @param size The size.
33
+ * @returns The CSS representation of the specified size.
34
+ */
35
+ export function toCss(size) {
36
+ switch (size) {
37
+ case Size.ExtraSmall: return "xs";
38
+ case Size.Small: return "sm";
39
+ case Size.Large: return "lg";
40
+ case Size.ExtraLarge: return "xl";
41
+ case Size.ExtraExtraLarge: return "xxl";
42
+ default: return "md";
43
+ }
44
+ }
@@ -5,22 +5,28 @@ export declare const Variant: Readonly<{
5
5
  /**
6
6
  * A dark variant.
7
7
  */
8
- Dark: "dark";
8
+ Dark: "Dark";
9
9
  /**
10
10
  * A light variant.
11
11
  */
12
- Light: "light";
12
+ Light: "Light";
13
13
  /**
14
14
  * A primary variant.
15
15
  */
16
- Primary: "primary";
16
+ Primary: "Primary";
17
17
  /**
18
18
  * A secondary variant.
19
19
  */
20
- Secondary: "secondary";
20
+ Secondary: "Secondary";
21
21
  }>;
22
22
  /**
23
23
  * Defines tone variants.
24
24
  */
25
25
  export type Variant = typeof Variant[keyof typeof Variant];
26
+ /**
27
+ * Returns the CSS representation of the specified variant.
28
+ * @param variant The variant.
29
+ * @returns The CSS representation of the specified variant.
30
+ */
31
+ export declare function toCss(variant: Variant): string;
26
32
  //# sourceMappingURL=Variant.d.ts.map
@@ -1 +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"}
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;AAE3D;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAE9C"}
package/lib/UI/Variant.js CHANGED
@@ -5,17 +5,25 @@ export const Variant = Object.freeze({
5
5
  /**
6
6
  * A dark variant.
7
7
  */
8
- Dark: "dark",
8
+ Dark: "Dark",
9
9
  /**
10
10
  * A light variant.
11
11
  */
12
- Light: "light",
12
+ Light: "Light",
13
13
  /**
14
14
  * A primary variant.
15
15
  */
16
- Primary: "primary",
16
+ Primary: "Primary",
17
17
  /**
18
18
  * A secondary variant.
19
19
  */
20
- Secondary: "secondary"
20
+ Secondary: "Secondary"
21
21
  });
22
+ /**
23
+ * Returns the CSS representation of the specified variant.
24
+ * @param variant The variant.
25
+ * @returns The CSS representation of the specified variant.
26
+ */
27
+ export function toCss(variant) {
28
+ return variant.toLowerCase();
29
+ }
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "name": "@cedx/base",
8
8
  "repository": "cedx/base",
9
9
  "type": "module",
10
- "version": "0.11.0",
10
+ "version": "0.13.0",
11
11
  "devDependencies": {
12
12
  "@playwright/browser-chromium": "^1.55.0",
13
13
  "@types/bootstrap": "^5.2.10",
@@ -15,7 +15,7 @@
15
15
  "@types/mocha": "^10.0.10",
16
16
  "@types/node": "^24.3.0",
17
17
  "@types/serve-handler": "^6.1.4",
18
- "chai": "^6.0.0",
18
+ "chai": "^6.0.1",
19
19
  "esbuild": "^0.25.9",
20
20
  "globals": "^16.3.0",
21
21
  "mocha": "^11.7.1",
@@ -14,22 +14,21 @@ export class OfflineIndicator extends HTMLElement {
14
14
  * Method invoked when this component is connected.
15
15
  */
16
16
  connectedCallback(): void {
17
- this.#updateHiddenState();
18
- for (const event of ["online", "offline"]) addEventListener(event, this.#updateHiddenState);
17
+ this.#update();
18
+ for (const event of ["online", "offline"]) addEventListener(event, this.#update);
19
19
  }
20
20
 
21
21
  /**
22
22
  * Method invoked when this component is disconnected.
23
23
  */
24
24
  disconnectedCallback(): void {
25
- for (const event of ["online", "offline"]) removeEventListener(event, this.#updateHiddenState);
25
+ for (const event of ["online", "offline"]) removeEventListener(event, this.#update);
26
26
  }
27
27
 
28
28
  /**
29
- * Updates the hidden state of this component according to the {@link navigator.onLine} property.
29
+ * Updates this component.
30
30
  */
31
- readonly #updateHiddenState: () => void = () =>
32
- this.hidden = navigator.onLine;
31
+ readonly #update: () => void = () => this.hidden = navigator.onLine;
33
32
  }
34
33
 
35
34
  /**
@@ -56,7 +56,7 @@ export class TabActivator extends HTMLElement {
56
56
  /**
57
57
  * The tab list.
58
58
  */
59
- get tabs(): NodeListOf<HTMLButtonElement> {
59
+ get tabs(): NodeListOf<HTMLElement> {
60
60
  return this.querySelectorAll('[data-bs-toggle="tab"]');
61
61
  }
62
62
 
@@ -1,3 +1,4 @@
1
+ import {Dropdown} from "bootstrap";
1
2
  import {AppTheme, getIcon} from "../AppTheme.js";
2
3
  import {MenuAlignment} from "../MenuAlignment.js";
3
4
 
@@ -11,6 +12,11 @@ export class ThemeDropdown extends HTMLElement {
11
12
  */
12
13
  static readonly observedAttributes = ["alignment", "apptheme", "label"];
13
14
 
15
+ /**
16
+ * The dropdown menu.
17
+ */
18
+ #dropdown!: Dropdown;
19
+
14
20
  /**
15
21
  * The media query used to check the application theme.
16
22
  */
@@ -21,7 +27,7 @@ export class ThemeDropdown extends HTMLElement {
21
27
  */
22
28
  constructor() {
23
29
  super();
24
- for (const button of this.querySelectorAll("button")) button.addEventListener("click", this.#setTheme);
30
+ for (const button of this.querySelectorAll("button")) button.addEventListener("click", this.#setAppTheme);
25
31
  }
26
32
 
27
33
  /**
@@ -51,7 +57,6 @@ export class ThemeDropdown extends HTMLElement {
51
57
  }
52
58
  set appTheme(value: AppTheme) {
53
59
  this.setAttribute("apptheme", value);
54
- localStorage.setItem(this.storageKey, this.appTheme);
55
60
  }
56
61
 
57
62
  /**
@@ -84,48 +89,63 @@ export class ThemeDropdown extends HTMLElement {
84
89
  */
85
90
  attributeChangedCallback(attribute: string, oldValue: string|null, newValue: string|null): void {
86
91
  if (newValue != oldValue) switch (attribute) {
87
- case "alignment": {
88
- const alignment = Object.values(MenuAlignment).includes(newValue as MenuAlignment) ? newValue as MenuAlignment : MenuAlignment.End;
89
- const {classList} = this.querySelector(".dropdown-menu")!;
90
- if (alignment == MenuAlignment.End) classList.add("dropdown-menu-end");
91
- else classList.remove("dropdown-menu-end");
92
+ case "alignment":
93
+ this.#updateAlignment(Object.values(MenuAlignment).includes(newValue as MenuAlignment) ? newValue as MenuAlignment : MenuAlignment.End);
92
94
  break;
93
- }
94
- case "apptheme": {
95
- const appTheme = Object.values(AppTheme).includes(newValue as AppTheme) ? newValue as AppTheme : AppTheme.System;
96
- this.querySelector(".dropdown-toggle > .icon")!.textContent = getIcon(appTheme);
97
- this.querySelector(`button[data-theme="${appTheme}"]`)!.appendChild(this.querySelector(".dropdown-item > .icon")!);
98
- this.#applyTheme();
95
+ case "apptheme":
96
+ this.#updateAppTheme(Object.values(AppTheme).includes(newValue as AppTheme) ? newValue as AppTheme : AppTheme.System);
99
97
  break;
100
- }
101
- case "label": {
102
- this.querySelector(".dropdown-toggle > span")!.textContent = (newValue ?? "").trim() || "Thème";
98
+ case "label":
99
+ this.#updateLabel(newValue ?? "");
103
100
  break;
104
- }
105
101
  // No default
106
102
  }
107
103
  }
108
104
 
105
+ /**
106
+ * Closes the dropdown menu.
107
+ */
108
+ close(): void {
109
+ this.#dropdown.hide();
110
+ }
111
+
109
112
  /**
110
113
  * Method invoked when this component is connected.
111
114
  */
112
115
  connectedCallback(): void {
113
116
  const appTheme = localStorage.getItem(this.storageKey) as AppTheme|null;
114
- if (appTheme) this.setAttribute("apptheme", appTheme);
115
- this.#mediaQuery.addEventListener("change", this.#applyTheme);
117
+ if (appTheme) this.appTheme = appTheme;
118
+
119
+ this.#dropdown = new Dropdown(this.querySelector(".dropdown-toggle")!);
120
+ this.#mediaQuery.addEventListener("change", this.#applyToDocument);
116
121
  }
117
122
 
118
123
  /**
119
124
  * Method invoked when this component is disconnected.
120
125
  */
121
126
  disconnectedCallback(): void {
122
- this.#mediaQuery.removeEventListener("change", this.#applyTheme);
127
+ this.#dropdown.dispose();
128
+ this.#mediaQuery.removeEventListener("change", this.#applyToDocument);
129
+ }
130
+
131
+ /**
132
+ * Opens the dropdown menu.
133
+ */
134
+ open(): void {
135
+ this.#dropdown.show();
136
+ }
137
+
138
+ /**
139
+ * Saves the current application theme into the local storage.
140
+ */
141
+ save(): void {
142
+ localStorage.setItem(this.storageKey, this.appTheme);
123
143
  }
124
144
 
125
145
  /**
126
146
  * Applies the application theme to the document.
127
147
  */
128
- readonly #applyTheme: () => void = () => {
148
+ readonly #applyToDocument: () => void = () => {
129
149
  const {appTheme} = this;
130
150
  const bsTheme = appTheme == AppTheme.System ? (this.#mediaQuery.matches ? AppTheme.Dark : AppTheme.Light) : appTheme;
131
151
  document.documentElement.dataset.bsTheme = bsTheme.toLowerCase();
@@ -135,11 +155,39 @@ export class ThemeDropdown extends HTMLElement {
135
155
  * Changes the current application theme.
136
156
  * @param event The dispatched event.
137
157
  */
138
- readonly #setTheme: (event: Event) => void = event => {
158
+ readonly #setAppTheme: (event: Event) => void = event => {
139
159
  event.preventDefault();
140
- const button = (event.target as HTMLElement).closest("button")!;
141
- this.appTheme = button.dataset.theme! as AppTheme;
160
+ this.appTheme = (event.target as Element).closest("button")!.dataset.theme! as AppTheme;
161
+ this.save();
142
162
  };
163
+
164
+ /**
165
+ * Updates the alignment of the dropdown menu.
166
+ * @param value The new value.
167
+ */
168
+ #updateAlignment(value: MenuAlignment): void {
169
+ const {classList} = this.querySelector(".dropdown-menu")!;
170
+ if (value == MenuAlignment.End) classList.add("dropdown-menu-end");
171
+ else classList.remove("dropdown-menu-end");
172
+ }
173
+
174
+ /**
175
+ * Updates the application theme.
176
+ * @param value The new value.
177
+ */
178
+ #updateAppTheme(value: AppTheme): void {
179
+ this.querySelector(".dropdown-toggle > .icon")!.textContent = getIcon(value);
180
+ this.querySelector(`button[data-theme="${value}"]`)!.appendChild(this.querySelector(".dropdown-item > .icon")!);
181
+ this.#applyToDocument();
182
+ }
183
+
184
+ /**
185
+ * Updates the label of the dropdown menu.
186
+ * @param value The new value.
187
+ */
188
+ #updateLabel(value: string): void {
189
+ this.querySelector(".dropdown-toggle > span")!.textContent = value.trim() || "Thème";
190
+ }
143
191
  }
144
192
 
145
193
  /**