@schukai/monster 3.86.4 → 3.87.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.
@@ -12,48 +12,49 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import { instanceSymbol } from "../../constants.mjs";
16
- import { findTargetElementFromEvent } from "../../dom/events.mjs";
15
+ import {instanceSymbol} from "../../constants.mjs";
16
+ import {findTargetElementFromEvent} from "../../dom/events.mjs";
17
17
  import {
18
- findElementWithIdUpwards,
19
- findElementWithSelectorUpwards,
18
+ findElementWithIdUpwards,
19
+ findElementWithSelectorUpwards,
20
20
  } from "../../dom/util.mjs";
21
21
  import {
22
- assembleMethodSymbol,
23
- CustomElement,
24
- getSlottedElements,
25
- registerCustomElement,
22
+ assembleMethodSymbol,
23
+ CustomElement,
24
+ getSlottedElements,
25
+ registerCustomElement,
26
26
  } from "../../dom/customelement.mjs";
27
- import { ID } from "../../types/id.mjs";
28
- import { Settings } from "./filter/settings.mjs";
29
- import { FilterStyleSheet } from "./stylesheet/filter.mjs";
30
- import { getDocument, getWindow } from "../../dom/util.mjs";
31
- import { getGlobal } from "../../types/global.mjs";
32
- import { isInstance, isFunction, isObject, isArray } from "../../types/is.mjs";
33
- import { Host } from "../host/host.mjs";
34
- import { addAttributeToken } from "../../dom/attributes.mjs";
35
- import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
27
+ import {ID} from "../../types/id.mjs";
28
+ import {DeadMansSwitch} from "../../util/deadmansswitch.mjs";
29
+ import {Settings} from "./filter/settings.mjs";
30
+ import {FilterStyleSheet} from "./stylesheet/filter.mjs";
31
+ import {getDocument, getWindow} from "../../dom/util.mjs";
32
+ import {getGlobal} from "../../types/global.mjs";
33
+ import {isInstance, isFunction, isObject, isArray} from "../../types/is.mjs";
34
+ import {Host} from "../host/host.mjs";
35
+ import {addAttributeToken} from "../../dom/attributes.mjs";
36
+ import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
36
37
  import "../form/message-state-button.mjs";
37
- import { Formatter } from "../../text/formatter.mjs";
38
- import { generateRangeComparisonExpression } from "../../text/util.mjs";
38
+ import {Formatter} from "../../text/formatter.mjs";
39
+ import {generateRangeComparisonExpression} from "../../text/util.mjs";
39
40
 
40
41
  import {
41
- parseBracketedKeyValueHash,
42
- createBracketedKeyValueHash,
42
+ parseBracketedKeyValueHash,
43
+ createBracketedKeyValueHash,
43
44
  } from "../../text/bracketed-key-value-hash.mjs";
44
- import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
45
- import { SpaceStyleSheet } from "../stylesheet/space.mjs";
46
- import { FormStyleSheet } from "../stylesheet/form.mjs";
45
+ import {ThemeStyleSheet} from "../stylesheet/theme.mjs";
46
+ import {SpaceStyleSheet} from "../stylesheet/space.mjs";
47
+ import {FormStyleSheet} from "../stylesheet/form.mjs";
47
48
 
48
49
  import {
49
- getStoredFilterConfigKey,
50
- getFilterConfigKey,
51
- parseDateInput,
50
+ getStoredFilterConfigKey,
51
+ getFilterConfigKey,
52
+ parseDateInput,
52
53
  } from "./filter/util.mjs";
53
54
 
54
55
  import "./filter/select.mjs";
55
56
 
56
- export { Filter };
57
+ export {Filter};
57
58
 
58
59
  /**
59
60
  * @private
@@ -90,7 +91,7 @@ const filterControlElementSymbol = Symbol("filterControlElement");
90
91
  * @type {symbol}
91
92
  */
92
93
  const filterSaveActionButtonElementSymbol = Symbol(
93
- "filterSaveActionButtonElement",
94
+ "filterSaveActionButtonElement",
94
95
  );
95
96
 
96
97
  /**
@@ -111,6 +112,25 @@ const locationChangeHandlerSymbol = Symbol("locationChangeHandler");
111
112
  */
112
113
  const settingsSymbol = Symbol("settings");
113
114
 
115
+
116
+ /**
117
+ * @private
118
+ * @type {symbol}
119
+ */
120
+ const resizeObserverSymbol = Symbol("resizeObserver");
121
+
122
+ /**
123
+ * @private
124
+ * @type {symbol}
125
+ */
126
+ const sizeDataSymbol = Symbol("sizeData");
127
+
128
+ /**
129
+ * @private
130
+ * @type {symbol}
131
+ */
132
+ const debounceSizeSymbol = Symbol("debounceSize");
133
+
114
134
  /**
115
135
  * The Filter component is used to show and handle the filter values.
116
136
  *
@@ -118,199 +138,211 @@ const settingsSymbol = Symbol("settings");
118
138
  * @summary The Filter component is used to show and handle the filter values.
119
139
  */
