@schukai/monster 3.95.2 → 3.96.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 (31) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +1 -1
  3. package/source/components/datatable/dataset.mjs +23 -19
  4. package/source/components/datatable/datasource/dom.mjs +4 -6
  5. package/source/components/datatable/datasource/rest.mjs +469 -471
  6. package/source/components/datatable/datasource.mjs +0 -8
  7. package/source/components/datatable/pagination.mjs +433 -439
  8. package/source/components/datatable/status.mjs +1 -3
  9. package/source/components/datatable/stylesheet/pagination.mjs +13 -6
  10. package/source/components/datatable/util.mjs +1 -1
  11. package/source/components/form/select.mjs +1 -1
  12. package/source/components/form/toggle-switch.mjs +2 -6
  13. package/source/components/layout/tabs.mjs +897 -895
  14. package/source/components/notify/message.mjs +10 -14
  15. package/source/components/notify/notify.mjs +9 -13
  16. package/source/components/notify/stylesheet/notify.mjs +13 -6
  17. package/source/components/state/log.mjs +184 -184
  18. package/source/components/state/stylesheet/log.mjs +13 -6
  19. package/source/data/datasource/server/restapi.mjs +2 -3
  20. package/source/data/transformer.mjs +803 -806
  21. package/source/dom/customelement.mjs +0 -34
  22. package/source/dom/updater.mjs +767 -767
  23. package/source/i18n/time-ago.mjs +1352 -636
  24. package/source/monster.mjs +2 -0
  25. package/source/types/has.mjs +3 -6
  26. package/source/types/version.mjs +1 -1
  27. package/test/cases/components/form/form.mjs +166 -125
  28. package/test/cases/monster.mjs +1 -1
  29. package/test/web/import.js +1 -0
  30. package/test/web/test.html +2 -2
  31. package/test/web/tests.js +2080 -1433
@@ -12,27 +12,27 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import {diff} from "../../../data/diff.mjs";
16
- import {addAttributeToken} from "../../../dom/attributes.mjs";
17
- import {ATTRIBUTE_ERRORMESSAGE} from "../../../dom/constants.mjs";
18
- import {isArray} from "../../../types/is.mjs";
19
- import {Datasource, dataSourceSymbol} from "../datasource.mjs";
20
- import {DatasourceStyleSheet} from "../stylesheet/datasource.mjs";
21
- import {instanceSymbol} from "../../../constants.mjs";
15
+ import { diff } from "../../../data/diff.mjs";
16
+ import { addAttributeToken } from "../../../dom/attributes.mjs";
17
+ import { ATTRIBUTE_ERRORMESSAGE } from "../../../dom/constants.mjs";
18
+ import { isArray } from "../../../types/is.mjs";
19
+ import { Datasource, dataSourceSymbol } from "../datasource.mjs";
20
+ import { DatasourceStyleSheet } from "../stylesheet/datasource.mjs";
21
+ import { instanceSymbol } from "../../../constants.mjs";
22
22
  import {
23
- assembleMethodSymbol,
24
- registerCustomElement,
23
+ assembleMethodSymbol,
24
+ registerCustomElement,
25
25
  } from "../../../dom/customelement.mjs";
26
- import {RestAPI} from "../../../data/datasource/server/restapi.mjs";
27
- import {Formatter} from "../../../text/formatter.mjs";
28
- import {clone} from "../../../util/clone.mjs";
29
- import {validateBoolean} from "../../../types/validate.mjs";
30
- import {findElementWithIdUpwards} from "../../../dom/util.mjs";
31
- import {Observer} from "../../../types/observer.mjs";
32
- import {Pathfinder} from "../../../data/pathfinder.mjs";
33
- import {fireCustomEvent} from "../../../dom/events.mjs";
26
+ import { RestAPI } from "../../../data/datasource/server/restapi.mjs";
27
+ import { Formatter } from "../../../text/formatter.mjs";
28
+ import { clone } from "../../../util/clone.mjs";
29
+ import { validateBoolean } from "../../../types/validate.mjs";
30
+ import { findElementWithIdUpwards } from "../../../dom/util.mjs";
31
+ import { Observer } from "../../../types/observer.mjs";
32
+ import { Pathfinder } from "../../../data/pathfinder.mjs";
33
+ import { fireCustomEvent } from "../../../dom/events.mjs";
34
34
 
35
- export {Rest};
35
+ export { Rest };
36
36
 
