@cedx/base 0.1.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.
Files changed (40) hide show
  1. package/ReadMe.md +1 -2
  2. package/lib/Date.d.ts +72 -0
  3. package/lib/Date.d.ts.map +1 -0
  4. package/lib/Date.js +125 -0
  5. package/lib/Duration.d.ts +26 -0
  6. package/lib/Duration.d.ts.map +1 -0
  7. package/lib/Duration.js +21 -0
  8. package/lib/Html/AppTheme.d.ts +4 -4
  9. package/lib/Html/AppTheme.d.ts.map +1 -1
  10. package/lib/Html/AppTheme.js +4 -4
  11. package/lib/Html/Context.d.ts +32 -0
  12. package/lib/Html/Context.d.ts.map +1 -0
  13. package/lib/Html/Context.js +34 -0
  14. package/lib/Html/File.d.ts +32 -0
  15. package/lib/Html/File.d.ts.map +1 -0
  16. package/lib/Html/File.js +76 -0
  17. package/lib/UI/LoadingIndicator.d.ts +1 -11
  18. package/lib/UI/LoadingIndicator.d.ts.map +1 -1
  19. package/lib/UI/LoadingIndicator.js +5 -20
  20. package/lib/UI/MenuActivator.d.ts +22 -0
  21. package/lib/UI/MenuActivator.d.ts.map +1 -0
  22. package/lib/UI/MenuActivator.js +27 -0
  23. package/lib/UI/OfflineIndicator.d.ts +1 -7
  24. package/lib/UI/OfflineIndicator.d.ts.map +1 -1
  25. package/lib/UI/OfflineIndicator.js +8 -18
  26. package/lib/UI/ThemeDropdown.d.ts +2 -36
  27. package/lib/UI/ThemeDropdown.d.ts.map +1 -1
  28. package/lib/UI/ThemeDropdown.js +41 -86
  29. package/package.json +3 -3
  30. package/src/Client/Date.ts +138 -0
  31. package/src/Client/Duration.ts +30 -0
  32. package/src/Client/Html/AppTheme.ts +4 -4
  33. package/src/Client/Html/Context.ts +44 -0
  34. package/src/Client/Html/File.ts +85 -0
  35. package/src/Client/Html/ViewportScroller.ts +1 -1
  36. package/src/Client/Html/tsconfig.json +4 -1
  37. package/src/Client/UI/LoadingIndicator.ts +4 -16
  38. package/src/Client/UI/MenuActivator.ts +42 -0
  39. package/src/Client/UI/OfflineIndicator.ts +10 -17
  40. package/src/Client/UI/ThemeDropdown.ts +34 -65
@@ -1,10 +1,7 @@
1
- import { __decorate } from "tslib";
2
- import { html, LitElement } from "lit";
3
- import { customElement } from "lit/decorators.js";
4
1
  /**
5
2
  * A component that shows up when the network is unavailable, and hides when connectivity is restored.
6
3
  */
