@cedx/base 0.10.1 → 0.12.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 (45) hide show
  1. package/ReadMe.md +1 -1
  2. package/lib/Data/Pagination.d.ts +3 -3
  3. package/lib/Data/Pagination.js +10 -10
  4. package/lib/UI/Components/KeyboardAccelerator.d.ts +11 -0
  5. package/lib/UI/Components/KeyboardAccelerator.d.ts.map +1 -1
  6. package/lib/UI/Components/KeyboardAccelerator.js +1 -1
  7. package/lib/UI/Components/OfflineIndicator.d.ts +0 -4
  8. package/lib/UI/Components/OfflineIndicator.d.ts.map +1 -1
  9. package/lib/UI/Components/OfflineIndicator.js +5 -11
  10. package/lib/UI/Components/TabActivator.d.ts +2 -2
  11. package/lib/UI/Components/TabActivator.d.ts.map +1 -1
  12. package/lib/UI/Components/TabActivator.js +6 -6
  13. package/lib/UI/Components/ThemeDropdown.d.ts +16 -0
  14. package/lib/UI/Components/ThemeDropdown.d.ts.map +1 -1
  15. package/lib/UI/Components/ThemeDropdown.js +73 -29
  16. package/lib/UI/Components/Toast.d.ts +74 -0
  17. package/lib/UI/Components/Toast.d.ts.map +1 -0
  18. package/lib/UI/Components/Toast.js +225 -0
  19. package/lib/UI/ElementExtensions.d.ts +13 -0
  20. package/lib/UI/ElementExtensions.d.ts.map +1 -0
  21. package/lib/UI/ElementExtensions.js +18 -0
  22. package/lib/UI/Position.d.ts +46 -0
  23. package/lib/UI/Position.d.ts.map +1 -0
  24. package/lib/UI/Position.js +41 -0
  25. package/lib/UI/Size.d.ts +34 -0
  26. package/lib/UI/Size.d.ts.map +1 -0
  27. package/lib/UI/Size.js +29 -0
  28. package/lib/UI/Variant.d.ts +26 -0
  29. package/lib/UI/Variant.d.ts.map +1 -0
  30. package/lib/UI/Variant.js +21 -0
  31. package/lib/UI/ViewportScroller.js +1 -1
  32. package/package.json +5 -5
  33. package/src/Client/Data/Pagination.ts +11 -11
  34. package/src/Client/UI/Components/KeyboardAccelerator.ts +14 -1
  35. package/src/Client/UI/Components/MessageBox.old +242 -0
  36. package/src/Client/UI/Components/OfflineIndicator.ts +5 -13
  37. package/src/Client/UI/Components/TabActivator.ts +7 -7
  38. package/src/Client/UI/Components/ThemeDropdown.ts +79 -26
  39. package/src/Client/UI/Components/Toast.ts +252 -0
  40. package/src/Client/UI/Components/Toaster.old +0 -0
  41. package/src/Client/UI/ElementExtensions.ts +19 -0
  42. package/src/Client/UI/Position.ts +55 -0
  43. package/src/Client/UI/Size.ts +40 -0
  44. package/src/Client/UI/Variant.ts +30 -0
  45. package/src/Client/UI/ViewportScroller.ts +1 -1