37
37
  /**
38
38
  * @private
@@ -46,7 +46,7 @@ const intersectionObserverHandlerSymbol = Symbol("intersectionObserverHandler");
46
46
  * @type {symbol}
47
47
  */
48
48
  const rawDataSymbol = Symbol.for(
49
- "@schukai/monster/data/datasource/server/restapi/rawdata",
49
+ "@schukai/monster/data/datasource/server/restapi/rawdata",
50
50
  );
51
51
 
52
52
  /**
@@ -54,7 +54,7 @@ const rawDataSymbol = Symbol.for(
54
54
  * @type {symbol}
55
55
  */
56
56
  const intersectionObserverObserverSymbol = Symbol(
57
- "intersectionObserverObserver",
57
+ "intersectionObserverObserver",
58
58
  );
59
59
 
60
60
  /**
@@ -78,413 +78,412 @@ const filterObserverSymbol = Symbol("filterObserver");
78
78
  * @summary A rest api datasource for the datatable or other components
79
79
  */
80
80
  class Rest extends Datasource {
81
- /**
82
- * the constructor of the class
83
- */
84
- constructor() {
85
- super();
86
- this[dataSourceSymbol] = new RestAPI();
87
- }
88
-
89
- /**
90
- * This method is called by the `instanceof` operator.
91
- * @return {symbol}
92
- */
93
- static get [instanceSymbol]() {
94
- return Symbol.for("@schukai/monster/components/datasource/rest@@instance");
95
- }
96
-
97
- /**
98
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
99
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
100
- *
101
- * The individual configuration values can be found in the table.
102
- *
103
- * @property {Object} templates Template definitions
104
- * @property {string} templates.main Main template
105
- * @property {Object} features Feature definitions
106
- * @property {boolean} features.autoInit If true, the component is initialized automatically
107
- * @property {boolean} features.filter If true, the component is initialized automatically
108
- * @property {Object} autoInit Auto init definitions
109
- * @property {boolean} autoInit.intersectionObserver If true, the intersection observer is initialized automatically
110
- * @property {boolean} autoInit.oneTime If true, the intersection observer is initialized only once
111
- * @property {Object} filter Filter definitions
112
- * @property {string} filter.id The id of the filter control
113
- * @property {Object} response Response definitions
114
- * @property {Object} response.path Path definitions (changed in 3.56.0)
115
- * @property {string} response.path.message Path to the message (changed in 3.56.0)
116
- * @property {Object} read Read configuration
117
- * @property {string} read.url The url of the rest api
118
- * @property {string} read.method The method of the rest api
119
- * @property {Object} read.parameters The parameters of the rest api
120
- * @property {Object} read.parameters.filter The filter of the rest api
121
- * @property {Object} read.parameters.orderBy The order by of the rest api
122
- * @property {Object} read.parameters.page The page of the rest api
123
- * @property {string} read.mapping.currentPage The current page
124
- * @property {Object} write Write configuration
125
- * @property {string} write.url The url of the rest api
126
- * @property {string} write.method The method of the rest api
127
- * @property {Object} write Write configuration
128
- */
129
- get defaults() {
130
- const restOptions = new RestAPI().defaults;
131
-
132
- restOptions.read.parameters = {
133
- filter: null,
134
- oderBy: null,
135
- page: "1",
136
- };
137
-
138
- restOptions.read.mapping.currentPage = "sys.pagination.currentPage";
139
-
140
- return Object.assign({}, super.defaults, restOptions, {
141
- templates: {
142
- main: getTemplate(),
143
- },
144
-
145
- features: {
146
- autoInit: false,
147
- filter: false,
148
- },
149
-
150
- autoInit: {
151
- intersectionObserver: false,
152
- oneTime: true,
153
- },
154
-
155
- filter: {
156
- id: null,
157
- },
158
-
159
- /*datatable: {
81
+ /**
82
+ * the constructor of the class
83
+ */
84
+ constructor() {
85
+ super();
86
+ this[dataSourceSymbol] = new RestAPI();
87
+ }
88
+
89
+ /**
90
+ * This method is called by the `instanceof` operator.
91
+ * @return {symbol}
92
+ */
93
+ static get [instanceSymbol]() {
94
+ return Symbol.for("@schukai/monster/components/datasource/rest@@instance");
95
+ }
96
+
97
+ /**
98
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
99
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
100
+ *
101
+ * The individual configuration values can be found in the table.
102
+ *
103
+ * @property {Object} templates Template definitions
104
+ * @property {string} templates.main Main template
105
+ * @property {Object} features Feature definitions
106
+ * @property {boolean} features.autoInit If true, the component is initialized automatically
107
+ * @property {boolean} features.filter If true, the component is initialized automatically
108
+ * @property {Object} autoInit Auto init definitions
109
+ * @property {boolean} autoInit.intersectionObserver If true, the intersection observer is initialized automatically
110
+ * @property {boolean} autoInit.oneTime If true, the intersection observer is initialized only once
111
+ * @property {Object} filter Filter definitions
112
+ * @property {string} filter.id The id of the filter control
113
+ * @property {Object} response Response definitions
114
+ * @property {Object} response.path Path definitions (changed in 3.56.0)
115
+ * @property {string} response.path.message Path to the message (changed in 3.56.0)
116
+ * @property {Object} read Read configuration
117
+ * @property {string} read.url The url of the rest api
118
+ * @property {string} read.method The method of the rest api
119
+ * @property {Object} read.parameters The parameters of the rest api
120
+ * @property {Object} read.parameters.filter The filter of the rest api
121
+ * @property {Object} read.parameters.orderBy The order by of the rest api
122
+ * @property {Object} read.parameters.page The page of the rest api
123
+ * @property {string} read.mapping.currentPage The current page
124
+ * @property {Object} write Write configuration
125
+ * @property {string} write.url The url of the rest api
126
+ * @property {string} write.method The method of the rest api
127
+ * @property {Object} write Write configuration
128
+ */
129
+ get defaults() {
130
+ const restOptions = new RestAPI().defaults;
131
+
132
+ restOptions.read.parameters = {
133
+ filter: null,
134
+ oderBy: null,
135
+ page: "1",
136
+ };
137
+
138
+ restOptions.read.mapping.currentPage = "sys.pagination.currentPage";
139
+
140
+ return Object.assign({}, super.defaults, restOptions, {
141
+ templates: {
142
+ main: getTemplate(),
143
+ },
144
+
145
+ features: {
146
+ autoInit: false,
147
+ filter: false,
148
+ },
149
+
150
+ autoInit: {
151
+ intersectionObserver: false,
152
+ oneTime: true,
153
+ },
154
+
155
+ filter: {
156
+ id: null,
157
+ },
158
+
159
+ /*datatable: {
160
160
  id: undefined, // not used?
161
161
  }, */
162
162
 
163
- response: {
164
- path: {
165
- message: "sys.message",
166
- code: "sys.code",
167
- },
168
- },
169
- });
170
- }
171
-
172
- /**
173
- * With this method, you can set the parameters for the rest api. The parameters are
174
- * used for building the url.
175
- *
176
- * @param {string} page
177
- * @param {string} query
178
- * @param {string} orderBy
179
- * @return {Rest}
180
- */
181
- setParameters({page, query, orderBy}) {
182
- const parameters = this.getOption("read.parameters");
183
- if (query !== undefined) {
184
- parameters.query = `${query}`;
185
- parameters.page = "1";
186
- }
187
-
188
- // after a query the page is set to 1, so if the page is not set, it is set to 1
189
- if (page !== undefined) parameters.page = `${page}`;
190
- if (orderBy !== undefined) parameters.order = `${orderBy}`;
191
- this.setOption("read.parameters", parameters);
192
- return this;
193
- }
194
-
195
- /**
196
- * @private
197
- * @return {void}
198
- */
199
- [assembleMethodSymbol]() {
200
- super[assembleMethodSymbol]();
201
- initEventHandler.call(this);
202
- initAutoInit.call(this);
203
- }
204
-
205
- /**
206
- * This method reloads the data from the rest api, this method is deprecated.
207
- * You should use the method `read` instead.
208
- *
209
- * @deprecated 2023-06-25
210
- * @return {Promise<never>|*}
211
- */
212
- reload() {
213
- return this.read();
214
- }
215
-
216
- /**
217
- * Fetches the data from the rest api, this method is deprecated.
218
- * You should use the method `read` instead.
219
- *
220
- * @deprecated 2024-12-24
221
- * @return {Promise<never>|*}
222
- */
223
- fetch() {
224
- return this.read();
225
- }
226
-
227
- /**
228
- *
229
- * @return {CSSStyleSheet[]}
230
- */
231
- static getCSSStyleSheet() {
232
- return [DatasourceStyleSheet];
233
- }
234
-
235
- /**
236
- * @private
237
- * @return {string}
238
- */
239
- static getTag() {
240
- return "monster-datasource-rest";
241
- }
242
-
243
- /**
244
- * This method activates the intersection observer manually.
245
- * For this purpose, the option `autoInit.intersectionObserver` must be set to `false`.
246
- *
247
- * @return {Rest}
248
- */
249
- initIntersectionObserver() {
250
- initIntersectionObserver.call(this);
251
- return this;
252
- }
253
-
254
- /**
255
- * @private
256
- */
257
- connectedCallback() {
258
- super.connectedCallback();
259
-
260
- queueMicrotask(() => {
261
- if (this.getOption("features.filter", false) === true) {
262
- initFilter.call(this);
263
- }
264
- });
265
- }
266
-
267
- /**
268
- * @private
269
- */
270
- disconnectedCallback() {
271
- super.disconnectedCallback();
272
- removeFilter.call(this);
273
- }
274
-
275
- /**
276
- * This method reads the data from the rest api.
277
- * The data is stored in the internal dataset object.
278
- *
279
- * @return {Promise}
280
- * @fires monster-datasource-fetch
281
- * @fires monster-datasource-fetched
282
- * @fires monster-datasource-error
283
- */
284
- read() {
285
- const opt = clone(this.getOption("read"));
286
- this[dataSourceSymbol].setOption("read", opt);
287
-
288
- let url = this.getOption("read.url");
289
- const formatter = new Formatter(this.getOption("read.parameters"));
290
-
291
- if (!url) {
292
- return Promise.reject(new Error("No url defined"));
293
- }
294
-
295
- url = formatter.format(url);
296
-
297
- this[dataSourceSymbol].setOption("read.url", url);
298
-
299
- return new Promise((resolve, reject) => {
300
- fireCustomEvent(this, "monster-datasource-fetch", {
301
- datasource: this,
302
- });
303
-
304
- queueMicrotask(() => {
305
- this[dataSourceSymbol]
306
- .read()
307
- .then((response) => {
308
- fireCustomEvent(this, "monster-datasource-fetched", {
309
- datasource: this,
310
- });
311
-
312
- resolve(response);
313
- })
314
- .catch((error) => {
315
- fireCustomEvent(this, "monster-datasource-error", {
316
- error: error,
317
- });
318
-
319
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
320
- reject(error);
321
- });
322
- });
323
- });
324
- }
325
-
326
- /**
327
- * Fetches the data from the rest api.
328
- * @return {Promise}
329
- */
330
- write() {
331
- const opt = clone(this.getOption("write"));
332
- this[dataSourceSymbol].setOption("write", opt);
333
-
334
- let url = this.getOption("write.url");
335
- const formatter = new Formatter(this.getOption("write.parameters"));
336
-
337
- if (!url) {
338
- return Promise.reject(new Error("No url defined"));
339
- }
340
-
341
- url = formatter.format(url);
342
-
343
- this[dataSourceSymbol].setOption("write.url", url);
344
-
345
- return new Promise((resolve, reject) => {
346
- fireCustomEvent(this, "monster-datasource-fetch", {
347
- datasource: this,
348
- });
349
-
350
- queueMicrotask(() => {
351
- this[dataSourceSymbol]
352
- .write()
353
- .then((response) => {
354
- fireCustomEvent(this, "monster-datasource-fetched", {
355
- datasource: this,
356
- });
357
-
358
- resolve(response);
359
- })
360
- .catch((error) => {
361
- fireCustomEvent(this, "monster-datasource-error", {
362
- error: error,
363
- });
364
-
365
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
366
- reject(error);
367
- });
368
- });
369
- });
370
- }
371
-
372
- // /**
373
- // * @return {int}
374
- // */
375
- // currentPage() {
376
- //
377
- // const key = this.getOption("read.mapping.currentPage")
378
- // if (key === undefined) {
379
- // return 1;
380
- // }
381
- //
382
- // const pf = new Pathfinder(this.data);
383
- // if (pf.exists(key)) {
384
- // return parseInt(pf.getVia(key), 10);
385
- // }
386
- //
387
- // return 1;
388
- //
389
- // }
390
-
163
+ response: {
164
+ path: {
165
+ message: "sys.message",
166
+ code: "sys.code",
167
+ },
168
+ },
169
+ });
170
+ }
171
+
172
+ /**
173
+ * With this method, you can set the parameters for the rest api. The parameters are
174
+ * used for building the url.
175
+ *
176
+ * @param {string} page
177
+ * @param {string} query
178
+ * @param {string} orderBy
179
+ * @return {Rest}
180
+ */
181
+ setParameters({ page, query, orderBy }) {
182
+ const parameters = this.getOption("read.parameters");
183
+ if (query !== undefined) {
184
+ parameters.query = `${query}`;
185
+ parameters.page = "1";
186
+ }
187
+
188
+ // after a query the page is set to 1, so if the page is not set, it is set to 1
189
+ if (page !== undefined) parameters.page = `${page}`;
190
+ if (orderBy !== undefined) parameters.order = `${orderBy}`;
191
+ this.setOption("read.parameters", parameters);
192
+ return this;
193
+ }
194
+
195
+ /**
196
+ * @private
197
+ * @return {void}
198
+ */
199
+ [assembleMethodSymbol]() {
200
+ super[assembleMethodSymbol]();
201
+ initEventHandler.call(this);
202
+ initAutoInit.call(this);
203
+ }
204
+
205
+ /**
206
+ * This method reloads the data from the rest api, this method is deprecated.
207
+ * You should use the method `read` instead.
208
+ *
209
+ * @deprecated 2023-06-25
210
+ * @return {Promise<never>|*}
211
+ */
212
+ reload() {
213
+ return this.read();
214
+ }
215
+
216
+ /**
217
+ * Fetches the data from the rest api, this method is deprecated.
218
+ * You should use the method `read` instead.
219
+ *
220
+ * @deprecated 2024-12-24
221
+ * @return {Promise<never>|*}
222
+ */
223
+ fetch() {
224
+ return this.read();
225
+ }
226
+
227
+ /**
228
+ *
229
+ * @return {CSSStyleSheet[]}
230
+ */
231
+ static getCSSStyleSheet() {
232
+ return [DatasourceStyleSheet];
233
+ }
234
+
235
+ /**
236
+ * @private
237
+ * @return {string}
238
+ */
239
+ static getTag() {
240
+ return "monster-datasource-rest";
241
+ }
242
+
243
+ /**
244
+ * This method activates the intersection observer manually.
245
+ * For this purpose, the option `autoInit.intersectionObserver` must be set to `false`.
246
+ *
247
+ * @return {Rest}
248
+ */
249
+ initIntersectionObserver() {
250
+ initIntersectionObserver.call(this);
251
+ return this;
252
+ }
253
+
254
+ /**
255
+ * @private
256
+ */
257
+ connectedCallback() {
258
+ super.connectedCallback();
259
+
260
+ queueMicrotask(() => {
261
+ if (this.getOption("features.filter", false) === true) {
262
+ initFilter.call(this);
263
+ }
264
+ });
265
+ }
266
+
267
+ /**
268
+ * @private
269
+ */
270
+ disconnectedCallback() {
271
+ super.disconnectedCallback();
272
+ removeFilter.call(this);
273
+ }
274
+
275
+ /**
276
+ * This method reads the data from the rest api.
277
+ * The data is stored in the internal dataset object.
278
+ *
279
+ * @return {Promise}
280
+ * @fires monster-datasource-fetch
281
+ * @fires monster-datasource-fetched
282
+ * @fires monster-datasource-error
283
+ */
284
+ read() {
285
+ const opt = clone(this.getOption("read"));
286
+ this[dataSourceSymbol].setOption("read", opt);
287
+
288
+ let url = this.getOption("read.url");
289
+ const formatter = new Formatter(this.getOption("read.parameters"));
290
+
291
+ if (!url) {
292
+ return Promise.reject(new Error("No url defined"));
293
+ }
294
+
295
+ url = formatter.format(url);
296
+
297
+ this[dataSourceSymbol].setOption("read.url", url);
298
+
299
+ return new Promise((resolve, reject) => {
300
+ fireCustomEvent(this, "monster-datasource-fetch", {
301
+ datasource: this,
302
+ });
303
+
304
+ queueMicrotask(() => {
305
+ this[dataSourceSymbol]
306
+ .read()
307
+ .then((response) => {
308
+ fireCustomEvent(this, "monster-datasource-fetched", {
309
+ datasource: this,
310
+ });
311
+
312
+ resolve(response);
313
+ })
314
+ .catch((error) => {
315
+ fireCustomEvent(this, "monster-datasource-error", {
316
+ error: error,
317
+ });
318
+
319
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
320
+ reject(error);
321
+ });
322
+ });
323
+ });
324
+ }
325
+
326
+ /**
327
+ * Fetches the data from the rest api.
328
+ * @return {Promise}
329
+ */
330
+ write() {
331
+ const opt = clone(this.getOption("write"));
332
+ this[dataSourceSymbol].setOption("write", opt);
333
+
334
+ let url = this.getOption("write.url");
335
+ const formatter = new Formatter(this.getOption("write.parameters"));
336
+
337
+ if (!url) {
338
+ return Promise.reject(new Error("No url defined"));
339
+ }
340
+
341
+ url = formatter.format(url);
342
+
343
+ this[dataSourceSymbol].setOption("write.url", url);
344
+
345
+ return new Promise((resolve, reject) => {
346
+ fireCustomEvent(this, "monster-datasource-fetch", {
347
+ datasource: this,
348
+ });
349
+
350
+ queueMicrotask(() => {
351
+ this[dataSourceSymbol]
352
+ .write()
353
+ .then((response) => {
354
+ fireCustomEvent(this, "monster-datasource-fetched", {
355
+ datasource: this,
356
+ });
357
+
358
+ resolve(response);
359
+ })
360
+ .catch((error) => {
361
+ fireCustomEvent(this, "monster-datasource-error", {
362
+ error: error,
363
+ });
364
+
365
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error.toString());
366
+ reject(error);
367
+ });
368
+ });
369
+ });
370
+ }
371
+
372
+ // /**
373
+ // * @return {int}
374
+ // */
375
+ // currentPage() {
376
+ //
377
+ // const key = this.getOption("read.mapping.currentPage")
378
+ // if (key === undefined) {
379
+ // return 1;
380
+ // }
381
+ //
382
+ // const pf = new Pathfinder(this.data);
383
+ // if (pf.exists(key)) {
384
+ // return parseInt(pf.getVia(key), 10);
385
+ // }
386
+ //
387
+ // return 1;
388
+ //
389
+ // }
391
390
  }
