@schukai/monster 3.88.0 → 3.88.1

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