@schukai/monster 3.95.0 → 3.95.2
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.
- package/CHANGELOG.md +16 -0
- package/package.json +1 -1
- package/source/components/datatable/dataset.mjs +327 -305
- package/source/components/datatable/datasource/dom.mjs +37 -1
- package/source/components/datatable/datasource/rest.mjs +481 -449
- package/source/components/datatable/datasource.mjs +23 -15
- package/source/components/datatable/embedded-pagination.mjs +11 -0
- package/source/components/datatable/pagination.mjs +444 -422
- package/source/components/datatable/status.mjs +1 -1
- package/source/components/datatable/style/pagination.pcss +2 -2
- package/source/components/datatable/stylesheet/pagination.mjs +7 -14
- package/source/components/datatable/util.mjs +1 -0
- package/source/components/host/config-manager.mjs +1 -3
- package/source/components/layout/tabs.mjs +35 -23
- package/source/data/datasource/server/restapi.mjs +23 -16
- package/source/data/datasource/server.mjs +1 -0
- package/source/dom/customelement.mjs +34 -0
- package/source/types/has.mjs +29 -0
@@ -12,27 +12,27 @@
|
|
12
12
|
* SPDX-License-Identifier: AGPL-3.0
|
13
13
|
*/
|
14
14
|
|
15
|
-
import {
|
16
|
-
import {
|
17
|
-
import {
|
18
|
-
import {
|
19
|
-
import {
|
20
|
-
import {
|
21
|
-
import {
|
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
|
-
|
24
|
-
|
23
|
+
assembleMethodSymbol,
|
24
|
+
registerCustomElement,
|
25
25
|
} from "../../../dom/customelement.mjs";
|
26
|
-
import {
|
27
|
-
import {
|
28
|
-
import {
|
29
|
-
import {
|
30
|
-
import {
|
31
|
-
import {
|
32
|
-
import {
|
33
|
-
import {
|
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 {
|
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
|
-
|
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
|
-
|
57
|
+
"intersectionObserverObserver",
|
58
58
|
);
|
59
59
|
|
60
60
|
/**
|
@@ -64,396 +64,427 @@ const intersectionObserverObserverSymbol = Symbol(
|
|
64
64
|
const filterObserverSymbol = Symbol("filterObserver");
|
65
65
|
|
66
66
|
/**
|
67
|
-
*
|
67
|
+
* A rest api datasource
|
68
68
|
*
|
69
|
-
*
|
69
|
+
* @fragments /fragments/components/datatable/datasource/rest
|
70
70
|
*
|
71
|
-
*
|
71
|
+
* @example /examples/components/datatable/datasource-rest-simple
|
72
|
+
* @example /examples/components/datatable/datasource-rest-auto-init
|
73
|
+
* @example /examples/components/datatable/datasource-rest-do-fetch
|
72
74
|
*
|
73
|
-
* @
|
74
|
-
* skinparam monochrome true
|
75
|
-
* skinparam shadowing false
|
76
|
-
* HTMLElement <|-- CustomElement
|
77
|
-
* CustomElement <|-- Datasource
|
78
|
-
* Datasource <|-- Rest
|
79
|
-
* @enduml
|
75
|
+
* @issue https://localhost.alvine.dev:8443/development/issues/closed/272.html
|
80
76
|
*
|
81
77
|
* @copyright schukai GmbH
|
82
|
-
* @summary A rest api datasource
|
78
|
+
* @summary A rest api datasource for the datatable or other components
|
83
79
|
*/
|
84
|
-
|
85
|
-
|
86
|
-
|
87
80
|
class Rest extends Datasource {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
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
|
+
id: undefined, // not used?
|
161
|
+
}, */
|
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
|
+
|
360
391
|
}
|
361
392
|
|
362
393
|
/**
|
363
394
|
* @private
|
364
395
|
*/
|
365
396
|
function removeFilter() {
|
366
|
-
|
367
|
-
|
397
|
+
const filterID = this.getOption("filter.id", undefined);
|
398
|
+
if (!filterID) return;
|
368
399
|
|
369
|
-
|
400
|
+
const filterControl = findElementWithIdUpwards(this, filterID);
|
370
401
|
|
371
|
-
|
372
|
-
|
373
|
-
|
402
|
+
if (filterControl && this[filterObserverSymbol]) {
|
403
|
+
filterControl?.detachObserver(this[filterObserverSymbol]);
|
404
|
+
}
|
374
405
|
}
|
375
406
|
|
376
407
|
/**
|
377
408
|
* @private
|
378
409
|
*/
|
379
410
|
function initFilter() {
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
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]);
|
457
488
|
}
|
458
489
|
|
459
490
|
/**
|
@@ -464,88 +495,89 @@ function initFilter() {
|
|
464
495
|
* @returns {Promise<never>|Promise<Awaited<unknown>>}
|
465
496
|
*/
|
466
497
|
function handleIntersectionObserver(json, response, filterControl) {
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
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
|
+
}
|
487
518
|
}
|
488
519
|
|
489
520
|
/**
|
490
521
|
* @private
|
491
522
|
*/
|
492
523
|
function initAutoInit() {
|
493
|
-
|
494
|
-
|
524
|
+
const autoInit = this.getOption("features.autoInit");
|
525
|
+
validateBoolean(autoInit);
|
495
526
|
|
496
|
-
|
527
|
+
if (autoInit !== true) return;
|
497
528
|
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
529
|
+
if (this.getOption("autoInit.intersectionObserver") === true) {
|
530
|
+
initIntersectionObserver.call(this);
|
531
|
+
return;
|
532
|
+
}
|
502
533
|
|
503
|
-
|
504
|
-
|
505
|
-
|
534
|
+
queueMicrotask(() => {
|
535
|
+
this.fetch().catch(() => {
|
536
|
+
});
|
537
|
+
});
|
506
538
|
}
|
507
539
|
|
508
540
|
/**
|
509
541
|
* @private
|
510
542
|
*/
|
511
543
|
function initEventHandler() {
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
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
|
+
};
|
529
561
|
}
|
530
562
|
|
531
563
|
/**
|
532
564
|
* @private
|
533
565
|
*/
|
534
566
|
function initIntersectionObserver() {
|
535
|
-
|
567
|
+
this.classList.add("intersection-observer");
|
536
568
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
569
|
+
const options = {
|
570
|
+
root: null,
|
571
|
+
rootMargin: "0px",
|
572
|
+
threshold: 0.1,
|
573
|
+
};
|
542
574
|
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
575
|
+
this[intersectionObserverObserverSymbol] = new IntersectionObserver(
|
576
|
+
this[intersectionObserverHandlerSymbol],
|
577
|
+
options,
|
578
|
+
);
|
547
579
|
|
548
|
-
|
580
|
+
this[intersectionObserverObserverSymbol].observe(this);
|
549
581
|
}
|
550
582
|
|
551
583
|
/**
|
@@ -553,8 +585,8 @@ function initIntersectionObserver() {
|
|
553
585
|
* @return {string}
|
554
586
|
*/
|
555
587
|
function getTemplate() {
|
556
|
-
|
557
|
-
|
588
|
+
// language=HTML
|
589
|
+
return `
|
558
590
|
<slot></slot>`;
|
559
591
|
}
|
560
592
|
|