@@ -0,0 +1,225 @@
1
+ import { Duration } from "@cedx/base/Duration.js";
2
+ import { Context, getIcon } from "@cedx/base/UI/Context.js";
3
+ import { Toast as BootstrapToast } from "bootstrap";
4
+ /**
5
+ * Manages the notification messages.
6
+ */
7
+ export class Toast extends HTMLElement {
8
+ /**
9
+ * The list of observed attributes.
10
+ */
11
+ static observedAttributes = ["caption", "context", "culture", "icon"];
12
+ /**
13
+ * The time units.
14
+ */
15
+ static #timeUnits = ["second", "minute", "hour"];
16
+ /**
17
+ * The formatter used to format the relative time.
18
+ */
19
+ #formatter;
20
+ /**
21
+ * The toast header.
22
+ */
23
+ #header = this.querySelector(".toast-header");
24
+ /**
25
+ * The time at which this component was initially shown.
26
+ */
27
+ #initialTime = Date.now();
28
+ /**
29
+ * The timer identifier.
30
+ */
31
+ #timer = 0;
32
+ /**
33
+ * The underlying Bootstrap toast.
34
+ */
35
+ #toast;
36
+ /**
37
+ * Registers the component.
38
+ */
39
+ static {
40
+ customElements.define("toaster-item", this);
41
+ }
42
+ /**
43
+ * Value indicating whether to apply a fade transition.
44
+ */
45
+ get animation() {
46
+ return this.hasAttribute("animation");
47
+ }
48
+ set animation(value) {
49
+ if (value)
50
+ this.setAttribute("animation", "");
51
+ else
52
+ this.removeAttribute("animation");
53
+ }
54
+ /**
55
+ * Value indicating whether to automatically hide the notification.
56
+ */
57
+ get autoHide() {
58
+ return this.hasAttribute("autohide");
59
+ }
60
+ set autoHide(value) {
61
+ if (value)
62
+ this.setAttribute("autohide", "");
63
+ else
64
+ this.removeAttribute("autohide");
65
+ }
66
+ /**
67
+ * The title displayed in the header.
68
+ */
69
+ get caption() {
70
+ return (this.getAttribute("caption") ?? "").trim();
71
+ }
72
+ set caption(value) {
73
+ this.setAttribute("caption", value);
74
+ }
75
+ /**
76
+ * A contextual modifier.
77
+ */
78
+ get context() {
79
+ const value = this.getAttribute("context");
80
+ return Object.values(Context).includes(value) ? value : Context.Info;
81
+ }
82
+ set context(value) {
83
+ this.setAttribute("context", value);
84
+ }
85
+ /**
86
+ * The culture used to format the relative time.
87
+ */
88
+ get culture() {
89
+ const value = this.getAttribute("culture") ?? "";
90
+ return new Intl.Locale(value.trim() || navigator.language);
91
+ }
92
+ set culture(value) {
93
+ this.setAttribute("culture", value.toString());
94
+ }
95
+ /**
96
+ * The delay, in milliseconds, to hide the notification.
97
+ */
98
+ get delay() {
99
+ const value = Number(this.getAttribute("delay"));
100
+ return Math.max(1, Number.isNaN(value) ? 5_000 : value);
101
+ }
102
+ set delay(value) {
103
+ this.setAttribute("delay", value.toString());
104
+ }
105
+ /**
106
+ * The time elapsed since this component was initially shown, in milliseconds.
107
+ */
108
+ get elapsedTime() {
109
+ return Date.now() - this.#initialTime;
110
+ }
111
+ /**
112
+ * The icon displayed next to the caption.
113
+ */
114
+ get icon() {
115
+ const value = this.getAttribute("icon") ?? "";
116
+ return value.trim() || getIcon(Context.Info);
117
+ }
118
+ set icon(value) {
119
+ this.setAttribute("icon", value);
120
+ }
121
+ /**
122
+ * Method invoked when an attribute has been changed.
123
+ * @param attribute The attribute name.
124
+ * @param oldValue The previous attribute value.
125
+ * @param newValue The new attribute value.
126
+ */
127
+ attributeChangedCallback(attribute, oldValue, newValue) {
128
+ if (newValue != oldValue)
129
+ switch (attribute) {
130
+ case "caption":
131
+ this.#updateCaption(newValue ?? "");
132
+ break;
133
+ case "context":
134
+ this.#updateContext(Object.values(Context).includes(newValue) ? newValue : Context.Info);
135
+ break;
136
+ case "culture":
137
+ this.#formatter = new Intl.RelativeTimeFormat((newValue ?? "").trim() || navigator.language, { style: "long" });
138
+ break;
139
+ case "icon":
140
+ this.#updateIcon(newValue ?? "");
141
+ break;
142
+ // No default
143
+ }
144
+ }
145
+ /**
146
+ * Method invoked when this component is connected.
147
+ */
148
+ connectedCallback() {
149
+ const toast = this.querySelector(".toast");
150
+ toast.addEventListener("hidden.bs.toast", () => clearInterval(this.#timer));
151
+ toast.addEventListener("show.bs.toast", () => this.#timer = window.setInterval(this.#updateElapsedTime, Duration.Second));
152
+ const { animation, autoHide: autohide, delay } = this;
153
+ this.#toast = new BootstrapToast(toast, { animation, autohide, delay });
154
+ }
155
+ /**
156
+ * Method invoked when this component is disconnected.
157
+ */
158
+ disconnectedCallback() {
159
+ clearInterval(this.#timer);
160
+ this.#toast.dispose();
161
+ }
162
+ /**
163
+ * Hides this toast.
164
+ */
165
+ hide() {
166
+ this.#toast.hide();
167
+ }
168
+ /**
169
+ * Shows this toast.
170
+ */
171
+ show() {
172
+ if (!this.#toast.isShown()) {
173
+ this.#initialTime = Date.now();
174
+ this.#updateElapsedTime();
175
+ }
176
+ this.#toast.show();
177
+ }
178
+ /**
179
+ * Formats the specified elapsed time.
180
+ * @param elapsed The elapsed time, in seconds.
181
+ * @returns The formated time.
182
+ */
183
+ #formatTime(elapsed) {
184
+ let index = 0;
185
+ while (elapsed > 60 && index < Toast.#timeUnits.length) {
186
+ elapsed /= 60;
187
+ index++;
188
+ }
189
+ return this.#formatter.format(Math.ceil(-elapsed), Toast.#timeUnits[index]);
190
+ }
191
+ /**
192
+ * Updates the title displayed in the header.
193
+ * @param value The new value.
194
+ */
195
+ #updateCaption(value) {
196
+ this.#header.querySelector("b").textContent = value.trim();
197
+ }
198
+ /**
199
+ * Updates the title displayed in the header.
200
+ * @param value The new value.
201
+ */
202
+ #updateContext(value) {
203
+ const contexts = Object.values(Context);
204
+ let { classList } = this.#header;
205
+ classList.remove(...contexts.map(context => `toast-header-${context}`));
206
+ classList.add(`toast-header-${value}`);
207
+ ({ classList } = this.#header.querySelector(".icon"));
208
+ classList.remove(...contexts.map(context => `text-${context}`));
209
+ classList.add(`text-${value}`);
210
+ }
211
+ /**
212
+ * Updates the label corresponding to the elapsed time.
213
+ */
214
+ #updateElapsedTime = () => {
215
+ const { elapsedTime } = this;
216
+ this.#header.querySelector("small").textContent = elapsedTime > 0 ? this.#formatTime(elapsedTime / Duration.Second) : "";
217
+ };
218
+ /**
219
+ * Updates the icon displayed next to the caption.
220
+ * @param value The new value.
221
+ */
222
+ #updateIcon(value) {
223
+ this.#header.querySelector(".icon").textContent = value.trim() || getIcon(Context.Info);
224
+ }
225
+ }
@@ -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
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Defines the placement of an element.
3
+ */
4
+ export declare const Position: Readonly<{
5
+ /**
6
+ * Top left.
7
+ */
8
+ TopLeft: "TopLeft";
9
+ /**
10
+ * Top center.
11
+ */
12
+ TopCenter: "TopCenter";
13
+ /**
14
+ * Top right.
15
+ */
16
+ TopRight: "TopRight";
17
+ /**
18
+ * Middle left.
19
+ */
20
+ MiddleLeft: "MiddleLeft";
21
+ /**
22
+ * Middle center.
23
+ */
24
+ MiddleCenter: "MiddleCenter";
25
+ /**
26
+ * Middle right.
27
+ */
28
+ MiddleRight: "MiddleRight";
29
+ /**
30
+ * Bottom left.
31
+ */
32
+ BottomLeft: "BottomLeft";
33
+ /**
34
+ * Bottom center.
35
+ */
36
+ BottomCenter: "BottomCenter";
37
+ /**
38
+ * Bottom right.
39
+ */
40
+ BottomRight: "BottomRight";
41
+ }>;
42
+ /**
43
+ * Defines the placement of an element.
44
+ */
45
+ export type Position = typeof Position[keyof typeof Position];
46
+ //# sourceMappingURL=Position.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Position.d.ts","sourceRoot":"","sources":["../../src/Client/UI/Position.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,QAAQ;IAEpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Defines the placement of an element.
3
+ */
4
+ export const Position = Object.freeze({
5
+ /**
6
+ * Top left.
7
+ */
8
+ TopLeft: "TopLeft",
9
+ /**
10
+ * Top center.
11
+ */
12
+ TopCenter: "TopCenter",
13
+ /**
14
+ * Top right.
15
+ */
16
+ TopRight: "TopRight",
17
+ /**
18
+ * Middle left.
19
+ */
20
+ MiddleLeft: "MiddleLeft",
21
+ /**
22
+ * Middle center.
23
+ */
24
+ MiddleCenter: "MiddleCenter",
25
+ /**
26
+ * Middle right.
27
+ */
28
+ MiddleRight: "MiddleRight",
29
+ /**
30
+ * Bottom left.
31
+ */
32
+ BottomLeft: "BottomLeft",
33
+ /**
34
+ * Bottom center.
35
+ */
36
+ BottomCenter: "BottomCenter",
37
+ /**
38
+ * Bottom right.
39
+ */
40
+ BottomRight: "BottomRight"
41
+ });
@@ -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, .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.1",
10
+ "version": "0.12.0",
11
11
  "devDependencies": {
12
- "@playwright/browser-chromium": "^1.54.2",
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": "^5.3.1",
18
+ "chai": "^6.0.1",
19
19
  "esbuild": "^0.25.9",
20
20
  "globals": "^16.3.0",
21
21
  "mocha": "^11.7.1",
22
- "playwright": "^1.54.2",
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.39.1"
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 one-based current page number.
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 ?? 1;
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 one-based current page number.
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(1, value);
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 > 1;
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 one-based last page number.
66
+ * The last page index.
67
67
  */
68
68
  get lastPageIndex(): number {
69
- return this.#totalItemCount > 0 ? Math.ceil(this.#totalItemCount / this.#itemsPerPage) : 1;
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 (this.#currentPageIndex - 1) * this.#itemsPerPage;
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 one-based current page number.
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).click();
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
  }