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