120
140
  class Filter extends CustomElement {
121
- /**
122
- *
123
- */
124
- constructor() {
125
- super();
126
- this[settingsSymbol] = new Settings();
127
- }
128
-
129
- /**
130
- * This method is called by the `instanceof` operator.
131
- * @return {symbol}
132
- */
133
- static get [instanceSymbol]() {
134
- return Symbol.for("@schukai/monster/components/filter@@instance");
135
- }
136
-
137
- /**
138
- *
139
- * @param {string} message
140
- * @return {Filter}
141
- */
142
- showFailureMessage(message) {
143
- this[searchButtonElementSymbol].setState(
144
- "failed",
145
- this.getOption("timeouts.message", 4000),
146
- );
147
- this[searchButtonElementSymbol]
148
- .setMessage(message.toString())
149
- .showMessage(this.getOption("timeouts.message", 4000));
150
- return this;
151
- }
152
-
153
- /**
154
- *
155
- * @return {{Filter}}
156
- */
157
- resetFailureMessage() {
158
- this[searchButtonElementSymbol].hideMessage();
159
- this[searchButtonElementSymbol].removeState();
160
- return this;
161
- }
162
-
163
- /**
164
- *
165
- * @return {{Filter}}
166
- */
167
- showSuccess() {
168
- this[searchButtonElementSymbol].setState(
169
- "successful",
170
- this.getOption("timeouts.message", 4000),
171
- );
172
- return this;
173
- }
174
-
175
- /**
176
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
177
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
178
- *
179
- * The individual configuration values can be found in the table.
180
- *
181
- * @property {Object} templates Template definitions
182
- * @property {string} templates.main Main template
183
- * @property {Object} labels Label definitions
184
- * @property {Object} formatter label Template Formatter definitions
185
- * @property {Object} formatter.marker
186
- * @property {Object} formatter.marker.open
187
- * @property {Object} formatter.marker.close
188
- * @property {string} labels.search Search button label
189
- * @property {string} labels.reset Reset button label
190
- * @property {Object} queries Query definitions
191
- * @property {function} queries.wrap Wrap query
192
- * @property {function} queries.join Join queries
193
- * @property {string} defaultQuery Default query
194
- */
195
- get defaults() {
196
- return Object.assign({}, super.defaults, {
197
- templates: {
198
- main: getTemplate(),
199
- },
200
- formatter: {
201
- marker: {
202
- open: null,
203
- close: null,
204
- },
205
- },
206
- labels: {
207
- search: "Search",
208
- reset: "Reset",
209
- save: "Save",
210
- "filter-name": "Filter name",
211
- "empty-query-and-no-default": "Please select a filter",
212
- "query-not-changed": "The query has not changed",
213
- },
214
-
215
- templateMapping: {
216
- "filter-save-label": null,
217
- "filter-name-label": name,
218
- },
219
-
220
- storedConfig: {
221
- enabled: true,
222
- selector: "",
223
- },
224
-
225
- timeouts: {
226
- message: 4000,
227
- },
228
-
229
- queries: {
230
- wrap: (value, definition) => {
231
- return value;
232
- },
233
- join: (queries) => {
234
- if (queries.length === 0) {
235
- return "";
236
- }
237
- return queries.join(" AND ");
238
- },
239
- },
240
-
241
- query: undefined,
242
- defaultQuery: "",
243
- eventProcessing: true,
244
- });
245
- }
246
-
247
- /**
248
- *
249
- * @return {string}
250
- */
251
- static getTag() {
252
- return "monster-datatable-filter";
253
- }
254
-
255
- /**
256
- * @return {FilterButton}
257
- */
258
- [assembleMethodSymbol]() {
259
- this.setOption(
260
- "templateMapping.filter-save-label",
261
- this.getOption("labels.save"),
262
- );
263
- this.setOption(
264
- "templateMapping.filter-name-label",
265
- this.getOption("labels.filter-name"),
266
- );
267
-
268
- super[assembleMethodSymbol]();
269
-
270
- initControlReferences.call(this);
271
- initEventHandler.call(this);
272
-
273
- initFromConfig
274
- .call(this)
275
- .then(() => {
276
- initFilter.call(this);
277
- updateFilterTabs.call(this);
278
- })
279
- .catch((error) => {
280
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message);
281
- });
282
- }
283
-
284
- /**
285
- *
286
- */
287
- connectedCallback() {
288
- super.connectedCallback();
289
-
290
- getWindow().addEventListener(
291
- "hashchange",
292
- this[locationChangeHandlerSymbol],
293
- );
294
- }
295
-
296
- /**
297
- *
298
- */
299
- disconnectedCallback() {
300
- super.disconnectedCallback();
301
-
302
- getWindow().removeEventListener(
303
- "hashchange",
304
- this[locationChangeHandlerSymbol],
305
- );
306
- }
307
-
308
- /**
309
- * @return {Array<CSSStyleSheet>}
310
- */
311
- static getCSSStyleSheet() {
312
- return [FilterStyleSheet, FormStyleSheet, ThemeStyleSheet, SpaceStyleSheet];
313
- }
141
+ /**
142
+ *
143
+ */
144
+ constructor() {
145
+ super();
146
+ this[settingsSymbol] = new Settings();
147
+ }
148
+
149
+ /**
150
+ * This method is called by the `instanceof` operator.
151
+ * @return {symbol}
152
+ */
153
+ static get [instanceSymbol]() {
154
+ return Symbol.for("@schukai/monster/components/filter@@instance");
155
+ }
156
+
157
+ /**
158
+ *
159
+ * @param {string} message
160
+ * @return {Filter}
161
+ */
162
+ showFailureMessage(message) {
163
+ this[searchButtonElementSymbol].setState(
164
+ "failed",
165
+ this.getOption("timeouts.message", 4000),
166
+ );
167
+ this[searchButtonElementSymbol]
168
+ .setMessage(message.toString())
169
+ .showMessage(this.getOption("timeouts.message", 4000));
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ *
175
+ * @return {{Filter}}
176
+ */
177
+ resetFailureMessage() {
178
+ this[searchButtonElementSymbol].hideMessage();
179
+ this[searchButtonElementSymbol].removeState();
180
+ return this;
181
+ }
182
+
183
+ /**
184
+ *
185
+ * @return {{Filter}}
186
+ */
187
+ showSuccess() {
188
+ this[searchButtonElementSymbol].setState(
189
+ "successful",
190
+ this.getOption("timeouts.message", 4000),
191
+ );
192
+ return this;
193
+ }
194
+
195
+ /**
196
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
197
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
198
+ *
199
+ * The individual configuration values can be found in the table.
200
+ *
201
+ * @property {Object} templates Template definitions
202
+ * @property {string} templates.main Main template
203
+ * @property {Object} labels Label definitions
204
+ * @property {string} labels.search Search button label
205
+ * @property {string} labels.reset Reset button label
206
+ * @property {string} labels.save Save button label
207
+ * @property {string} labels.filter-name Filter name label
208
+ * @property {string} labels.empty-query-and-no-default Empty query and no default query label
209
+ * @property {string} labels.query-not-changed Query not changed label
210
+ * @property {Object} formatter Formatter definitions
211
+ * @property {Object} formatter.marker Marker definitions
212
+ * @property {Object} formatter.marker.open Marker open
213
+ * @property {Object} formatter.marker.close Marker close
214
+ * @property {Object} storedConfig Stored configuration
215
+ * @property {boolean} storedConfig.enabled Enabled
216
+ * @property {string} storedConfig.selector Selector
217
+ * @property {Object} timeouts Timeout definitions
218
+ * @property {number} timeouts.message Message timeout
219
+ * @property {Object} queries Query definitions
220
+ * @property {Function} queries.wrap Wrap callback
221
+ * @property {Function} queries.join Join callback
222
+ * @property {string} query Query
223
+ * @property {string} defaultQuery Default query
224
+ * @property {boolean} eventProcessing Event processing
225
+ */
226
+ get defaults() {
227
+ return Object.assign({}, super.defaults, {
228
+ templates: {
229
+ main: getTemplate(),
230
+ },
231
+ formatter: {
232
+ marker: {
233
+ open: null,
234
+ close: null,
235
+ },
236
+ },
237
+ labels: {
238
+ search: "Search",
239
+ reset: "Reset",
240
+ save: "Save",
241
+ "filter-name": "Filter name",
242
+ "empty-query-and-no-default": "Please select a filter",
243
+ "query-not-changed": "The query has not changed",
244
+ },
245
+
246
+ templateMapping: {
247
+ "filter-save-label": null,
248
+ "filter-name-label": name,
249
+ },
250
+
251
+ storedConfig: {
252
+ enabled: true,
253
+ selector: "",
254
+ },
255
+
256
+ timeouts: {
257
+ message: 4000,
258
+ },
259
+
260
+ queries: {
261
+ wrap: (value, definition) => {
262
+ return value;
263
+ },
264
+ join: (queries) => {
265
+ if (queries.length === 0) {
266
+ return "";
267
+ }
268
+ return queries.join(" AND ");
269
+ },
270
+ },
271
+
272
+ query: undefined,
273
+ defaultQuery: "",
274
+ eventProcessing: true,
275
+ });
276
+ }
277
+
278
+ /**
279
+ *
280
+ * @return {string}
281
+ */
282
+ static getTag() {
283
+ return "monster-datatable-filter";
284
+ }
285
+
286
+ /**
287
+ * @return {FilterButton}
288
+ */
289
+ [assembleMethodSymbol]() {
290
+ this.setOption(
291
+ "templateMapping.filter-save-label",
292
+ this.getOption("labels.save"),
293
+ );
294
+ this.setOption(
295
+ "templateMapping.filter-name-label",
296
+ this.getOption("labels.filter-name"),
297
+ );
298
+
299
+ super[assembleMethodSymbol]();
300
+
301
+ initControlReferences.call(this);
302
+ initEventHandler.call(this);
303
+
304
+ initFromConfig
305
+ .call(this)
306
+ .then(() => {
307
+ initFilter.call(this);
308
+ updateFilterTabs.call(this);
309
+ })
310
+ .catch((error) => {
311
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message);
312
+ });
313
+ }
314
+
315
+ /**
316
+ *
317
+ */
318
+ connectedCallback() {
319
+ super.connectedCallback();
320
+
321
+ getWindow().addEventListener(
322
+ "hashchange",
323
+ this[locationChangeHandlerSymbol],
324
+ );
325
+ }
326
+
327
+ /**
328
+ *
329
+ */
330
+ disconnectedCallback() {
331
+ super.disconnectedCallback();
332
+
333
+ getWindow().removeEventListener(
334
+ "hashchange",
335
+ this[locationChangeHandlerSymbol],
336
+ );
337
+
338
+ }
339
+
340
+ /**
341
+ * @return {Array<CSSStyleSheet>}
342
+ */
343
+ static getCSSStyleSheet() {
344
+ return [FilterStyleSheet, FormStyleSheet, ThemeStyleSheet, SpaceStyleSheet];
345
+ }
314
346
  }
315
347
 
316
348
  /**
@@ -318,48 +350,48 @@ class Filter extends CustomElement {
318
350
  * @return {FilterButton}
319
351
  */
