@cedx/base 0.6.0 → 0.7.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 (125) hide show
  1. package/ReadMe.md +1 -1
  2. package/lib/Data/Pagination.d.ts +30 -13
  3. package/lib/Data/Pagination.d.ts.map +1 -1
  4. package/lib/Data/Pagination.js +39 -12
  5. package/lib/Data/Sort.d.ts +25 -23
  6. package/lib/Data/Sort.d.ts.map +1 -1
  7. package/lib/Data/Sort.js +37 -33
  8. package/lib/{Date.d.ts → DateExtensions.d.ts} +1 -1
  9. package/lib/DateExtensions.d.ts.map +1 -0
  10. package/lib/{Html/File.d.ts → FileExtensions.d.ts} +4 -11
  11. package/lib/FileExtensions.d.ts.map +1 -0
  12. package/lib/{Html/File.js → FileExtensions.js} +3 -12
  13. package/lib/{Http → Net/Http}/HttpClient.d.ts +17 -2
  14. package/lib/Net/Http/HttpClient.d.ts.map +1 -0
  15. package/lib/{Http → Net/Http}/HttpClient.js +17 -15
  16. package/lib/Net/Http/HttpMethod.d.ts +46 -0
  17. package/lib/Net/Http/HttpMethod.d.ts.map +1 -0
  18. package/lib/Net/Http/HttpMethod.js +41 -0
  19. package/lib/Net/Http/HttpRequestError.d.ts +33 -0
  20. package/lib/Net/Http/HttpRequestError.d.ts.map +1 -0
  21. package/lib/{Http/HttpError.js → Net/Http/HttpRequestError.js} +16 -16
  22. package/lib/Net/Http/StatusCode.d.ts +122 -0
  23. package/lib/Net/Http/StatusCode.d.ts.map +1 -0
  24. package/lib/Net/Http/StatusCode.js +117 -0
  25. package/lib/Net/Mime/DispositionType.d.ts +18 -0
  26. package/lib/Net/Mime/DispositionType.d.ts.map +1 -0
  27. package/lib/Net/Mime/DispositionType.js +13 -0
  28. package/lib/Net/Mime/MediaType.d.ts +151 -0
  29. package/lib/Net/Mime/MediaType.d.ts.map +1 -0
  30. package/lib/Net/Mime/MediaType.js +150 -0
  31. package/lib/{Number.d.ts → NumberExtensions.d.ts} +1 -1
  32. package/lib/NumberExtensions.d.ts.map +1 -0
  33. package/lib/{String.d.ts → StringExtensions.d.ts} +1 -1
  34. package/lib/StringExtensions.d.ts.map +1 -0
  35. package/lib/{Html → UI}/AppTheme.d.ts +3 -3
  36. package/lib/UI/AppTheme.d.ts.map +1 -0
  37. package/lib/{Html → UI}/AppTheme.js +3 -3
  38. package/lib/UI/{Component.d.ts → Components/ComponentBase.d.ts} +5 -5
  39. package/lib/UI/Components/ComponentBase.d.ts.map +1 -0
  40. package/lib/UI/Components/ComponentBase.js +29 -0
  41. package/lib/UI/{LoadingIndicator.d.ts → Components/LoadingIndicator.d.ts} +1 -2
  42. package/lib/UI/Components/LoadingIndicator.d.ts.map +1 -0
  43. package/lib/UI/Components/MenuActivator.d.ts.map +1 -0
  44. package/lib/UI/Components/OfflineIndicator.d.ts.map +1 -0
  45. package/lib/UI/Components/ThemeDropdown.d.ts +67 -0
  46. package/lib/UI/Components/ThemeDropdown.d.ts.map +1 -0
  47. package/lib/UI/Components/ThemeDropdown.js +140 -0
  48. package/lib/UI/Context.d.ts.map +1 -0
  49. package/lib/UI/MenuAlignment.d.ts +18 -0
  50. package/lib/UI/MenuAlignment.d.ts.map +1 -0
  51. package/lib/UI/MenuAlignment.js +13 -0
  52. package/lib/{Html → UI}/ViewportScroller.d.ts +1 -1
  53. package/lib/UI/ViewportScroller.d.ts.map +1 -0
  54. package/lib/{Html → UI}/ViewportScroller.js +6 -6
  55. package/package.json +5 -7
  56. package/src/Client/Data/Pagination.ts +59 -13
  57. package/src/Client/Data/Sort.ts +40 -35
  58. package/src/Client/Data/tsconfig.json +1 -1
  59. package/src/Client/{Html/File.ts → FileExtensions.ts} +3 -13
  60. package/src/Client/{Http → Net/Http}/HttpClient.ts +32 -14
  61. package/src/Client/Net/Http/HttpMethod.ts +55 -0
  62. package/src/Client/{Http/HttpError.ts → Net/Http/HttpRequestError.ts} +17 -17
  63. package/src/Client/Net/Http/StatusCode.ts +150 -0
  64. package/src/Client/Net/Mime/DispositionType.ts +20 -0
  65. package/src/Client/Net/Mime/MediaType.ts +185 -0
  66. package/src/Client/{Abstractions → Net}/tsconfig.json +3 -3
  67. package/src/Client/{Html → UI}/AppTheme.ts +3 -3
  68. package/src/Client/UI/Components/ComponentBase.ts +34 -0
  69. package/src/Client/UI/{LoadingIndicator.ts → Components/LoadingIndicator.ts} +1 -3
  70. package/src/Client/UI/Components/ThemeDropdown.ts +163 -0
  71. package/src/Client/UI/MenuAlignment.ts +20 -0
  72. package/src/Client/{Html → UI}/ViewportScroller.ts +6 -6
  73. package/src/Client/UI/tsconfig.json +2 -3
  74. package/src/Client/tsconfig.json +1 -4
  75. package/lib/Abstractions/ILoadingIndicator.d.ts +0 -17
  76. package/lib/Abstractions/ILoadingIndicator.d.ts.map +0 -1
  77. package/lib/Abstractions/ILoadingIndicator.js +0 -1
  78. package/lib/Date.d.ts.map +0 -1
  79. package/lib/DependencyInjection/Container.d.ts +0 -43
  80. package/lib/DependencyInjection/Container.d.ts.map +0 -1
  81. package/lib/DependencyInjection/Container.js +0 -65
  82. package/lib/Html/AppTheme.d.ts.map +0 -1
  83. package/lib/Html/Context.d.ts.map +0 -1
  84. package/lib/Html/File.d.ts.map +0 -1
  85. package/lib/Html/ViewportScroller.d.ts.map +0 -1
  86. package/lib/Http/HttpClient.d.ts.map +0 -1
  87. package/lib/Http/HttpError.d.ts +0 -33
  88. package/lib/Http/HttpError.d.ts.map +0 -1
  89. package/lib/Http/StatusCodes.d.ts +0 -114
  90. package/lib/Http/StatusCodes.d.ts.map +0 -1
  91. package/lib/Http/StatusCodes.js +0 -109
  92. package/lib/Number.d.ts.map +0 -1
  93. package/lib/String.d.ts.map +0 -1
  94. package/lib/UI/Component.d.ts.map +0 -1
  95. package/lib/UI/Component.js +0 -29
  96. package/lib/UI/LoadingIndicator.d.ts.map +0 -1
  97. package/lib/UI/MenuActivator.d.ts.map +0 -1
  98. package/lib/UI/OfflineIndicator.d.ts.map +0 -1
  99. package/lib/UI/ThemeDropdown.d.ts +0 -40
  100. package/lib/UI/ThemeDropdown.d.ts.map +0 -1
  101. package/lib/UI/ThemeDropdown.js +0 -80
  102. package/src/Client/Abstractions/ILoadingIndicator.ts +0 -16
  103. package/src/Client/DependencyInjection/Container.ts +0 -75
  104. package/src/Client/DependencyInjection/tsconfig.json +0 -13
  105. package/src/Client/Html/tsconfig.json +0 -16
  106. package/src/Client/Http/StatusCodes.ts +0 -140
  107. package/src/Client/Http/tsconfig.json +0 -16
  108. package/src/Client/UI/Component.ts +0 -34
  109. package/src/Client/UI/ThemeDropdown.ts +0 -104
  110. /package/lib/{Date.js → DateExtensions.js} +0 -0
  111. /package/lib/{Number.js → NumberExtensions.js} +0 -0
  112. /package/lib/{String.js → StringExtensions.js} +0 -0
  113. /package/lib/UI/{LoadingIndicator.js → Components/LoadingIndicator.js} +0 -0
  114. /package/lib/UI/{MenuActivator.d.ts → Components/MenuActivator.d.ts} +0 -0
  115. /package/lib/UI/{MenuActivator.js → Components/MenuActivator.js} +0 -0
  116. /package/lib/UI/{OfflineIndicator.d.ts → Components/OfflineIndicator.d.ts} +0 -0
  117. /package/lib/UI/{OfflineIndicator.js → Components/OfflineIndicator.js} +0 -0
  118. /package/lib/{Html → UI}/Context.d.ts +0 -0
  119. /package/lib/{Html → UI}/Context.js +0 -0
  120. /package/src/Client/{Date.ts → DateExtensions.ts} +0 -0
  121. /package/src/Client/{Number.ts → NumberExtensions.ts} +0 -0
  122. /package/src/Client/{String.ts → StringExtensions.ts} +0 -0
  123. /package/src/Client/UI/{MenuActivator.ts → Components/MenuActivator.ts} +0 -0
  124. /package/src/Client/UI/{OfflineIndicator.ts → Components/OfflineIndicator.ts} +0 -0
  125. /package/src/Client/{Html → UI}/Context.ts +0 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Defines the alignment of a dropdown menu.
