@schukai/monster 3.86.4 → 3.87.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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