320
352
  function initControlReferences() {
321
- if (!this.shadowRoot) {
322
- throw new Error("no shadow-root is defined");
323
- }
324
-
325
- this[filterControlElementSymbol] = this.shadowRoot.querySelector(
326
- "[data-monster-role=control]",
327
- );
328
- this[filterSelectElementSymbol] = this.shadowRoot.querySelector(
329
- "[data-monster-role=filter-select]",
330
- );
331
- this[searchButtonElementSymbol] = this.shadowRoot.querySelector(
332
- "[data-monster-role=search-button]",
333
- );
334
- this[resetButtonElementSymbol] = this.shadowRoot.querySelector(
335
- "[data-monster-role=reset-button]",
336
- );
337
-
338
- this[saveButtonElementSymbol] = this.shadowRoot.querySelector(
339
- "[data-monster-role=save-button]",
340
- );
341
-
342
- this[filterSaveActionButtonElementSymbol] = this.shadowRoot.querySelector(
343
- "[data-monster-role=save-action-button]",
344
- );
345
-
346
- this[filterTabElementSymbol] = findElementWithSelectorUpwards(
347
- this,
348
- this.getOption("storedConfig.selector", ""),
349
- );
350
-
351
- return this;
353
+ if (!this.shadowRoot) {
354
+ throw new Error("no shadow-root is defined");
355
+ }
356
+
357
+ this[filterControlElementSymbol] = this.shadowRoot.querySelector(
358
+ "[data-monster-role=control]",
359
+ );
360
+ this[filterSelectElementSymbol] = this.shadowRoot.querySelector(
361
+ "[data-monster-role=filter-select]",
362
+ );
363
+ this[searchButtonElementSymbol] = this.shadowRoot.querySelector(
364
+ "[data-monster-role=search-button]",
365
+ );
366
+ this[resetButtonElementSymbol] = this.shadowRoot.querySelector(
367
+ "[data-monster-role=reset-button]",
368
+ );
369
+
370
+ this[saveButtonElementSymbol] = this.shadowRoot.querySelector(
371
+ "[data-monster-role=save-button]",
372
+ );
373
+
374
+ this[filterSaveActionButtonElementSymbol] = this.shadowRoot.querySelector(
375
+ "[data-monster-role=save-action-button]",
376
+ );
377
+
378
+ this[filterTabElementSymbol] = findElementWithSelectorUpwards(
379
+ this,
380
+ this.getOption("storedConfig.selector", ""),
381
+ );
382
+
383
+ return this;
352
384
  }
353
385
 
354
386
  function updateFilterSelections() {
355
- queueMicrotask(() => {
356
- const options = this[settingsSymbol].getOptions();
357
- this[filterSelectElementSymbol].setOption("options", options);
358
- queueMicrotask(() => {
359
- this[filterSelectElementSymbol].value =
360
- this[settingsSymbol].getSelected();
361
- });
362
- });
387
+ queueMicrotask(() => {
388
+ const options = this[settingsSymbol].getOptions();
389
+ this[filterSelectElementSymbol].setOption("options", options);
390
+ queueMicrotask(() => {
391
+ this[filterSelectElementSymbol].value =
392
+ this[settingsSymbol].getSelected();
393
+ });
394
+ });
363
395
  }
364
396
 
365
397
  /**
@@ -367,58 +399,59 @@ function updateFilterSelections() {
367
399
  * @throws {Error} no filter label is defined
368
400
  */
369
401
  function initFilter() {
370
- const storedSetting = this[settingsSymbol];
371
- this[settingsSymbol] = new Settings();
372
-
373
- const result = parseBracketedKeyValueHash(getGlobal().location.hash);
374
- let valuesFromHash = {};
375
- if (isObject(result) && result?.[this.id]) {
376
- valuesFromHash = result[this.id];
377
- }
378
-
379
- getSlottedElements
380
- .call(this, "label[data-monster-label]")
381
- .forEach((element) => {
382
- const label = element.getAttribute("data-monster-label");
383
- if (!label) {
384
- throw new Error("no filter label is defined");
385
- }
386
-
387
- let value = element.id;
388
- if (!value) {
389
- const prefix = label.replace(/\W/g, "-");
390
- prefix.charAt(0).match(/[\d_]/g)?.length ? `f${prefix}` : prefix;
391
-
392
- value = new ID(prefix + "-").toString();
393
- element.id = value;
394
- }
395
-
396
- let setting = storedSetting.get(value);
397
-
398
- if (setting) {
399
- this[settingsSymbol].set(setting);
400
- }
401
-
402
- if (valuesFromHash?.[element.id]) {
403
- const v = escapeAttributeValue(valuesFromHash[element.id]);
404
- const searchInput = element.firstElementChild;
405
- try {
406
- searchInput.value = v;
407
- } catch (error) {}
408
- }
409
-
410
- setting = this[settingsSymbol].get(value);
411
- let visible = false;
412
- if (setting) {
413
- setSlotAttribute(element, setting.visible);
414
- visible = setting.visible;
415
- } else {
416
- visible = getVisibilityFromSlotAttribute(element);
417
- }
418
-
419
- this[settingsSymbol].set({ value, label, visible });
420
- });
421
- updateFilterSelections.call(this);
402
+ const storedSetting = this[settingsSymbol];
403
+ this[settingsSymbol] = new Settings();
404
+
405
+ const result = parseBracketedKeyValueHash(getGlobal().location.hash);
406
+ let valuesFromHash = {};
407
+ if (isObject(result) && result?.[this.id]) {
408
+ valuesFromHash = result[this.id];
409
+ }
410
+
411
+ getSlottedElements
412
+ .call(this, "label[data-monster-label]")
413
+ .forEach((element) => {
414
+ const label = element.getAttribute("data-monster-label");
415
+ if (!label) {
416
+ throw new Error("no filter label is defined");
417
+ }
418
+
419
+ let value = element.id;
420
+ if (!value) {
421
+ const prefix = label.replace(/\W/g, "-");
422
+ prefix.charAt(0).match(/[\d_]/g)?.length ? `f${prefix}` : prefix;
423
+
424
+ value = new ID(prefix + "-").toString();
425
+ element.id = value;
426
+ }
427
+
428
+ let setting = storedSetting.get(value);
429
+
430
+ if (setting) {
431
+ this[settingsSymbol].set(setting);
432
+ }
433
+
434
+ if (valuesFromHash?.[element.id]) {
435
+ const v = escapeAttributeValue(valuesFromHash[element.id]);
436
+ const searchInput = element.firstElementChild;
437
+ try {
438
+ searchInput.value = v;
439
+ } catch (error) {
440
+ }
441
+ }
442
+
443
+ setting = this[settingsSymbol].get(value);
444
+ let visible = false;
445
+ if (setting) {
446
+ setSlotAttribute(element, setting.visible);
447
+ visible = setting.visible;
448
+ } else {
449
+ visible = getVisibilityFromSlotAttribute(element);
450
+ }
451
+
452
+ this[settingsSymbol].set({value, label, visible});
453
+ });
454
+ updateFilterSelections.call(this);
422
455
  }
423
456
 
424
457
  /**
@@ -427,16 +460,16 @@ function initFilter() {
427
460
  * @return {*}
428
461
  */