392
391
 
393
392
  /**
394
393
  * @private
395
394
  */
396
395
  function removeFilter() {
397
- const filterID = this.getOption("filter.id", undefined);
398
- if (!filterID) return;
396
+ const filterID = this.getOption("filter.id", undefined);
397
+ if (!filterID) return;
399
398
 
400
- const filterControl = findElementWithIdUpwards(this, filterID);
399
+ const filterControl = findElementWithIdUpwards(this, filterID);
401
400
 
402
- if (filterControl && this[filterObserverSymbol]) {
403
- filterControl?.detachObserver(this[filterObserverSymbol]);
404
- }
401
+ if (filterControl && this[filterObserverSymbol]) {
402
+ filterControl?.detachObserver(this[filterObserverSymbol]);
403
+ }
405
404
  }
406
405
 
407
406
  /**
408
407
  * @private
409
408
  */
410
409
  function initFilter() {
411
- const filterID = this.getOption("filter.id", undefined);
412
-
413
- if (!filterID)
414
- throw new Error("filter feature is enabled but no filter id is defined");
415
-
416
- const filterControl = findElementWithIdUpwards(this, filterID);
417
- if (!filterControl)
418
- throw new Error(
419
- "filter feature is enabled but no filter control with id " +
420
- filterID +
421
- " is found",
422
- );
423
-
424
- this[filterObserverSymbol] = new Observer(() => {
425
- const query = filterControl.getOption("query");
426
- if (query === undefined) {
427
- return;
428
- }
429
- this.setParameters({query: query});
430
- this.fetch()
431
- .then((response) => {
432
- if (!(response instanceof Response)) {
433
- throw new Error("Response is not an instance of Response");
434
- }
435
-
436
- if (response?.ok === true) {
437
- this.dispatchEvent(new CustomEvent("reload", {bubbles: true}));
438
- filterControl?.showSuccess();
439
- }
440
-
441
- if (response.bodyUsed === true) {
442
- return handleIntersectionObserver.call(
443
- this,
444
- response[rawDataSymbol],
445
- response,
446
- filterControl,
447
- );
448
- }
449
-
450
- response
451
- .text()
452
- .then((jsonAsText) => {
453
- let json;
454
- try {
455
- json = JSON.parse(jsonAsText);
456
- } catch (e) {
457
- const message = e instanceof Error ? e.message : `${e}`;
458
- filterControl?.showFailureMessage(message);
459
- return Promise.reject(e);
460
- }
461
-
462
- return handleIntersectionObserver.call(
463
- this,
464
- json,
465
- response,
466
- filterControl,
467
- );
468
- })
469
- .catch((e) => {
470
- filterControl?.showFailureMessage(e.message);
471
- });
472
- })
473
- .catch((e) => {
474
- this.dispatchEvent(
475
- new CustomEvent("error", {bubbles: true, detail: e}),
476
- );
477
-
478
- if (!(e instanceof Error)) {
479
- e = new Error(e);
480
- }
481
-
482
- filterControl?.showFailureMessage(e.message);
483
- return Promise.reject(e);
484
- });
485
- });
486
-
487
- filterControl.attachObserver(this[filterObserverSymbol]);
410
+ const filterID = this.getOption("filter.id", undefined);
411
+
412
+ if (!filterID)
413
+ throw new Error("filter feature is enabled but no filter id is defined");
414
+
415
+ const filterControl = findElementWithIdUpwards(this, filterID);
416
+ if (!filterControl)
417
+ throw new Error(
418
+ "filter feature is enabled but no filter control with id " +
419
+ filterID +
420
+ " is found",
421
+ );
422
+
423
+ this[filterObserverSymbol] = new Observer(() => {
424
+ const query = filterControl.getOption("query");
425
+ if (query === undefined) {
426
+ return;
427
+ }
428
+ this.setParameters({ query: query });
429
+ this.fetch()
430
+ .then((response) => {
431
+ if (!(response instanceof Response)) {
432
+ throw new Error("Response is not an instance of Response");
433
+ }
434
+
435
+ if (response?.ok === true) {
436
+ this.dispatchEvent(new CustomEvent("reload", { bubbles: true }));
437
+ filterControl?.showSuccess();
438
+ }
439
+
440
+ if (response.bodyUsed === true) {
441
+ return handleIntersectionObserver.call(
442
+ this,
443
+ response[rawDataSymbol],
444
+ response,
445
+ filterControl,
446
+ );
447
+ }
448
+
449
+ response
450
+ .text()
451
+ .then((jsonAsText) => {
452
+ let json;
453
+ try {
454
+ json = JSON.parse(jsonAsText);
455
+ } catch (e) {
456
+ const message = e instanceof Error ? e.message : `${e}`;
457
+ filterControl?.showFailureMessage(message);
458
+ return Promise.reject(e);
459
+ }
460
+
461
+ return handleIntersectionObserver.call(
462
+ this,
463
+ json,
464
+ response,
465
+ filterControl,
466
+ );
467
+ })
468
+ .catch((e) => {
469
+ filterControl?.showFailureMessage(e.message);
470
+ });
471
+ })
472
+ .catch((e) => {
473
+ this.dispatchEvent(
474
+ new CustomEvent("error", { bubbles: true, detail: e }),
475
+ );
476
+
477
+ if (!(e instanceof Error)) {
478
+ e = new Error(e);
479
+ }
480
+
481
+ filterControl?.showFailureMessage(e.message);
482
+ return Promise.reject(e);
483
+ });
484
+ });
485
+
486
+ filterControl.attachObserver(this[filterObserverSymbol]);
488
487
  }