3
+ */
4
+ export declare const MenuAlignment: Readonly<{
5
+ /**
6
+ * The dropdown menu is left aligned.
7
+ */
8
+ Start: "Start";
9
+ /**
10
+ * The dropdown menu is right aligned.
11
+ */
12
+ End: "End";
13
+ }>;
14
+ /**
15
+ * Defines the alignment of a dropdown menu.
16
+ */
17
+ export type MenuAlignment = typeof MenuAlignment[keyof typeof MenuAlignment];
18
+ //# sourceMappingURL=MenuAlignment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MenuAlignment.d.ts","sourceRoot":"","sources":["../../src/Client/UI/MenuAlignment.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,aAAa;IAEzB;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Defines the alignment of a dropdown menu.
3
+ */
4
+ export const MenuAlignment = Object.freeze({
5
+ /**
6
+ * The dropdown menu is left aligned.
7
+ */
8
+ Start: "Start",
9
+ /**
10
+ * The dropdown menu is right aligned.
11
+ */
12
+ End: "End"
13
+ });
@@ -23,7 +23,7 @@ export declare class ViewportScroller {
23
23
  get scrollOffset(): number;
24
24
  /**
25
25
  * Scrolls to the specified anchor.
26
- * @param anchor The identifier or name of an elment.
26
+ * @param anchor The identifier or name of an element.
27
27
  * @param options Value indicating whether scrolling is instant or animates smoothly.
28
28
  */
29
29
  scrollToAnchor(anchor: string, options?: ScrollOptions): void;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ViewportScroller.d.ts","sourceRoot":"","sources":["../../src/Client/UI/ViewportScroller.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC;IAEnC;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAA;CACxB,CAAC,CAAC;AAEH;;GAEG;AACH,qBAAa,gBAAgB;;IAY5B;;;OAGG;gBACS,QAAQ,GAAE,MAAM,OAAqE;IAIjG;;OAEG;IACH,IAAI,YAAY,IAAI,MAAM,CAWzB;IAED;;;;OAIG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI;IAKjE;;;;OAIG;IACH,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI;IAMpE;;;;;OAKG;IACH,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,IAAI;IAIzE;;;OAGG;IACH,WAAW,CAAC,OAAO,GAAE,aAAkB,GAAG,IAAI;CAG9C"}
@@ -22,21 +22,21 @@ export class ViewportScroller {
22
22
  */
23
23
  get scrollOffset() {
24
24
  if (this.#scrollOffset < 0) {
25
- const fontSize = parseInt(getComputedStyle(document.body).fontSize);
25
+ const fontSize = Number.parseInt(getComputedStyle(document.body).fontSize);
26
26
  this.#scrollOffset = Number.isNaN(fontSize) ? 0 : fontSize * 2;
27
- const navbarHeight = parseInt(getComputedStyle(document.documentElement).getPropertyValue("--navbar-height"));
28
- this.#scrollOffset += Number.isNaN(navbarHeight) ? 0 : navbarHeight;
27
+ const navbar = document.body.querySelector(".navbar");
28
+ this.#scrollOffset += (navbar?.offsetHeight ?? 0);
29
29
  }
30
- const actionBar = document.body.querySelector("action-bar");
30
+ const actionBar = document.body.querySelector("action-bar, .action-bar");
31
31
  return this.#scrollOffset + (actionBar?.offsetHeight ?? 0);
32
32
  }
33
33
  /**
34
34
  * Scrolls to the specified anchor.
35
- * @param anchor The identifier or name of an elment.
35
+ * @param anchor The identifier or name of an element.
36
36
  * @param options Value indicating whether scrolling is instant or animates smoothly.
37
37
  */
38
38
  scrollToAnchor(anchor, options = {}) {
39
- const element = document.getElementById(anchor) ?? document.body.querySelector(`[name="${anchor}"]`);
39
+ const element = document.getElementById(anchor) ?? this.#viewport().querySelector(`[name="${anchor}"]`);
40
40
  if (element)
41
41
  this.scrollToElement(element, options);
42
42
  }
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.6.0",
10
+ "version": "0.7.0",
11
11
  "devDependencies": {
12
12
  "@playwright/browser-chromium": "^1.54.2",
13
13
  "@types/bootstrap": "^5.2.10",
@@ -16,14 +16,14 @@
16
16
  "@types/node": "^24.2.1",
17
17
  "@types/serve-handler": "^6.1.4",
18
18
  "chai": "^5.2.1",
19
- "esbuild": "^0.25.8",
19
+ "esbuild": "^0.25.9",
20
20
  "globals": "^16.3.0",
21
21
  "mocha": "^11.7.1",
22
22
  "playwright": "^1.54.2",
23
23
  "serve-handler": "^6.1.6",
24
- "typedoc": "^0.28.9",
24
+ "typedoc": "^0.28.10",
25
25
  "typescript": "^5.9.2",
26
- "typescript-eslint": "^8.39.0"
26
+ "typescript-eslint": "^8.39.1"
27
27
  },
28
28
  "engines": {
29
29
  "node": ">=24.0.0"
@@ -39,9 +39,7 @@
39
39
  "src/Client/"
40
40
  ],
41
41
  "imports": {
42
- "#Abstractions/*.js": "./lib/Abstractions/*.js",
43
- "#Base/*.js": "./lib/*.js",
44
- "#Html/*.js": "./lib/Html/*.js"
42
+ "#Base/*.js": "./lib/*.js"
45
43
  },
46
44
  "keywords": [
47
45
  "belin",
@@ -6,68 +6,98 @@ export class Pagination {
6
6
  /**
7
7
  * The one-based current page number.
8
8
  */
9
- currentPageIndex: number;
9
+ #currentPageIndex!: number;
10
10
 
11
11
  /**
12
12
  * The number of items per page.
13
13
  */
14
- itemsPerPage: number;
14
+ #itemsPerPage!: number;
15
15
 
16
16
  /**
17
17
  * The total number of items.
18
18
  */
19
- totalItemCount: number;
19
+ #totalItemCount!: number;
20
20
 
21
21
  /**
22
22
  * Creates a new pagination.
23
23
  * @param options An object providing values to initialize this instance.
24
24
  */
25
25
  constructor(options: PaginationOptions = {}) {
26
- this.currentPageIndex = Math.max(1, options.currentPageIndex ?? 1);
27
- this.itemsPerPage = Math.max(1, Math.min(1000, options.itemsPerPage ?? 25));
28
- this.totalItemCount = Math.max(0, options.totalItemCount ?? 0);
26
+ this.currentPageIndex = options.currentPageIndex ?? 1;
27
+ this.itemsPerPage = options.itemsPerPage ?? 25;
28
+ this.totalItemCount = options.totalItemCount ?? 0;
29
+ }
30
+
31
+ /**
32
+ * The one-based current page number.
33
+ */
34
+ get currentPageIndex(): number {
35
+ return this.#currentPageIndex;
36
+ }
37
+ set currentPageIndex(value: number) {
38
+ this.#currentPageIndex = Math.max(1, value);
29
39
  }
30
40
 
31
41
  /**
32
42
  * Value indicating whether a next page exists.
33
43
  */
34
44
  get hasNextPage(): boolean {
35
- return this.currentPageIndex < this.totalItemCount;
45
+ return this.#currentPageIndex < this.lastPageIndex;
36
46
  }
37
47
 
38
48
  /**
39
49
  * Value indicating whether a previous page exists.
40
50
  */
41
51
  get hasPreviousPage(): boolean {
42
- return this.currentPageIndex > 1;
52
+ return this.#currentPageIndex > 1;
53
+ }
54
+
55
+ /**
56
+ * The number of items per page.
57
+ */
58
+ get itemsPerPage(): number {
59
+ return this.#itemsPerPage;
60
+ }
61
+ set itemsPerPage(value: number) {
62
+ this.#itemsPerPage = Math.max(1, Math.min(1000, value));
43
63
  }
44
64
 
45
65
  /**
46
66
  * The one-based last page number.
47
67
  */
48
68
  get lastPageIndex(): number {
49
- return Math.ceil(this.totalItemCount / this.itemsPerPage);
69
+ return this.#totalItemCount > 0 ? Math.ceil(this.#totalItemCount / this.#itemsPerPage) : 1;
50
70
  }
51
71
 
52
72
  /**
53
73
  * The data limit.
54
74
  */
55
75
  get limit(): number {
56
- return this.itemsPerPage;
76
+ return this.#itemsPerPage;
57
77
  }
58
78
 
59
79
  /**
60
80
  * The data offset.
61
81
  */
62
82
  get offset(): number {
63
- return (this.currentPageIndex - 1) * this.itemsPerPage;
83
+ return (this.#currentPageIndex - 1) * this.#itemsPerPage;
64
84
  }
65
85
 
66
86
  /**
67
87
  * The search parameters corresponding to this object.
68
88
  */
69
89
  get searchParams(): URLSearchParams {
70
- return new URLSearchParams({page: this.currentPageIndex.toString(), perPage: this.itemsPerPage.toString()});
90
+ return new URLSearchParams({page: this.#currentPageIndex.toString(), perPage: this.#itemsPerPage.toString()});
91
+ }
92
+
93
+ /**
94
+ * The total number of items.
95
+ */
96
+ get totalItemCount(): number {
97
+ return this.#totalItemCount;
98
+ }
99
+ set totalItemCount(value: number) {
100
+ this.#totalItemCount = Math.max(0, value);
71
101
  }
72
102
 
73
103
  /**
@@ -87,4 +117,20 @@ export class Pagination {
87
117
  /**
88
118
  * Defines the options of a {@link Pagination} instance.
89
119
  */
90
- export type PaginationOptions = Partial<Pick<Pagination, "currentPageIndex"|"itemsPerPage"|"totalItemCount">>;
120
+ export type PaginationOptions = Partial<{
121
+
122
+ /**
123
+ * The one-based current page number.
124
+ */
125
+ currentPageIndex: number;
126
+
127
+ /**
128
+ * The number of items per page.
129
+ */
130
+ itemsPerPage: number;
131
+
132
+ /**
133
+ * The total number of items.
134
+ */
135
+ totalItemCount: number;
136
+ }>;
@@ -1,21 +1,21 @@
1
1
  /**
2
- * Specifies the order of a sorted property.
2
+ * Specifies the order of a sort property.
3
3
  */
4
4
  export const SortOrder = Object.freeze({
5
5
 
6
6
  /**
7
7
  * The sort is ascending.
8
8
  */
9
- Ascending: "ASC",
9
+ Ascending: "Ascending",
10
10
 
11
11
  /**
12
12
  * The sort is descending.
13
13
  */
14
- Descending: "DESC"
14
+ Descending: "Descending"
15
15
  });
16
16
 
17
17
  /**
18
- * Specifies the order of a sorted property.
18
+ * Specifies the order of a sort property.
19
19
  */
20
20
  export type SortOrder = typeof SortOrder[keyof typeof SortOrder];
21
21
 
@@ -30,7 +30,7 @@ export type SortProperty = [string, SortOrder];
30
30
  export class Sort implements Iterable<SortProperty> {
31
31
 
32
32
  /**
33
- * The list of sorted properties.
33
+ * The list of sort properties.
34
34
  */
35
35
  #properties: SortProperty[];
36
36
 
@@ -43,7 +43,14 @@ export class Sort implements Iterable<SortProperty> {
43
43
  }
44
44
 
45
45
  /**
46
- * The number of properties.
46
+ * The list of sort properties.
47
+ */
48
+ get keys(): string[] {
49
+ return this.#properties.map(item => item[0]);
50
+ }
51
+
52
+ /**
53
+ * The number of properties in this sort.
47
54
  */
48
55
  get length(): number {
49
56
  return this.#properties.length;
@@ -73,33 +80,39 @@ export class Sort implements Iterable<SortProperty> {
73
80
 
74
81
  /**
75
82
  * Returns a new iterator that allows iterating the entries of this sort.
76
- * @returns An iterator over the sorted properties.
83
+ * @returns An iterator over the sort properties.
77
84
  */
78
- *[Symbol.iterator](): Generator<SortProperty, void, void> {
79
- for (const entry of this.#properties) yield entry;
85
+ [Symbol.iterator](): Iterator<SortProperty> {
86
+ return this.#properties[Symbol.iterator]();
80
87
  }
81
88
 
82
89
  /**
83
90
  * Appends the specified property to this sort.
84
91
  * @param property The property name.
85
92
  * @param order The sort order.
86
- * @returns This instance.
93
+ * @throws `Error` when an item with the property name already exists.
87
94
  */
88
- append(property: string, order: SortOrder): this {
89
- this.delete(property);
95
+ add(property: string, order: SortOrder): void {
96
+ if (this.containsKey(property)) throw new Error("An item with the property name has already been added.");
90
97
  this.#properties.push([property, order]);
91
- return this;
92
98
  }
93
99
 
94
100
  /**
95
- * Gets the sorted property at the specified index.
101
+ * Gets the sort property at the specified index.
96
102
  * @param index The position in this sort.
97
- * @returns The sorted property at the specified index, or `null` if it doesn't exist.
103
+ * @returns The sort property at the specified index, or `null` if it doesn't exist.
98
104
  */
99
105
  at(index: number): SortProperty|null {
100
106
  return this.#properties.at(index) ?? null;
101
107
  }
102
108
 
109
+ /**
110
+ * Removes all properties from this sort.
111
+ */
112
+ clear(): void {
113
+ this.#properties = [];
114
+ }
115
+
103
116
  /**
104
117
  * Compares the specified objects, according to the current sort properties.
105
118
  * @param x The first object to compare.
@@ -117,6 +130,15 @@ export class Sort implements Iterable<SortProperty> {
117
130
  return 0;
118
131
  }
119
132
 
133
+ /**
134
+ * Returns a value indicating whether the specified property exists in this sort.
135
+ * @param property The property name.
136
+ * @returns `true` if the specified property exists in this sort, otherwise `false`.
137
+ */
138
+ containsKey(property: string): boolean {
139
+ return this.#properties.some(([key]) => key == property);
140
+ }
141
+
120
142
  /**
121
143
  * Removes the specified property from this sort.
122
144
  * @param property The property name.
@@ -148,15 +170,6 @@ export class Sort implements Iterable<SortProperty> {
148
170
  }
149
171
  }
150
172
 
151
- /**
152
- * Returns a value indicating whether the specified property exists in this sort.
153
- * @param property The property name.
154
- * @returns `true` if the specified property exists in this sort, otherwise `false`.
155
- */
156
- has(property: string): boolean {
157
- return this.#properties.some(([key]) => key == property);
158
- }
159
-
160
173
  /**
161
174
  * Gets the index of the specified property in the underlying list.
162
175
  * @param property The property name.
@@ -207,7 +220,8 @@ export class Sort implements Iterable<SortProperty> {
207
220
  return this;
208
221
  }
209
222
 
210
- return this.append(property, order);
223
+ this.add(property, order);
224
+ return this;
211
225
  }
212
226
 
213
227
  /**
@@ -218,20 +232,11 @@ export class Sort implements Iterable<SortProperty> {
218
232
  return this.toString();
219
233
  }
220
234
 
221
- /**
222
- * Converts this sort to an SQL clause.
223
- * @param escape A function used to escape the SQL identifiers.
224
- * @returns The SQL clause corresponding to this object.
225
- */
226
- toSql(escape: (identifier: string) => string = id => id): string {
227
- return this.#properties.map(([property, order]) => `${escape(property)} ${order}`).join(", ");
228
- }
229
-
230
235
  /**
231
236
  * Returns a string representation of this object.
232
237
  * @returns The string representation of this object.
233
238
  */
234
239
  toString(): string {
235
- return this.#properties.map(([property, order]) => `${order == SortOrder.Descending ? "-" : ""}${property}`).join();
240
+ return this.#properties.map(([property, order]) => `${order == SortOrder.Descending ? "-" : ""}${property}`).join(",");
236
241
  }
237
242
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "extends": "../../../tsconfig.json",
3
- "include": ["*.ts"],
3
+ "include": ["**/*.ts"],
4
4
  "compilerOptions": {
5
5
  "composite": true,
6
6
  "declaration": true,
@@ -4,7 +4,7 @@ import {Duration} from "#Base/Duration.js";
4
4
  * Downloads the specified file.
5
5
  * @param file The file to be downloaded.
6
6
  */
7
- export function downloadFile(file: File): void {
7
+ export function download(file: File): void {
8
8
  const url = URL.createObjectURL(file);
9
9
  const anchor = document.createElement("a");
10
10
  anchor.download = file.name;
@@ -16,22 +16,12 @@ export function downloadFile(file: File): void {
16
16
  URL.revokeObjectURL(url);
17
17
  }
18
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
19
  /**
30
20
  * Opens the specified file.
31
21
  * @param file The file to be opened.
32
22
  * @param options Value indicating whether to open the file in a new tab.
33
23
  */
34
- export function openFile(file: File, options: {newTab?: boolean} = {}): void {
24
+ export function open(file: File, options: {newTab?: boolean} = {}): void {
35
25
  const url = URL.createObjectURL(file);
36
26
  if (!options.newTab) {
37
27
  location.assign(url);
@@ -55,7 +45,7 @@ export function openFile(file: File, options: {newTab?: boolean} = {}): void {
55
45
  * Prints the specified file.
56
46
  * @param file The file to be printed.
57
47
  */
58
- export function printFile(file: File): void {
48
+ export function print(file: File): void {
59
49
  const url = URL.createObjectURL(file);
60
50
  const frame = document.createElement("iframe");
61
51
  frame.addEventListener("load", () => frame.contentWindow?.print());
@@ -1,5 +1,6 @@
1
- import type {ILoadingIndicator} from "#Abstractions/ILoadingIndicator.js";
2
- import {HttpError} from "./HttpError.js";
1
+ import {MediaType} from "../Mime/MediaType.js";
2
+ import {HttpMethod} from "./HttpMethod.js";
3
+ import {HttpRequestError} from "./HttpRequestError.js";
3
4
 
4
5
  /**
5
6
  * Performs HTTP requests.
@@ -9,7 +10,7 @@ export class HttpClient {
9
10
  /**
10
11
  * The base URL of the remote service.
11
12
  */
12
- readonly baseUrl: URL;
13
+ readonly baseAddress: URL;
13
14
 
14
15
  /**
15
16
  * The function returning the component used as loading indicator.
@@ -22,8 +23,8 @@ export class HttpClient {
22
23
  */
23
24
  constructor(options: HttpClientOptions = {}) {
24
25
  const url = options.baseUrl ? (options.baseUrl instanceof URL ? options.baseUrl.href : options.baseUrl) : document.baseURI;
25
- this.baseUrl = new URL(url.endsWith("/") ? url : `${url}/`);
26
- this.#loadingIndicator = options.loadingIndicator ?? (() => document.body.querySelector("loading-indicator") as ILoadingIndicator|null);
26
+ this.baseAddress = new URL(url.endsWith("/") ? url : `${url}/`);
27
+ this.#loadingIndicator = options.loadingIndicator ?? (() => document.body.querySelector("loading-indicator, .loading-indicator") as ILoadingIndicator|null);
27
28
  }
28
29
 
29
30
  /**
@@ -33,7 +34,7 @@ export class HttpClient {
33
34
  * @returns The server response.
34
35
  */
35
36
  delete(url?: string|URL, options?: RequestInit): Promise<Response> {
36
- return this.#fetch("DELETE", url, null, options);
37
+ return this.#fetch(HttpMethod.Delete, url, null, options);
37
38
  }
38
39
 
39
40
  /**
@@ -43,7 +44,7 @@ export class HttpClient {
43
44
  * @returns The server response.
44
45
  */
45
46
  get(url?: string|URL, options?: RequestInit): Promise<Response> {
46
- return this.#fetch("GET", url, null, options);
47
+ return this.#fetch(HttpMethod.Get, url, null, options);
47
48
  }
48
49
 
49
50
  /**
@@ -54,7 +55,7 @@ export class HttpClient {
54
55
  * @returns The server response.
55
56
  */
56
57
  patch(url?: string|URL, body?: unknown, options?: RequestInit): Promise<Response> {
57
- return this.#fetch("PATCH", url, body, options);
58
+ return this.#fetch(HttpMethod.Patch, url, body, options);
58
59
  }
59
60
 
60
61
  /**
@@ -65,7 +66,7 @@ export class HttpClient {
65
66
  * @returns The server response.
66
67
  */
67
68
  post(url?: string|URL, body?: unknown, options?: RequestInit): Promise<Response> {
68
- return this.#fetch("POST", url, body, options);
69
+ return this.#fetch(HttpMethod.Post, url, body, options);
69
70
  }
70
71
 
71
72
  /**
@@ -76,7 +77,7 @@ export class HttpClient {
76
77
  * @returns The server response.
77
78
  */
78
79
  put(url?: string|URL, body?: unknown, options?: RequestInit): Promise<Response> {
79
- return this.#fetch("PUT", url, body, options);
80
+ return this.#fetch(HttpMethod.Put, url, body, options);
80
81
  }
81
82
 
82
83
  /**
@@ -89,19 +90,19 @@ export class HttpClient {
89
90
  */
90
91
  async #fetch(method: string, url: string|URL = "", body: unknown = null, options: RequestInit = {}): Promise<Response> {
91
92
  const headers = new Headers(options.headers);
92
- if (!headers.has("accept")) headers.set("accept", "application/json");
93
+ if (!headers.has("Accept")) headers.set("Accept", MediaType.Application.Json);
93
94
 
94
95
  if (body && !(body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams)) {
95
96
  if (typeof body != "string") body = JSON.stringify(body);
96
- if (!headers.has("content-type")) headers.set("content-type", "application/json");
97
+ if (!headers.has("Content-Type")) headers.set("Content-Type", MediaType.Application.Json);
97
98
  }
98
99
 
99
100
  const loadingIndicator = this.#loadingIndicator();
100
101
  try {
101
102
  loadingIndicator?.start();
102
- const request = new Request(new URL(url, this.baseUrl), {...options, method, headers, body} as RequestInit);
103
+ const request = new Request(new URL(url, this.baseAddress), {...options, method, headers, body} as RequestInit);
103
104
  const response = await fetch(request);
104
- if (!response.ok) throw new HttpError(response);
105
+ if (!response.ok) throw new HttpRequestError(response);
105
106
  return response;
106
107
  }
107
108
  finally {
@@ -125,3 +126,20 @@ export type HttpClientOptions = Partial<{
125
126
  */
126
127
  loadingIndicator: () => ILoadingIndicator|null;
127
128
  }>;
129
+
130
+ /**
131
+ * A component that shows up when an HTTP request starts, and hides when all concurrent HTTP requests are completed.
132
+ */
133
+ export interface ILoadingIndicator {
134
+
135
+ /**
136
+ * Starts the loading indicator.
137
+ */
138
+ start: () => void;
139
+
140
+ /**
141
+ * Stops the loading indicator.
142
+ * @param options Value indicating whether to force the loading indicator to stop.
143
+ */
144
+ stop: (options?: {force?: boolean}) => void;
145
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Provides HTTP methods.
3
+ */
4
+ export const HttpMethod = Object.freeze({
5
+
6
+ /**
7
+ * The `CONNECT` HTTP method.
8
+ */
9
+ Connect: "CONNECT",
10
+
11
+ /**
12
+ * The `DELETE` HTTP method.
13
+ */
14
+ Delete: "DELETE",
15
+
16
+ /**
17
+ * The `GET` HTTP method.
18
+ */
19
+ Get: "GET",
20
+
21
+ /**
22
+ * The `HEAD` HTTP method.
23
+ */
24
+ Head: "HEAD",
25
+
26
+ /**
27
+ * The `OPTIONS` HTTP method.
28
+ */
29
+ Options: "OPTIONS",
30
+
31
+ /**
32
+ * The `PATCH` HTTP method.
33
+ */
34
+ Patch: "PATCH",
35
+
36
+ /**
37
+ * The `POST` HTTP method.
38
+ */
39
+ Post: "POST",
40
+
41
+ /**
42
+ * The `PUT` HTTP method.
43
+ */
44
+ Put: "PUT",
45
+
46
+ /**
47
+ * The `TRACE` HTTP method.
48
+ */
49
+ Trace: "TRACE"
50
+ });
51
+
52
+ /**
53
+ * Provides HTTP methods.
54
+ */
55
+ export type HttpMethod = typeof HttpMethod[keyof typeof HttpMethod];