@schukai/monster 3.55.0 → 3.55.1

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