489
488
 
490
489
  /**
@@ -495,89 +494,88 @@ function initFilter() {
495
494
  * @returns {Promise<never>|Promise<Awaited<unknown>>}
496
495
  */
497
496
  function handleIntersectionObserver(json, response, filterControl) {
498
- const path = new Pathfinder(json);
499
-
500
- const codePath = this.getOption("response.path.code");
501
-
502
- if (path.exists(codePath)) {
503
- const code = `${path.getVia(codePath)}`;
504
- if (code && code === "200") {
505
- filterControl?.showSuccess();
506
- return Promise.resolve(response);
507
- }
508
-
509
- const messagePath = this.getOption("response.path.message");
510
- if (path.exists(messagePath)) {
511
- const message = path.getVia(messagePath);
512
- filterControl?.showFailureMessage(message);
513
- return Promise.reject(new Error(message));
514
- }
515
-
516
- return Promise.reject(new Error("Response code is not 200"));
517
- }
497
+ const path = new Pathfinder(json);
498
+
499
+ const codePath = this.getOption("response.path.code");
500
+
501
+ if (path.exists(codePath)) {
502
+ const code = `${path.getVia(codePath)}`;
503
+ if (code && code === "200") {
504
+ filterControl?.showSuccess();
505
+ return Promise.resolve(response);
506
+ }
507
+
508
+ const messagePath = this.getOption("response.path.message");
509
+ if (path.exists(messagePath)) {
510
+ const message = path.getVia(messagePath);
511
+ filterControl?.showFailureMessage(message);
512
+ return Promise.reject(new Error(message));
513
+ }
514
+
515
+ return Promise.reject(new Error("Response code is not 200"));
516
+ }
518
517
  }