429
462
  function escapeAttributeValue(input) {
430
- if (input === undefined || input === null) {
431
- return input;
432
- }
433
-
434
- return input
435
- .replace(/&/g, "&amp;")
436
- .replace(/"/g, "&quot;")
437
- .replace(/'/g, "&#x27;")
438
- .replace(/</g, "&lt;")
439
- .replace(/>/g, "&gt;");
463
+ if (input === undefined || input === null) {
464
+ return input;
465
+ }
466
+
467
+ return input
468
+ .replace(/&/g, "&amp;")
469
+ .replace(/"/g, "&quot;")
470
+ .replace(/'/g, "&#x27;")
471
+ .replace(/</g, "&lt;")
472
+ .replace(/>/g, "&gt;");
440
473
  }
441
474
 
442
475
  /**
@@ -445,9 +478,9 @@ function escapeAttributeValue(input) {
445
478
  * @return {boolean}
446
479
  */
447
480
  function getVisibilityFromSlotAttribute(element) {
448
- return !(
449
- element.hasAttribute("slot") && element.getAttribute("slot") === "hidden"
450
- );
481
+ return !(
482
+ element.hasAttribute("slot") && element.getAttribute("slot") === "hidden"
483
+ );
451
484
  }
452
485
 
453
486
  /**
@@ -456,343 +489,338 @@ function getVisibilityFromSlotAttribute(element) {
456
489
  * @param {boolean} visible
457
490
  */
458
491
  function setSlotAttribute(element, visible) {
459
- if (visible) {
460
- element.removeAttribute("slot");
461
- return;
462
- }
492
+ if (visible) {
493
+ element.removeAttribute("slot");
494
+ return;
495
+ }
463
496
 
464
- element.setAttribute("slot", "hidden");
497
+ element.setAttribute("slot", "hidden");
465
498
  }
466
499
 
467
500
  /**
468
501
  * @private
469
502
  */
470
503
  function initEventHandler() {
471
- const self = this;
472
- /**
473
- * Monster.Components.Form.event:monster-selection-cleared
474
- */
475
- if (self[filterSelectElementSymbol]) {
476
- self[filterSelectElementSymbol].addEventListener(
477
- "monster-selection-cleared",
478
- function () {
479
- const settings = self[settingsSymbol].getOptions();
480
-
481
- for (const setting of settings) {
482
- const filterElement = findElementWithIdUpwards(self, setting.value);
483
- if (filterElement) {
484
- setSlotAttribute(filterElement, false);
485
-
486
- self[settingsSymbol].set({ value: setting.value, visible: false });
487
- }
488
- }
489
-
490
- updateConfig.call(self);
491
- },
492
- );
493
-
494
- self[filterSelectElementSymbol].addEventListener(
495
- "monster-changed",
496
- function (event) {
497
- const filterElement = findElementWithIdUpwards(
498
- self,
499
- event.detail.value,
500
- );
501
- if (filterElement) {
502
- setSlotAttribute(filterElement, event.detail.checked);
503
- }
504
-
505
- self[settingsSymbol].set({
506
- value: event.detail.value,
507
- visible: event.detail.checked,
508
- });
509
-
510
- updateConfig.call(self);
511
- },
512
- );
513
- }
514
-
515
- if (self[filterSaveActionButtonElementSymbol]) {
516
- self[filterSaveActionButtonElementSymbol].setOption(
517
- "actions.click",
518
- function (event) {
519
- const button = findTargetElementFromEvent(
520
- event,
521
- "data-monster-role",
522
- "save-action-button",
523
- );
524
- const form = button.closest("[data-monster-role=form]");
525
-
526
- if (!form) {
527
- button.setState("failed", self.getOption("timeouts.message", 4000));
528
- return;
529
- }
530
-
531
- const input = form.querySelector("input[name=filter-name]");
532
- if (!input) {
533
- button.setState("failed", self.getOption("timeouts.message", 4000));
534
- return;
535
- }
536
-
537
- const name = input.value;
538
- if (!name) {
539
- button.setState("failed", self.getOption("timeouts.message", 4000));
540
- button.setMessage("Please enter a name").showMessage();
541
- return;
542
- }
543
-
544
- doSearch
545
- .call(self, { showEffect: false })
546
- .then(() => {
547
- const configKey = getStoredFilterConfigKey.call(self);
548
- const host = getDocument().querySelector("monster-host");
549
- if (!host) {
550
- return;
551
- }
552
-
553
- const query = self.getOption("query");
554
- if (!query) {
555
- button.setState(
556
- "failed",
557
- self.getOption(
558
- "timeouts.message",
559
- self.getOption("timeouts.message", 4000),
560
- ),
561
- );
562
- button
563
- .setMessage("No query found")
564
- .showMessage(self.getOption("timeouts.message", 4000));
565
- return;
566
- }
567
-
568
- host
569
- .hasConfig(configKey)
570
- .then((hasConfig) => {
571
- return new Promise((resolve, reject) => {
572
- if (hasConfig) {
573
- host.getConfig(configKey).then(resolve).catch(reject);
574
- return;
575
- }
576
- return resolve({});
577
- });
578
- })
579
- .then((config) => {
580
- config[name] = query;
581
- return host.setConfig(configKey, {
582
- ...config,
583
- });
584
- })
585
- .then(() => {
586
- button.setState(
587
- "successful",
588
- self.getOption("timeouts.message", 4000),
589
- );
590
- updateFilterTabs.call(self);
591
- })
592
- .catch((error) => {
593
- button.setState(
594
- "failed",
595
- self.getOption("timeouts.message", 4000),
596
- );
597
- button
598
- .setMessage(error.message)
599
- .showMessage(self.getOption("timeouts.message", 4000));
600
- });
601
- })
602
- .catch((error) => {
603
- button.setState("failed", self.getOption("timeouts.message", 4000));
604
- const msg = error.message || error;
605
- button
606
- .setMessage(msg)
607
- .showMessage(self.getOption("timeouts.message", 4000));
608
- });
609
- },
610
- );
611
- }
612
-
613
- self[searchButtonElementSymbol].setOption("actions.click", () => {
614
- doSearch
615
- .call(self)
616
- .then(() => {})
617
- .catch((error) => {});
618
- });
619
-
620
- // the reset button should reset the filter and the search query
621
- // all input elements should be reset to their default values
622
- // which is the empty string. we search for all input elements
623
- // in the filter and reset them to their default value
624
- self[resetButtonElementSymbol].setOption("actions.click", () => {
625
- getSlottedElements
626
- .call(self, "label[data-monster-label]")
627
- .forEach((element) => {
628
- const label = element.getAttribute("data-monster-label");
629
- if (!label) {
630
- return;
631
- }
632
-
633
- const input = element.firstElementChild;
634
-
635
- if (input) {
636
- input.value = "";
637
- }
638
- });
639
-
640
- doSearch
641
- .call(self, { showEffect: false })
642
- .then(() => {})
643
- .catch((e) => addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.message));
644
- });
645
-
646
- self[locationChangeHandlerSymbol] = (event) => {
647
- // if (event instanceof HashChangeEvent) {
648
- // if (event.oldURL === event.newURL) {
649
- // return;
650
- // }
651
- // }
652
- };
653
-
654
- self.addEventListener("keyup", (event) => {
655
- const path = event.composedPath();
656
- if (path.length === 0) {
657
- return;
658
- }
659
-
660
- if (!(path[0] instanceof HTMLInputElement)) {
661
- return;
662
- }
663
-
664
- if (event.keyCode === 13) {
665
- doSearch.call(self, { showEffect: false });
666
- }
667
- });
668
-
669
- // tabs
670
- const element = this[filterTabElementSymbol];
671
- if (element) {
672
- initTabEvents.call(this);
673
- }
504
+ const self = this;
505
+ /**
506
+ * Monster.Components.Form.event:monster-selection-cleared
507
+ */
508
+ if (self[filterSelectElementSymbol]) {
509
+ self[filterSelectElementSymbol].addEventListener(
510
+ "monster-selection-cleared",
511
+ function () {
512
+ const settings = self[settingsSymbol].getOptions();
513
+
514
+ for (const setting of settings) {
515
+ const filterElement = findElementWithIdUpwards(self, setting.value);
516
+ if (filterElement) {
517
+ setSlotAttribute(filterElement, false);
518
+
519
+ self[settingsSymbol].set({value: setting.value, visible: false});
520
+ }
521
+ }
522
+
523
+ updateConfig.call(self);
524
+ },
525
+ );
526
+
527
+ self[filterSelectElementSymbol].addEventListener(
528
+ "monster-changed",
529
+ function (event) {
530
+ const filterElement = findElementWithIdUpwards(
531
+ self,
532
+ event.detail.value,
533
+ );
534
+ if (filterElement) {
535
+ setSlotAttribute(filterElement, event.detail.checked);
536
+ }
537
+
538
+ self[settingsSymbol].set({
539
+ value: event.detail.value,
540
+ visible: event.detail.checked,
541
+ });
542
+
543
+ updateConfig.call(self);
544
+ },
545
+ );
546
+ }
547
+
548
+ if (self[filterSaveActionButtonElementSymbol]) {
549
+ self[filterSaveActionButtonElementSymbol].setOption(
550
+ "actions.click",
551
+ function (event) {
552
+ const button = findTargetElementFromEvent(
553
+ event,
554
+ "data-monster-role",
555
+ "save-action-button",
556
+ );
557
+ const form = button.closest("[data-monster-role=form]");
558
+
559
+ if (!form) {
560
+ button.setState("failed", self.getOption("timeouts.message", 4000));
561
+ return;
562
+ }
563
+
564
+ const input = form.querySelector("input[name=filter-name]");
565
+ if (!input) {
566
+ button.setState("failed", self.getOption("timeouts.message", 4000));
567
+ return;
568
+ }
569
+
570
+ const name = input.value;
571
+ if (!name) {
572
+ button.setState("failed", self.getOption("timeouts.message", 4000));
573
+ button.setMessage("Please enter a name").showMessage();
574
+ return;
575
+ }
576
+
577
+ doSearch
578
+ .call(self, {showEffect: false})
579
+ .then(() => {
580
+ const configKey = getStoredFilterConfigKey.call(self);
581
+ const host = getDocument().querySelector("monster-host");
582
+ if (!host) {
583
+ return;
584
+ }
585
+
586
+ const query = self.getOption("query");
587
+ if (!query) {
588
+ button.setState(
589
+ "failed",
590
+ self.getOption(
591
+ "timeouts.message",
592
+ self.getOption("timeouts.message", 4000),
593
+ ),
594
+ );
595
+ button
596
+ .setMessage("No query found")
597
+ .showMessage(self.getOption("timeouts.message", 4000));
598
+ return;
599
+ }
600
+
601
+ host
602
+ .hasConfig(configKey)
603
+ .then((hasConfig) => {
604
+ return new Promise((resolve, reject) => {
605
+ if (hasConfig) {
606
+ host.getConfig(configKey).then(resolve).catch(reject);
607
+ return;
608
+ }
609
+ return resolve({});
610
+ });
611
+ })
612
+ .then((config) => {
613
+ config[name] = query;
614
+ return host.setConfig(configKey, {
615
+ ...config,
616
+ });
617
+ })
618
+ .then(() => {
619
+ button.setState(
620
+ "successful",
621
+ self.getOption("timeouts.message", 4000),
622
+ );
623
+ updateFilterTabs.call(self);
624
+ })
625
+ .catch((error) => {
626
+ button.setState(
627
+ "failed",
628
+ self.getOption("timeouts.message", 4000),
629
+ );
630
+ button
631
+ .setMessage(error.message)
632
+ .showMessage(self.getOption("timeouts.message", 4000));
633
+ });
634
+ })
635
+ .catch((error) => {
636
+ button.setState("failed", self.getOption("timeouts.message", 4000));
637
+ const msg = error.message || error;
638
+ button
639
+ .setMessage(msg)
640
+ .showMessage(self.getOption("timeouts.message", 4000));
641
+ });
642
+ },
643
+ );
644
+ }
645
+
646
+ self[searchButtonElementSymbol].setOption("actions.click", () => {
647
+ doSearch
648
+ .call(self)
649
+ .then(() => {
650
+ })
651
+ .catch((error) => {
652
+ });
653
+ });
654
+
655
+ // the reset button should reset the filter and the search query
656
+ // all input elements should be reset to their default values
657
+ // which is the empty string. we search for all input elements
658
+ // in the filter and reset them to their default value
659
+ self[resetButtonElementSymbol].setOption("actions.click", () => {
660
+ getSlottedElements
661
+ .call(self, "label[data-monster-label]")
662
+ .forEach((element) => {
663
+ const label = element.getAttribute("data-monster-label");
664
+ if (!label) {
665
+ return;
666
+ }
667
+
668
+ const input = element.firstElementChild;
669
+
670
+ if (input) {
671
+ input.value = "";
672
+ }
673
+ });
674
+
675
+ doSearch
676
+ .call(self, {showEffect: false})
677
+ .then(() => {
678
+ })
679
+ .catch((e) => addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.message));
680
+ });
681
+
682
+ self.addEventListener("keyup", (event) => {
683
+ const path = event.composedPath();
684
+ if (path.length === 0) {
685
+ return;
686
+ }
687
+
688
+ if (!(path[0] instanceof HTMLInputElement)) {
689
+ return;
690
+ }
691
+
692
+ if (event.keyCode === 13) {
693
+ doSearch.call(self, {showEffect: false});
694
+ }
695
+ });
696
+
697
+ // tabs
698
+ const element = this[filterTabElementSymbol];
699
+ if (element) {
700
+ initTabEvents.call(this);
701
+ }
674
702
  }
675
703
 
676
704
  function initTabEvents() {
677
- this[filterTabElementSymbol].addEventListener(
678
- "monster-tab-changed",
679
- (event) => {
680
- const query = event?.detail?.data?.["data-monster-query"];
681
- const q = this.getOption("query");
682
- if (query !== q) {
683
- this.setOption("query", query);
684
- }
685
- },
686
- );
687
-
688
- this[filterTabElementSymbol].addEventListener(
689
- "monster-tab-remove",
690
- (event) => {
691
- const labels = [];
692
- const buttons = this[filterTabElementSymbol].getOption("buttons");
693
-
694
- const keys = ["popper", "standard"];
695
- for (let i = 0; i < keys.length; i++) {
696
- const key = keys[i];
697
-
698
- for (const button of buttons[key]) {
699
- if (button.label !== event.detail.label) {
700
- labels.push(button.label);
701
- }
702
- }
703
- }
704
-
705
- const host = findElementWithSelectorUpwards(this, "monster-host");
706
- if (!(host && this.id)) {
707
- return;
708
- }
709
-
710
- const configKey = getStoredFilterConfigKey.call(this);
711
- host
712
- .hasConfig(configKey)
713
- .then((hasConfig) => {
714
- if (!hasConfig) {
715
- return;
716
- }
717
-
718
- return host.getConfig(configKey);
719
- })
720
- .then((config) => {
721
- for (const [name, query] of Object.entries(config)) {
722
- if (labels.includes(name)) {
723
- continue;
724
- }
725
-
726
- delete config[name];
727
- }
728
-
729
- return host.setConfig(configKey, {
730
- ...config,
731
- });
732
- });
733
- },
734
- );
705
+ this[filterTabElementSymbol].addEventListener(
706
+ "monster-tab-changed",
707
+ (event) => {
708
+ const query = event?.detail?.data?.["data-monster-query"];
709
+ const q = this.getOption("query");
710
+ if (query !== q) {
711
+ this.setOption("query", query);
712
+ }
713
+ },
714
+ );
715
+
716
+ this[filterTabElementSymbol].addEventListener(
717
+ "monster-tab-remove",
718
+ (event) => {
719
+ const labels = [];
720
+ const buttons = this[filterTabElementSymbol].getOption("buttons");
721
+
722
+ const keys = ["popper", "standard"];
723
+ for (let i = 0; i < keys.length; i++) {
724
+ const key = keys[i];
725
+
726
+ for (const button of buttons[key]) {
727
+ if (button.label !== event.detail.label) {
728
+ labels.push(button.label);
729
+ }
730
+ }
731
+ }
732
+
733
+ const host = findElementWithSelectorUpwards(this, "monster-host");
734
+ if (!(host && this.id)) {
735
+ return;
736
+ }
737
+
738
+ const configKey = getStoredFilterConfigKey.call(this);
739
+ host
740
+ .hasConfig(configKey)
741
+ .then((hasConfig) => {
742
+ if (!hasConfig) {
743
+ return;
744
+ }
745
+
746
+ return host.getConfig(configKey);
747
+ })
748
+ .then((config) => {
749
+ for (const [name, query] of Object.entries(config)) {
750
+ if (labels.includes(name)) {
751
+ continue;
752
+ }
753
+
754
+ delete config[name];
755
+ }
756
+
757
+ return host.setConfig(configKey, {
758
+ ...config,
759
+ });
760
+ });
761
+ },
762
+ );
735
763
  }
736
764
 
737
765
  /**
738
766
  * @private
739
767
  */
740
768
  function updateFilterTabs() {
741
- const element = this[filterTabElementSymbol];
742
- if (!element) {
743
- return;
744
- }
745
-
746
- const host = findElementWithSelectorUpwards(this, "monster-host");
747
- if (!(host && this.id)) {
748
- return;
749
- }
750
-
751
- const configKey = getStoredFilterConfigKey.call(this);
752
- host
753
- .hasConfig(configKey)
754
- .then((hasConfig) => {
755
- if (!hasConfig) {
756
- return;
757
- }
758
-
759
- return host.getConfig(configKey);
760
- })
761
- .then((config) => {
762
- for (const [name, query] of Object.entries(config)) {
763
- const found = element.querySelector(
764
- `[data-monster-button-label="${name}"]`,
765
- );
766
- if (found) {
767
- continue;
768
- }
769
-
770
- if (query === undefined || query === null) {
771
- continue;
772
- }
773
-
774
- const escapedQuery = escapeAttributeValue(query);
775
-
776
- element.insertAdjacentHTML(
777
- "beforeend",
778
- `<div data-monster-button-label="${name}"
769
+ const element = this[filterTabElementSymbol];
770
+ if (!element) {
771
+ return;
772
+ }
773
+
774
+ const host = findElementWithSelectorUpwards(this, "monster-host");
775
+ if (!(host && this.id)) {
776
+ return;
777
+ }
778
+
779
+ const configKey = getStoredFilterConfigKey.call(this);
780
+ host
781
+ .hasConfig(configKey)
782
+ .then((hasConfig) => {
783
+ if (!hasConfig) {
784
+ return;
785
+ }
786
+
787
+ return host.getConfig(configKey);
788
+ })
789
+ .then((config) => {
790
+ for (const [name, query] of Object.entries(config)) {
791
+ const found = element.querySelector(
792
+ `[data-monster-button-label="${name}"]`,
793
+ );
794
+ if (found) {
795
+ continue;
796
+ }
797
+
798
+ if (query === undefined || query === null) {
799
+ continue;
800
+ }
801
+
802
+ const escapedQuery = escapeAttributeValue(query);
803
+
804
+ element.insertAdjacentHTML(
805
+ "beforeend",
806
+ `<div data-monster-button-label="${name}"
779
807
  data-monster-removable="true"
780
808
  data-monster-query="${escapedQuery}" data-monster-role="filter-tab" >
781
809
  </div>`,
782
- );
783
- }
784
- })
785
- .catch((error) => {
786
- if (error instanceof Error) {
787
- addAttributeToken(
788
- this,
789
- ATTRIBUTE_ERRORMESSAGE,
790
- error.message + " " + error.stack,
791
- );
792
- } else {
793
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error + "");
794
- }
795
- });
810
+ );
811
+ }
812
+ })
813
+ .catch((error) => {
814
+ if (error instanceof Error) {
815
+ addAttributeToken(
816
+ this,
817
+ ATTRIBUTE_ERRORMESSAGE,
818
+ error.message + " " + error.stack,
819
+ );
820
+ } else {
821
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error + "");
822
+ }
823
+ });
796
824
  }
797
825
 
798
826
  /**
@@ -800,76 +828,76 @@ function updateFilterTabs() {
800
828
  * @param showEffect
801
829
  * @return {Promise<*>}
802
830
  */
803
- function doSearch({ showEffect } = { showEffect: true }) {
804
- this.resetFailureMessage();
805
-
806
- if (showEffect) {
807
- this[searchButtonElementSymbol].setState(
808
- "activity",
809
- this.getOption("timeouts.message", 4000),
810
- );
811
- }
812
-
813
- return collectSearchQueries
814
- .call(this)
815
- .then((query) => {
816
- const buildQuery = buildSearchQuery.call(this, query);
817
- if (buildQuery === "" && !this.getOption("defaultQuery")) {
818
- const msg = this.getOption("labels.empty-query-and-no-default");
819
-
820
- if (showEffect) {
821
- this[searchButtonElementSymbol].removeState();
822
- this[searchButtonElementSymbol]
823
- .setMessage(msg)
824
- .showMessage(this.getOption("timeouts.message", 4000));
825
- }
826
-
827
- throw new Error(msg);
828
- }
829
-
830
- if (buildQuery === this.getOption("query")) {
831
- const msg = this.getOption("labels.query-not-changed");
832
-
833
- if (showEffect) {
834
- this[searchButtonElementSymbol].removeState();
835
- this[searchButtonElementSymbol]
836
- .setMessage(msg)
837
- .showMessage(this.getOption("timeouts.message", 4000));
838
- }
839
-
840
- throw new Error(msg);
841
- }
842
-
843
- if (showEffect) {
844
- this[searchButtonElementSymbol].setState(
845
- "activity",
846
- this.getOption("timeouts.message", 4000),
847
- );
848
- }
849
-
850
- this.setOption("query", buildSearchQuery.call(this, query));
851
- })
852
- .catch((error) => {
853
- if (error instanceof Error) {
854
- addAttributeToken(
855
- this,
856
- ATTRIBUTE_ERRORMESSAGE,
857
- error.message + " " + error.stack,
858
- );
859
- } else {
860
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
861
- }
862
-
863
- if (showEffect) {
864
- this[searchButtonElementSymbol].setState(
865
- "failed",
866
- this.getOption("timeouts.message", 4000),
867
- );
868
- this[searchButtonElementSymbol].setMessage(error.message).showMessage();
869
- }
870
-
871
- return Promise.reject(error);
872
- });
831
+ function doSearch({showEffect} = {showEffect: true}) {
832
+ this.resetFailureMessage();
833
+
834
+ if (showEffect) {
835
+ this[searchButtonElementSymbol].setState(
836
+ "activity",
837
+ this.getOption("timeouts.message", 4000),
838
+ );
839
+ }
840
+
841
+ return collectSearchQueries
842
+ .call(this)
843
+ .then((query) => {
844
+ const buildQuery = buildSearchQuery.call(this, query);
845
+ if (buildQuery === "" && !this.getOption("defaultQuery")) {
846
+ const msg = this.getOption("labels.empty-query-and-no-default");
847
+
848
+ if (showEffect) {
849
+ this[searchButtonElementSymbol].removeState();
850
+ this[searchButtonElementSymbol]
851
+ .setMessage(msg)
852
+ .showMessage(this.getOption("timeouts.message", 4000));
853
+ }
854
+
855
+ throw new Error(msg);
856
+ }
857
+
858
+ if (buildQuery === this.getOption("query")) {
859
+ const msg = this.getOption("labels.query-not-changed");
860
+
861
+ if (showEffect) {
862
+ this[searchButtonElementSymbol].removeState();
863
+ this[searchButtonElementSymbol]
864
+ .setMessage(msg)
865
+ .showMessage(this.getOption("timeouts.message", 4000));
866
+ }
867
+
868
+ throw new Error(msg);
869
+ }
870
+
871
+ if (showEffect) {
872
+ this[searchButtonElementSymbol].setState(
873
+ "activity",
874
+ this.getOption("timeouts.message", 4000),
875
+ );
876
+ }
877
+
878
+ this.setOption("query", buildSearchQuery.call(this, query));
879
+ })
880
+ .catch((error) => {
881
+ if (error instanceof Error) {
882
+ addAttributeToken(
883
+ this,
884
+ ATTRIBUTE_ERRORMESSAGE,
885
+ error.message + " " + error.stack,
886
+ );
887
+ } else {
888
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
889
+ }
890
+
891
+ if (showEffect) {
892
+ this[searchButtonElementSymbol].setState(
893
+ "failed",
894
+ this.getOption("timeouts.message", 4000),
895
+ );
896
+ this[searchButtonElementSymbol].setMessage(error.message).showMessage();
897
+ }
898
+
899
+ return Promise.reject(error);
900
+ });
873
901
  }
874
902
 
875
903
  /**
@@ -878,21 +906,21 @@ function doSearch({ showEffect } = { showEffect: true }) {
878
906
  * @return {*|string}
879
907
  */
880
908
  function buildSearchQuery(queries) {
881
- if (!isArray(queries) || queries.length === 0) {
882
- return this.getOption("defaultQuery");
883
- }
909
+ if (!isArray(queries) || queries.length === 0) {
910
+ return this.getOption("defaultQuery");
911
+ }
884
912
 
885
- const joinCallback = this.getOption("queries.join");
886
- if (isFunction(joinCallback)) {
887
- return joinCallback(queries);
888
- }
913
+ const joinCallback = this.getOption("queries.join");
914
+ if (isFunction(joinCallback)) {
915
+ return joinCallback(queries);
916
+ }
889
917
 
890
- const q = queries.join(" ").trim();
891
- if (q.length === 0) {
892
- return this.getOption("defaultQuery");
893
- }
918
+ const q = queries.join(" ").trim();
919
+ if (q.length === 0) {
920
+ return this.getOption("defaultQuery");
921
+ }
894
922
 
895
- return q;
923
+ return q;
896
924
  }
897
925
 
898
926
  /**
@@ -900,108 +928,108 @@ function buildSearchQuery(queries) {
900
928
  * @return {Promise<unknown>}
901
929
  */
902
930
  function collectSearchQueries() {
903
- const currentHash = parseBracketedKeyValueHash(getGlobal().location.hash);
904
- const self = this;
905
-
906
- return new Promise((resolve, reject) => {
907
- const query = [];
908
- const wrapCallback = this.getOption("queries.wrap");
909
-
910
- let hasNoIdError = false;
911
-
912
- getSlottedElements
913
- .call(this, "label[data-monster-label]")
914
- .forEach((element) => {
915
- const label = element.getAttribute("data-monster-label");
916
- if (!label) {
917
- throw new Error("no filter label is defined");
918
- }
919
-
920
- const id = element.id;
921
- if (!id) {
922
- hasNoIdError = true;
923
- return;
924
- }
925
-
926
- //const visible = window.getComputedStyle(element).display !== "none";
927
- const visible = getVisibilityFromSlotAttribute(element);
928
- if (!visible) {
929
- return;
930
- }
931
-
932
- let template = element.getAttribute("data-monster-template");
933
- if (!template) {
934
- template = "${id}=${value}";
935
- }
936
-
937
- const controlValue = getControlValuesFromLabel(element);
938
- if (!controlValue) {
939
- if (controlValue === "" && currentHash?.[this.id]?.[id]) {
940
- delete currentHash[this.id][id];
941
- }
942
-
943
- return;
944
- }
945
-
946
- if (!isObject(currentHash[this.id])) {
947
- currentHash[this.id] = {};
948
- }
949
- currentHash[this.id][id] = controlValue;
950
-
951
- const mapping = {
952
- id,
953
- value: controlValue,
954
- label,
955
- };
956
-
957
- const formatter = new Formatter(mapping, {
958
- callbacks: {
959
- range: (value, key) => {
960
- return generateRangeComparisonExpression(value, key, {
961
- urlEncode: true,
962
- andOp: "AND",
963
- orOp: "OR",
964
- eqOp: "=",
965
- gtOp: ">",
966
- ltOp: "<",
967
- });
968
- },
969
- "date-range": (value, key) => {
970
- const query = parseDateInput(value, key);
971
- if (!query || query === "false") {
972
- return "";
973
- }
974
-
975
- // return query as url encoded
976
- return encodeURIComponent(query);
977
- },
978
- },
979
- });
980
-
981
- if (self.getOption("formatter.marker.open")) {
982
- formatter.setMarker(
983
- self.getOption("formatter.marker.open"),
984
- self.getOption("formatter.marker.close"),
985
- );
986
- }
987
-
988
- let queryPart = formatter.format(template);
989
- if (queryPart) {
990
- if (isFunction(wrapCallback)) {
991
- queryPart = wrapCallback(queryPart, mapping);
992
- }
993
- query.push(queryPart);
994
- }
995
- });
996
-
997
- if (hasNoIdError) {
998
- reject(new Error("some or all filter elements have no id"));
999
- return;
1000
- }
1001
-
1002
- getGlobal().location.hash = createBracketedKeyValueHash(currentHash);
1003
- resolve(query);
1004
- });
931
+ const currentHash = parseBracketedKeyValueHash(getGlobal().location.hash);
932
+ const self = this;
933
+
934
+ return new Promise((resolve, reject) => {
935
+ const query = [];
936
+ const wrapCallback = this.getOption("queries.wrap");
937
+
938
+ let hasNoIdError = false;
939
+
940
+ getSlottedElements
941
+ .call(this, "label[data-monster-label]")
942
+ .forEach((element) => {
943
+ const label = element.getAttribute("data-monster-label");
944
+ if (!label) {
945
+ throw new Error("no filter label is defined");
946
+ }
947
+
948
+ const id = element.id;
949
+ if (!id) {
950
+ hasNoIdError = true;
951
+ return;
952
+ }
953
+
954
+ //const visible = window.getComputedStyle(element).display !== "none";
955
+ const visible = getVisibilityFromSlotAttribute(element);
956
+ if (!visible) {
957
+ return;
958
+ }
959
+
960
+ let template = element.getAttribute("data-monster-template");
961
+ if (!template) {
962
+ template = "${id}=${value}";
963
+ }
964
+
965
+ const controlValue = getControlValuesFromLabel(element);
966
+ if (!controlValue) {
967
+ if (controlValue === "" && currentHash?.[this.id]?.[id]) {
968
+ delete currentHash[this.id][id];
969
+ }
970
+
971
+ return;
972
+ }
973
+
974
+ if (!isObject(currentHash[this.id])) {
975
+ currentHash[this.id] = {};
976
+ }
977
+ currentHash[this.id][id] = controlValue;
978
+
979
+ const mapping = {
980
+ id,
981
+ value: controlValue,
982
+ label,
983
+ };
984
+
985
+ const formatter = new Formatter(mapping, {
986
+ callbacks: {
987
+ range: (value, key) => {
988
+ return generateRangeComparisonExpression(value, key, {
989
+ urlEncode: true,
990
+ andOp: "AND",
991
+ orOp: "OR",
992
+ eqOp: "=",
993
+ gtOp: ">",
994
+ ltOp: "<",
995
+ });
996
+ },
997
+ "date-range": (value, key) => {
998
+ const query = parseDateInput(value, key);
999
+ if (!query || query === "false") {
1000
+ return "";
1001
+ }
1002
+
1003
+ // return query as url encoded
1004
+ return encodeURIComponent(query);
1005
+ },
1006
+ },
1007
+ });
1008
+
1009
+ if (self.getOption("formatter.marker.open")) {
1010
+ formatter.setMarker(
1011
+ self.getOption("formatter.marker.open"),
1012
+ self.getOption("formatter.marker.close"),
1013
+ );
1014
+ }
1015
+
1016
+ let queryPart = formatter.format(template);
1017
+ if (queryPart) {
1018
+ if (isFunction(wrapCallback)) {
1019
+ queryPart = wrapCallback(queryPart, mapping);
1020
+ }
1021
+ query.push(queryPart);
1022
+ }
1023
+ });
1024
+
1025
+ if (hasNoIdError) {
1026
+ reject(new Error("some or all filter elements have no id"));
1027
+ return;
1028
+ }
1029
+
1030
+ getGlobal().location.hash = createBracketedKeyValueHash(currentHash);
1031
+ resolve(query);
1032
+ });
1005
1033
  }
1006
1034
 
1007
1035
  /**
@@ -1010,38 +1038,38 @@ function collectSearchQueries() {
1010
1038
  * @return {null|Array|undefined|string}
1011
1039
  */
1012
1040
  function getControlValuesFromLabel(label) {
1013
- const foundControl = label.firstElementChild;
1014
-
1015
- if (foundControl) {
1016
- if (foundControl.tagName === "INPUT") {
1017
- if (foundControl.type === "checkbox") {
1018
- const checkedControls = label.querySelectorAll(
1019
- `${foundControl}:checked`,
1020
- );
1021
- const values = [];
1022
-
1023
- checkedControls.forEach((checkedControl) => {
1024
- values.push(checkedControl.value);
1025
- });
1026
-
1027
- return values;
1028
- } else if (foundControl.type === "radio") {
1029
- const checkedControl = label.querySelector(`${foundControl}:checked`);
1030
-
1031
- if (checkedControl) {
1032
- return checkedControl.value;
1033
- } else {
1034
- return null;
1035
- }
1036
- } else {
1037
- return foundControl.value;
1038
- }
1039
- } else {
1040
- return foundControl.value;
1041
- }
1042
- }
1043
-
1044
- return null;
1041
+ const foundControl = label.firstElementChild;
1042
+
1043
+ if (foundControl) {
1044
+ if (foundControl.tagName === "INPUT") {
1045
+ if (foundControl.type === "checkbox") {
1046
+ const checkedControls = label.querySelectorAll(
1047
+ `${foundControl}:checked`,
1048
+ );
1049
+ const values = [];
1050
+
1051
+ checkedControls.forEach((checkedControl) => {
1052
+ values.push(checkedControl.value);
1053
+ });
1054
+
1055
+ return values;
1056
+ } else if (foundControl.type === "radio") {
1057
+ const checkedControl = label.querySelector(`${foundControl}:checked`);
1058
+
1059
+ if (checkedControl) {
1060
+ return checkedControl.value;
1061
+ } else {
1062
+ return null;
1063
+ }
1064
+ } else {
1065
+ return foundControl.value;
1066
+ }
1067
+ } else {
1068
+ return foundControl.value;
1069
+ }
1070
+ }
1071
+
1072
+ return null;
1045
1073
  }
1046
1074
 
1047
1075
  /**
@@ -1049,60 +1077,60 @@ function getControlValuesFromLabel(label) {
1049
1077
  * @return {Promise<unknown>}
1050
1078
  */
1051
1079
  function initFromConfig() {
1052
- const host = findElementWithSelectorUpwards(this, "monster-host");
1053
-
1054
- if (!(isInstance(host, Host) && this.id)) {
1055
- return Promise.resolve();
1056
- }
1057
-
1058
- const configKey = getFilterConfigKey.call(this);
1059
-
1060
- return new Promise((resolve, reject) => {
1061
- host
1062
- .getConfig(configKey)
1063
- .then((config) => {
1064
- if ((config && isObject(config)) || isArray(config)) {
1065
- this[settingsSymbol].setOptions(config);
1066
- }
1067
- resolve();
1068
- })
1069
- .catch((error) => {
1070
- if (error === undefined) {
1071
- resolve();
1072
- return;
1073
- }
1074
-
1075
- // config not written
1076
- if (error?.message?.match(/is not defined/)) {
1077
- resolve();
1078
- return;
1079
- }
1080
-
1081
- addAttributeToken(
1082
- this,
1083
- ATTRIBUTE_ERRORMESSAGE,
1084
- error?.message || error,
1085
- );
1086
- reject(error);
1087
- });
1088
- });
1080
+ const host = findElementWithSelectorUpwards(this, "monster-host");
1081
+
1082
+ if (!(isInstance(host, Host) && this.id)) {
1083
+ return Promise.resolve();
1084
+ }
1085
+
1086
+ const configKey = getFilterConfigKey.call(this);
1087
+
1088
+ return new Promise((resolve, reject) => {
1089
+ host
1090
+ .getConfig(configKey)
1091
+ .then((config) => {
1092
+ if ((config && isObject(config)) || isArray(config)) {
1093
+ this[settingsSymbol].setOptions(config);
1094
+ }
1095
+ resolve();
1096
+ })
1097
+ .catch((error) => {
1098
+ if (error === undefined) {
1099
+ resolve();
1100
+ return;
1101
+ }
1102
+
1103
+ // config not written
1104
+ if (error?.message?.match(/is not defined/)) {
1105
+ resolve();
1106
+ return;
1107
+ }
1108
+
1109
+ addAttributeToken(
1110
+ this,
1111
+ ATTRIBUTE_ERRORMESSAGE,
1112
+ error?.message || error,
1113
+ );
1114
+ reject(error);
1115
+ });
1116
+ });
1089
1117
  }
1090
1118
 
1091
1119
  /**
1092
1120
  * @private
1093
1121
  */
1094
1122
  function updateConfig() {
1095
- const host = findElementWithSelectorUpwards(this, "monster-host");
1096
- if (!(host && this.id)) {
1097
- return;
1098
- }
1099
- const configKey = getFilterConfigKey.call(this);
1100
-
1101
- try {
1102
- host.setConfig(configKey, this[settingsSymbol].getOptions());
1103
- } catch (error) {
1104
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message || error);
1105
- }
1123
+ const host = findElementWithSelectorUpwards(this, "monster-host");
1124
+ if (!(host && this.id)) {
1125
+ return;
1126
+ }
1127
+ const configKey = getFilterConfigKey.call(this);
1128
+
1129
+ try {
1130
+ host.setConfig(configKey, this[settingsSymbol].getOptions());
1131
+ } catch (error) {
1132
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message || error);
1133
+ }
1106
1134
  }
1107
1135
 
1108
1136
  /**
@@ -1110,49 +1138,50 @@ function updateConfig() {
1110
1138
  * @return {string}
1111
1139
  */
1112
1140
  function getTemplate() {
1113
- // language=HTML
1114
- return `
1115
- <div data-monster-role="control" part="control">
1116
- <div data-monster-role="layout">
1117
- <div data-monster-role="filter">
1118
- <slot></slot>
1119
- <slot name="hidden"></slot>
1120
- </div>
1121
- <div data-monster-role="select-and-search">
1122
- <monster-message-state-button data-monster-role="search-button" class="stretched-control"
1123
- data-monster-replace="path:labels.search">
1124
- </monster-message-state-button>
1125
- <monster-select class="stretched-control"
1126
- data-monster-selected-template="summary"
1127
- data-monster-option-type="checkbox"
1128
- data-monster-option-filter-mode="options"
1129
- data-monster-option-filter-position="popper"
1130
- data-monster-role="filter-select"></monster-select>
1131
- <monster-popper-button data-monster-role="save-button" class="stretched-control"
1132
- data-monster-attributes="data-monster-visible path:storedConfig.enabled">
1133
- <div slot="button">\${filter-save-label}</div>
1134
- <div class="monster-form" data-monster-role="form">
1135
-
1136
- <label for="filter-name">\${filter-name-label}
1137
- <input name="filter-name"
1138
- type="search"
1139
- class="monster-margin-bottom-5"></label>
1140
- <monster-message-state-button
1141
- data-monster-role="save-action-button"
1142
- data-monster-option-labels-button="\${filter-save-label}">
1143
- </monster-message-state-button>
1144
-
1145
- </div>
1146
- </monster-popper-button>
1147
- <monster-button data-monster-role="reset-button" class="stretched-control"
1148
- data-monster-replace="path:labels.reset">
1149
- </monster-button>
1150
- </div>
1151
-
1141
+ // language=HTML
1142
+ return `<div data-monster-role="control" part="control">
1143
+ <div data-monster-role="container">
1144
+ <div data-monster-role="layout">
1145
+ <div data-monster-role="filter">
1146
+ <slot></slot>
1147
+ <slot name="hidden"></slot>
1152
1148
  </div>
1153
- <input class="hidden" name="query" data-monster-role="query"
1154
- data-monster-attributes="value path:query">
1149
+ <div data-monster-role="select-and-search">
1150
+ <monster-message-state-button data-monster-role="search-button" class="stretched-control"
1151
+ data-monster-replace="path:labels.search">
1152
+ </monster-message-state-button>
1153
+ <monster-select class="stretched-control"
1154
+ data-monster-selected-template="summary"
1155
+ data-monster-option-type="checkbox"
1156
+ data-monster-option-filter-mode="options"
1157
+ data-monster-option-filter-position="popper"
1158
+ data-monster-role="filter-select"></monster-select>
1159
+ <monster-popper-button data-monster-role="save-button" class="stretched-control"
1160
+ data-monster-attributes="data-monster-visible path:storedConfig.enabled">
1161
+ <div slot="button">\${filter-save-label}</div>
1162
+ <div class="monster-form" data-monster-role="form">
1163
+
1164
+ <label for="filter-name">\${filter-name-label}
1165
+ <input name="filter-name"
1166
+ type="search"
1167
+ class="monster-margin-bottom-5"></label>
1168
+ <monster-message-state-button
1169
+ data-monster-role="save-action-button"
1170
+ data-monster-option-labels-button="\${filter-save-label}">
1171
+ </monster-message-state-button>
1172
+
1173
+ </div>
1174
+ </monster-popper-button>
1175
+ <monster-button data-monster-role="reset-button" class="stretched-control"
1176
+ data-monster-replace="path:labels.reset">
1177
+ </monster-button>
1178
+ </div>
1179
+
1155
1180
  </div>
1181
+ <input class="hidden" name="query" data-monster-role="query"
1182
+ data-monster-attributes="value path:query">
1183
+ </div>
1184
+ </div>
1156
1185
  `;
1157
1186
  }
1158
1187