7
- let OfflineIndicator = class OfflineIndicator extends LitElement {
4
+ export class OfflineIndicator extends HTMLElement {
8
5
  /**
9
6
  * Creates a new offline indicator.
10
7
  */
@@ -12,11 +9,16 @@ let OfflineIndicator = class OfflineIndicator extends LitElement {
12
9
  super();
13
10
  this.hidden = navigator.onLine;
14
11
  }
12
+ /**
13
+ * Registers the component.
14
+ */
15
+ static {
16
+ customElements.define("offline-indicator", this);
17
+ }
15
18
  /**
16
19
  * Method invoked when this component is connected.
17
20
  */
18
21
  connectedCallback() {
19
- super.connectedCallback();
20
22
  for (const event of ["online", "offline"])
21
23
  addEventListener(event, this);
22
24
  }
@@ -26,7 +28,6 @@ let OfflineIndicator = class OfflineIndicator extends LitElement {
26
28
  disconnectedCallback() {
27
29
  for (const event of ["online", "offline"])
28
30
  removeEventListener(event, this);
29
- super.disconnectedCallback();
30
31
  }
31
32
  /**
32
33
  * Handles the events.
@@ -34,15 +35,4 @@ let OfflineIndicator = class OfflineIndicator extends LitElement {
34
35
  handleEvent() {
35
36
  this.hidden = navigator.onLine;
36
37
  }
37
- /**
38
- * Renders this component.
39
- * @returns The view template.
40
- */
41
- render() {
42
- return html `<slot></slot>`;
43
- }
44
- };
45
- OfflineIndicator = __decorate([
46
- customElement("offline-indicator")
47
- ], OfflineIndicator);
48
- export { OfflineIndicator };
38
+ }
@@ -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 color mode.
2
+ * A dropdown menu for switching the application theme.
6
3
  */
7
- export declare class ThemeDropdown extends LitElement {
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":"AAAA,OAAO,EAAC,QAAQ,EAAwB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAC,aAAa,EAAC,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAO,UAAU,EAAE,KAAK,cAAc,EAAC,MAAM,KAAK,CAAC;AAK1D;;GAEG;AACH,qBACa,aAAc,SAAQ,UAAU;;IAE5C;;OAEG;IACS,SAAS,EAAE,aAAa,CAAqB;IAEzD;;OAEG;IACS,KAAK,SAAM;IAEvB;;OAEG;IACS,UAAU,SAAc;IAEpC;;OAEG;IACM,OAAO,CAAC,QAAQ,CAAW;IAOpC;;OAEG;;IAOH;;OAEG;IACH,IAAI,KAAK,IAAI,QAAQ,CAA0B;IAC/C,IAAI,KAAK,CAAC,KAAK,EAAE,QAAQ,EAGxB;IAED;;OAEG;IACM,iBAAiB,IAAI,IAAI;IAMlC;;OAEG;IACM,oBAAoB,IAAI,IAAI;IAKrC;;OAEG;IACH,WAAW,IAAI,IAAI;IAInB;;;OAGG;cACgB,gBAAgB,IAAI,gBAAgB,GAAC,WAAW;IAInE;;;OAGG;cACgB,MAAM,IAAI,cAAc;CA4B3C;AAED;;GAEG;AACH,OAAO,CAAC,MAAM,CAAC;IAEd;;OAEG;IACH,UAAU,qBAAqB;QAC9B,gBAAgB,EAAE,aAAa,CAAC;KAChC;CACD"}
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"}
@@ -1,64 +1,52 @@
1
- import { __decorate } from "tslib";
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 color mode.
3
+ * A dropdown menu for switching the application theme.
10
4
  */
11
- let ThemeDropdown = class ThemeDropdown extends LitElement {
5
+ export class ThemeDropdown extends HTMLElement {
12
6
  /**
13
- * The media query used to check the system theme.
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
- * The alignment of the dropdown menu.
23
- */
24
- this.alignment = MenuAlignment.End;
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
- * The current theme mode.
33
+ * Registers the component.
42
34
  */
43
- get theme() { return this.appTheme; }
44
- set theme(value) {
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.appTheme == AppTheme.System ? (this.#mediaQuery.matches ? AppTheme.Dark : AppTheme.Light) : this.appTheme;
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.1.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.0",
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",
@@ -40,6 +40,7 @@
40
40
  ],
41
41
  "imports": {
42
42
  "#Abstractions/*.js": "./lib/Abstractions/*.js",
43
+ "#Base/*.js": "./lib/*.js",
43
44
  "#Html/*.js": "./lib/Html/*.js"
44
45
  },
45
46
  "keywords": [
@@ -50,7 +51,6 @@
50
51
  ],
51
52
  "peerDependencies": {
52
53
  "bootstrap": ">=5.3.0",
53
- "lit": ">=3.3.0",
54
54
  "tslib": ">=2.8.0"
55
55
  },
56
56
  "publishConfig": {
@@ -0,0 +1,138 @@
1
+ import {Duration} from "./Duration.js";
2
+
3
+ /**
4
+ * Returns the date at midnight corresponding to the specified date.
5
+ * @param date The source date.
6
+ * @returns The date whose time has been set at midnight.
7
+ */
8
+ export function atMidnight(date: Date): Date {
9
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
10
+ }
11
+
12
+ /**
13
+ * Gets the number of days in the month of the specified date.
14
+ * @param date The date.
15
+ * @returns The number of days in the month of the specified date.
16
+ */
17
+ export function daysInMonth(date: Date): number {
18
+ return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
19
+ }
20
+
21
+ /**
22
+ * Gets the date of Easter for a given year.
23
+ * @param year The year.
24
+ * @returns The date of Easter for the specified year.
25
+ */
26
+ export function getEaster(year = new Date().getFullYear()): Date {
27
+ /* eslint-disable id-length */
28
+ const n = year % 19;
29
+ const c = Math.trunc(year / 100);
30
+ const u = year % 100;
31
+ const e = ((19 * n) + c - Math.trunc(c / 4) - Math.trunc((c - Math.trunc((c + 8) / 25) + 1) / 3) + 15) % 30;
32
+ const l = ((2 * (c % 4)) + (2 * Math.trunc(u / 4)) - e - (u % 4) + 32) % 7;
33
+ const h = Math.trunc((n + (11 * e) + (22 * l)) / 451);
34
+ const result = e + l - (7 * h) + 114;
35
+ /* eslint-enable id-length */
36
+
37
+ return new Date(year, Math.trunc(result / 31) - 1, (result % 31) + 1);
38
+ }
39
+
40
+ /**
41
+ * Gets the list of holidays for a given year.
42
+ * @param year The year.
43
+ * @returns The list of holidays for the specified year.
44
+ */
45
+ export function getHolidays(year = new Date().getFullYear()): Date[] {
46
+ const holidays = [];
47
+
48
+ // Fixed holidays.
49
+ holidays.push(new Date(year, 0, 1)); // New year.
50
+ holidays.push(new Date(year, 4, 1)); // Labor day.
51
+ holidays.push(new Date(year, 4, 8)); // Armistice (1945).
52
+ holidays.push(new Date(year, 6, 14)); // National holiday.
53
+ holidays.push(new Date(year, 7, 15)); // Assumption.
54
+ holidays.push(new Date(year, 10, 1)); // Toussaint.
55
+ holidays.push(new Date(year, 10, 11)); // Armistice (1918).
56
+ holidays.push(new Date(year, 11, 25)); // Christmas.
57
+
58
+ // Holidays depending on Easter.
59
+ const easter = getEaster(year);
60
+ holidays.push(new Date(easter.getTime() + Duration.Day)); // Easter monday.
61
+ holidays.push(new Date(easter.getTime() + (39 * Duration.Day))); // Ascension thursday.
62
+ holidays.push(new Date(easter.getTime() + (50 * Duration.Day))); // Pentecost monday.
63
+
64
+ return holidays;
65
+ }
66
+
67
+ /**
68
+ * Gets the quarter corresponding to the specified date.
69
+ * @param date The date.
70
+ * @returns The quarter corresponding to the specified date.
71
+ */
72
+ export function getQuarter(date: Date): number {
73
+ return Math.ceil((date.getMonth() + 1) / 3);
74
+ }
75
+
76
+ /**
77
+ * Gets the week number corresponding to the specified date.
78
+ * @param date The date.
79
+ * @returns The week number corresponding to the specified date.
80
+ */
81
+ export function getWeekOfYear(date: Date): number {
82
+ const thursday = new Date(date.getFullYear(), date.getMonth(), date.getDate() + 3 - ((date.getDay() + 6) % 7));
83
+ const firstWeek = new Date(thursday.getFullYear(), 0, 4);
84
+ return 1 + Math.round((((thursday.getTime() - firstWeek.getTime()) / Duration.Day) - 3 + ((firstWeek.getDay() + 6) % 7)) / 7);
85
+ }
86
+
87
+ /**
88
+ * Returns a value indicating whether the specified date is in a leap year.
89
+ * @param date The date to check.
90
+ * @returns `true` if the specified date is in a leap year, otherwise `false`.
91
+ */
92
+ export function inLeapYear(date: Date): boolean {
93
+ const year = date.getFullYear();
94
+ return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
95
+ }
96
+
97
+ /**
98
+ * Returns a value indicating whether the specified date is a holiday.
99
+ * @param date The date to check.
100
+ * @returns `true` if the specified date is a holiday, otherwise `false`.
101
+ */
102
+ export function isHoliday(date: Date): boolean {
103
+ const timestamp = atMidnight(date).getTime();
104
+ return getHolidays(date.getFullYear()).some(holiday => holiday.getTime() == timestamp);
105
+ }
106
+
107
+ /**
108
+ * Returns a value indicating whether the specified date is a working day.
109
+ * @param date The date to check.
110
+ * @returns `true` if the specified date is a working day, otherwise `false`.
111
+ */
112
+ export function isWorkingDay(date: Date): boolean {
113
+ const dayOfWeek = date.getDay();
114
+ return dayOfWeek >= 1 && dayOfWeek <= 5;
115
+ }
116
+
117
+ /**
118
+ * Converts the specified date to an ISO 8601 week.
119
+ * @param date The date to convert.
120
+ * @returns The ISO 8601 week corresponding to the specified date.
121
+ */
122
+ export function toIsoWeek(date: Date): string {
123
+ return `${date.getFullYear().toString().padStart(4, "0")}-W${getWeekOfYear(date).toString().padStart(2, "0")}`;
124
+ }
125
+
126
+ /**
127
+ * Converts the specified date to the `YYYY-MM-DD HH:MM:SS` format.
128
+ * @param date The date to convert.
129
+ * @param options Values indicating how to format the resulting string.
130
+ * @returns The `YYYY-MM-DD HH:MM:SS` format corresponding to the specified date.
131
+ */
132
+ export function toYmdHms(date: Date, options: {excludeDate?: boolean, excludeTime?: boolean, separator?: string} = {}): string {
133
+ const padStart = (number: number, length = 2): string => number.toString().padStart(length, "0");
134
+ const parts = [];
135
+ if (!options.excludeDate) parts.push([padStart(date.getFullYear(), 4), padStart(date.getMonth() + 1), padStart(date.getDate())].join("-"));
136
+ if (!options.excludeTime) parts.push([date.getHours(), date.getMinutes(), date.getSeconds()].map(item => padStart(item)).join(":"));
137
+ return parts.join(options.separator ?? " ");
138
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Provides some common durations in milliseconds.
3
+ */
4
+ export const Duration = Object.freeze({
5
+
6
+ /**
7
+ * One second.
8
+ */
9
+ Second: 1_000,
10
+
11
+ /**
12
+ * One minute.
13
+ */
14
+ Minute: 60_000,
15
+
16
+ /**
17
+ * One hour.
18
+ */
19
+ Hour: 3_600_000,
20
+
21
+ /**
22
+ * One day.
23
+ */
24
+ Day: 86_400_000
25
+ });
26
+
27
+ /**
28
+ * Provides some common durations in milliseconds.
29
+ */
30
+ export type Duration = typeof Duration[keyof typeof Duration];
@@ -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 mode.
29
+ * @param theme The application theme.
30
30
  * @returns The icon corresponding to the specified theme.
31
31
  */
32
- export function themeIcon(theme: AppTheme): string {
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 mode.
42
+ * @param theme The application theme.
43
43
  * @returns The label corresponding to the specified theme.
44
44
  */
45
- export function themeLabel(theme: AppTheme): string {
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";
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Defines contextual modifiers.
3
+ */
4
+ export const Context = Object.freeze({
5
+
6
+ /**
7
+ * A danger.
8
+ */
9
+ Danger: "danger",
10
+
11
+ /**
12
+ * A warning.
13
+ */
14
+ Warning: "warning",
15
+
16
+ /**
17
+ * An information.
18
+ */
19
+ Info: "info",
20
+
21
+ /**
22
+ * A success.
23
+ */
24
+ Success: "success"
25
+ });
26
+
27
+ /**
28
+ * Defines contextual modifiers.
29
+ */
30
+ export type Context = typeof Context[keyof typeof Context];
31
+
32
+ /**
33
+ * Gets the icon corresponding to the specified context.
34
+ * @param context The context.
35
+ * @returns The icon corresponding to the specified context.
36
+ */
37
+ export function contextIcon(context: Context): string {
38
+ switch (context) {
39
+ case Context.Danger: return "error";
40
+ case Context.Success: return "check_circle";
41
+ case Context.Warning: return "warning";
42
+ default: return "info";
43
+ }
44
+ }
@@ -0,0 +1,85 @@
1
+ import {Duration} from "#Base/Duration.js";
2
+
3
+ /**
4
+ * Downloads the specified file.
5
+ * @param file The file to be downloaded.
6
+ */
7
+ export function downloadFile(file: File): void {
8
+ const url = URL.createObjectURL(file);
9
+ const anchor = document.createElement("a");
10
+ anchor.download = file.name;
11
+ anchor.href = url;
12
+
13
+ document.body.appendChild(anchor);
14
+ anchor.click();
15
+ document.body.removeChild(anchor);
16
+ URL.revokeObjectURL(url);
17
+ }
18
+
19
+ /**
20
+ * Downloads the specified text content.
21
+ * @param text The text content.
22
+ * @param fileName The file name.
23
+ * @param options The optional attributes for the file.
24
+ */
25
+ export function downloadString(text: string, fileName: string, options: FilePropertyBag = {}): void {
26
+ downloadFile(new File([text], fileName, options));
27
+ }
28
+
29
+ /**
30
+ * Opens the specified file.
31
+ * @param file The file to be opened.
32
+ * @param options Value indicating whether to open the file in a new tab.
33
+ */
34
+ export function openFile(file: File, options: {newTab?: boolean} = {}): void {
35
+ const url = URL.createObjectURL(file);
36
+ if (!options.newTab) {
37
+ location.assign(url);
38
+ return;
39
+ }
40
+
41
+ const handle = window.open(url, "_blank");
42
+ if (!handle) {
43
+ location.assign(url);
44
+ return;
45
+ }
46
+
47
+ const timer = window.setInterval(() => {
48
+ if (!handle.closed) return;
49
+ clearInterval(timer);
50
+ URL.revokeObjectURL(url);
51
+ }, 5 * Duration.Second);
52
+ }
53
+
54
+ /**
55
+ * Prints the specified file.
56
+ * @param file The file to be printed.
57
+ */
58
+ export function printFile(file: File): void {
59
+ const url = URL.createObjectURL(file);
60
+ const frame = document.createElement("iframe");
61
+ frame.addEventListener("load", () => frame.contentWindow?.print());
62
+ frame.hidden = true;
63
+ frame.src = url;
64
+
65
+ window.addEventListener("focus", () => {
66
+ document.body.removeChild(frame);
67
+ URL.revokeObjectURL(url);
68
+ }, {once: true});
69
+
70
+ document.body.appendChild(frame);
71
+ }
72
+
73
+ /**
74
+ * Converts the specified file to a data URL.
75
+ * @param file The file to convert.
76
+ * @returns The data URL corresponding to the given file.
77
+ */
78
+ export function toDataUrl(file: File): Promise<URL> {
79
+ return new Promise((resolve, reject) => {
80
+ const reader = new FileReader;
81
+ reader.addEventListener("error", reject);
82
+ reader.addEventListener("load", () => resolve(new URL(reader.result as string)));
83
+ reader.readAsDataURL(file);
84
+ });
85
+ }
@@ -44,7 +44,7 @@ export class ViewportScroller {
44
44
  this.#scrollOffset += Number.isNaN(navbarHeight) ? 0 : navbarHeight;
45
45
  }
46
46
 
47
- const actionBar: HTMLElement|null = document.body.querySelector("action-bar");
47
+ const actionBar = document.body.querySelector<HTMLElement>("action-bar");
48
48
  return this.#scrollOffset + (actionBar?.offsetHeight ?? 0);
49
49
  }
50
50
 
@@ -9,5 +9,8 @@
9
9
  "outDir": "../../../lib/Html",
10
10
  "rootDir": ".",
11
11
  "tsBuildInfoFile": "../../../var/Html.tsbuildinfo"
12
- }
12
+ },
13
+ "references": [
14
+ {"path": "../Base/tsconfig.json"}
15
+ ]
13
16
  }