519
518
 
520
519
  /**
521
520
  * @private
522
521
  */
523
522
  function initAutoInit() {
524
- const autoInit = this.getOption("features.autoInit");
525
- validateBoolean(autoInit);
523
+ const autoInit = this.getOption("features.autoInit");
524
+ validateBoolean(autoInit);
526
525
 
527
- if (autoInit !== true) return;
526
+ if (autoInit !== true) return;
528
527
 
529
- if (this.getOption("autoInit.intersectionObserver") === true) {
530
- initIntersectionObserver.call(this);
531
- return;
532
- }
528
+ if (this.getOption("autoInit.intersectionObserver") === true) {
529
+ initIntersectionObserver.call(this);
530
+ return;
531
+ }
533
532
 
534
- queueMicrotask(() => {
535
- this.fetch().catch(() => {
536
- });
537
- });
533
+ queueMicrotask(() => {
534
+ this.fetch().catch(() => {});
535
+ });
538
536
  }
539
537
 
540
538
  /**
541
539
  * @private
542
540
  */
543
541
  function initEventHandler() {
544
- this[intersectionObserverHandlerSymbol] = (entries) => {
545
- entries.forEach((entry) => {
546
- if (entry.isIntersecting) {
547
- if (entry.intersectionRatio > 0) {
548
- this.fetch();
549
- }
550
-
551
- // only load once
552
- if (
553
- this.getOption("autoInit.oneTime") === true &&
554
- this[intersectionObserverObserverSymbol] !== undefined
555
- ) {
556
- this[intersectionObserverObserverSymbol].unobserve(this);
557
- }
558
- }
559
- });
560
- };
542
+ this[intersectionObserverHandlerSymbol] = (entries) => {
543
+ entries.forEach((entry) => {
544
+ if (entry.isIntersecting) {
545
+ if (entry.intersectionRatio > 0) {
546
+ this.fetch();
547
+ }
548
+
549
+ // only load once
550
+ if (
551
+ this.getOption("autoInit.oneTime") === true &&
552
+ this[intersectionObserverObserverSymbol] !== undefined
553
+ ) {
554
+ this[intersectionObserverObserverSymbol].unobserve(this);
555
+ }
556
+ }
557
+ });
558
+ };
561
559
  }
562
560
 
563
561
  /**
564
562
  * @private
565
563
  */
566
564
  function initIntersectionObserver() {
567
- this.classList.add("intersection-observer");
565
+ this.classList.add("intersection-observer");
568
566
 
569
- const options = {
570
- root: null,
571
- rootMargin: "0px",
572
- threshold: 0.1,
573
- };
567
+ const options = {
568
+ root: null,
569
+ rootMargin: "0px",
570
+ threshold: 0.1,
571
+ };
574
572
 
575
- this[intersectionObserverObserverSymbol] = new IntersectionObserver(
576
- this[intersectionObserverHandlerSymbol],
577
- options,
578
- );
573
+ this[intersectionObserverObserverSymbol] = new IntersectionObserver(
574
+ this[intersectionObserverHandlerSymbol],
575
+ options,
576
+ );
579
577
 
580
- this[intersectionObserverObserverSymbol].observe(this);
578
+ this[intersectionObserverObserverSymbol].observe(this);
581
579
  }
582
580
 
583
581
  /**
@@ -585,8 +583,8 @@ function initIntersectionObserver() {
585
583
  * @return {string}
586
584
  */
587
585
  function getTemplate() {
588
- // language=HTML
589
- return `
586
+ // language=HTML
587
+ return `
590
588
  <slot></slot>`;
591
589
  }
592
590