@schukai/monster 3.55.0 → 3.55.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +1 -1
  3. package/source/components/datatable/datasource/rest.mjs +77 -46
  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 +905 -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/datasource/server/restapi.mjs +1 -0
  91. package/source/data/transformer.mjs +6 -8
  92. package/source/dom/attributes.mjs +5 -5
  93. package/source/dom/customelement.mjs +2 -2
  94. package/source/dom/updater.mjs +697 -700
  95. package/source/dom/util.mjs +2 -2
  96. package/source/monster.mjs +0 -1
  97. package/source/types/noderecursiveiterator.mjs +9 -7
  98. package/source/types/version.mjs +1 -1
  99. package/source/util/sleep.mjs +3 -4
  100. 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: undefined,
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,16 @@ 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
+ return input;
445
+ }
446
+
447
+ return input
448
+ .replace(/&/g, "&amp;")
449
+ .replace(/"/g, "&quot;")
450
+ .replace(/'/g, "&#x27;")
451
+ .replace(/</g, "&lt;")
452
+ .replace(/>/g, "&gt;");
437
453
  }
438
454
 
439
455
  /**
@@ -442,7 +458,9 @@ function escapeAttributeValue(input) {
442
458
  * @returns {boolean}
443
459
  */
444
460
  function getVisibilityFromSlotAttribute(element) {
445
- return !(element.hasAttribute("slot") && element.getAttribute("slot") === "hidden");
461
+ return !(
462
+ element.hasAttribute("slot") && element.getAttribute("slot") === "hidden"
463
+ );
446
464
  }
447
465
 
448
466
  /**
@@ -451,306 +469,346 @@ function getVisibilityFromSlotAttribute(element) {
451
469
  * @param {boolean} visible
452
470
  */
453
471
  function setSlotAttribute(element, visible) {
454
- if (visible) {
455
- element.removeAttribute("slot");
456
- return;
457
- }
472
+ if (visible) {
473
+ element.removeAttribute("slot");
474
+ return;
475
+ }
458
476
 
459
- element.setAttribute("slot", "hidden");
477
+ element.setAttribute("slot", "hidden");
460
478
  }
461
479
 
462
480
  /**
463
481
  * @private
464
482
  */
465
483
  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
-
484
+ const self = this;
485
+ /**
486
+ * Monster.Components.Form.event:monster-selection-cleared
487
+ */
488
+ if (self[filterSelectElementSymbol]) {
489
+ self[filterSelectElementSymbol].addEventListener(
490
+ "monster-selection-cleared",
491
+ function () {
492
+ const settings = self[settingsSymbol].getOptions();
493
+
494
+ for (const setting of settings) {
495
+ const filterElement = findElementWithIdUpwards(self, setting.value);
496
+ if (filterElement) {
497
+ setSlotAttribute(filterElement, false);
498
+
499
+ self[settingsSymbol].set({ value: setting.value, visible: false });
500
+ }
501
+ }
502
+
503
+ updateConfig.call(self);
504
+ },
505
+ );
506
+
507
+ self[filterSelectElementSymbol].addEventListener(
508
+ "monster-changed",
509
+ function (event) {
510
+ const filterElement = findElementWithIdUpwards(
511
+ self,
512
+ event.detail.value,
513
+ );
514
+ if (filterElement) {
515
+ //filterElement.style.display = event.detail.checked ? "block" : "none";
516
+ setSlotAttribute(filterElement, event.detail.checked);
517
+ }
518
+
519
+ self[settingsSymbol].set({
520
+ value: event.detail.value,
521
+ visible: event.detail.checked,
522
+ });
523
+
524
+ updateConfig.call(self);
525
+ },
526
+ );
527
+ }
528
+
529
+ if (self[filterSaveActionButtonElementSymbol]) {
530
+ self[filterSaveActionButtonElementSymbol].setOption(
531
+ "actions.click",
532
+ function (event) {
533
+ const button = findTargetElementFromEvent(
534
+ event,
535
+ "data-monster-role",
536
+ "save-action-button",
537
+ );
538
+ const form = button.closest("[data-monster-role=form]");
539
+
540
+ if (!form) {
541
+ button.setState("failed", self.getOption("timeouts.message", 4000));
542
+ return;
543
+ }
544
+
545
+ const input = form.querySelector("input[name=filter-name]");
546
+ if (!input) {
547
+ button.setState("failed", self.getOption("timeouts.message", 4000));
548
+ return;
549
+ }
550
+
551
+ const name = input.value;
552
+ if (!name) {
553
+ button.setState("failed", self.getOption("timeouts.message", 4000));
554
+ button.setMessage("Please enter a name").showMessage();
555
+ return;
556
+ }
557
+
558
+ doSearch
559
+ .call(self, { showEffect: false })
560
+ .then(() => {
561
+ const configKey = getStoredFilterConfigKey.call(self);
562
+ const host = getDocument().querySelector("monster-host");
563
+ if (!host) {
564
+ return;
565
+ }
566
+
567
+ const query = self.getOption("query");
568
+ if (!query) {
569
+ button.setState(
570
+ "failed",
571
+ self.getOption(
572
+ "timeouts.message",
573
+ self.getOption("timeouts.message", 4000),
574
+ ),
575
+ );
576
+ button
577
+ .setMessage("No query found")
578
+ .showMessage(self.getOption("timeouts.message", 4000));
579
+ return;
580
+ }
581
+
582
+ host
583
+ .hasConfig(configKey)
584
+ .then((hasConfig) => {
585
+ return new Promise((resolve, reject) => {
586
+ if (hasConfig) {
587
+ host.getConfig(configKey).then(resolve).catch(reject);
588
+ return;
589
+ }
590
+ return resolve({});
591
+ });
592
+ })
593
+ .then((config) => {
594
+ config[name] = query;
595
+ return host.setConfig(configKey, {
596
+ ...config,
597
+ });
598
+ })
599
+ .then(() => {
600
+ button.setState(
601
+ "successful",
602
+ self.getOption("timeouts.message", 4000),
603
+ );
604
+ updateFilterTabs.call(self);
605
+ })
606
+ .catch((error) => {
607
+ button.setState(
608
+ "failed",
609
+ self.getOption("timeouts.message", 4000),
610
+ );
611
+ button
612
+ .setMessage(error.message)
613
+ .showMessage(self.getOption("timeouts.message", 4000));
614
+ });
615
+ })
616
+ .catch((error) => {
617
+ button.setState("failed", self.getOption("timeouts.message", 4000));
618
+ const msg = error.message || error;
619
+ button
620
+ .setMessage(msg)
621
+ .showMessage(self.getOption("timeouts.message", 4000));
622
+ });
623
+ },
624
+ );
625
+ }
626
+
627
+ self[searchButtonElementSymbol].setOption("actions.click", () => {
628
+ doSearch
629
+ .call(self)
630
+ .then(() => {})
631
+ .catch((error) => {});
632
+ });
633
+
634
+ // the reset button should reset the filter and the search query
635
+ // all input elements should be reset to their default values
636
+ // which is the empty string. we search for all input elements
637
+ // in the filter and reset them to their default value
638
+ self[resetButtonElementSymbol].setOption("actions.click", () => {
639
+ getSlottedElements
640
+ .call(self, "label[data-monster-label]")
641
+ .forEach((element) => {
642
+ const label = element.getAttribute("data-monster-label");
643
+ if (!label) {
644
+ return;
645
+ }
646
+
647
+ const input = element.firstElementChild;
648
+
649
+ if (input) {
650
+ input.value = "";
651
+ }
652
+ });
653
+
654
+ doSearch.call(self, { showEffect: false });
655
+ });
656
+
657
+ self[locationChangeHandlerSymbol] = (event) => {
658
+ if (event instanceof HashChangeEvent) {
659
+ if (event.oldURL === event.newURL) {
660
+ return;
661
+ }
662
+ }
663
+ };
664
+
665
+ self.addEventListener("keyup", (event) => {
666
+ const path = event.composedPath();
667
+ if (path.length === 0) {
668
+ return;
669
+ }
670
+
671
+ if (!(path[0] instanceof HTMLInputElement)) {
672
+ return;
673
+ }
674
+
675
+ if (event.keyCode === 13) {
676
+ doSearch.call(self, { showEffect: false });
677
+ }
678
+ });
679
+
680
+ // tabs
681
+ const element = this[filterTabElementSymbol];
682
+ if (element) {
683
+ initTabEvents.call(this);
684
+ }
647
685
  }
648
686
 
649
687
  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
-
688
+
689
+ this[filterTabElementSymbol].addEventListener(
690
+ "monster-tab-changed",
691
+ (event) => {
692
+
693
+ const query = event?.detail?.data?.["data-monster-query"];
694
+ const q = this.getOption("query");
695
+ if (query !== q) {
696
+ this.setOption("query", query);
697
+ }
698
+ },
699
+ );
700
+
701
+ this[filterTabElementSymbol].addEventListener(
702
+ "monster-tab-remove",
703
+ (event) => {
704
+ const labels = [];
705
+ const buttons = this[filterTabElementSymbol].getOption("buttons");
706
+
707
+ const keys = ["popper", "standard"];
708
+ for (let i = 0; i < keys.length; i++) {
709
+ const key = keys[i];
710
+
711
+ for (const button of buttons[key]) {
712
+ if (button.label !== event.detail.label) {
713
+ labels.push(button.label);
714
+ }
715
+ }
716
+ }
717
+
718
+ const document = getDocument();
719
+ const host = document.querySelector("monster-host");
720
+ if (!(host && this.id)) {
721
+ return;
722
+ }
723
+
724
+ const configKey = getStoredFilterConfigKey.call(this);
725
+ host
726
+ .hasConfig(configKey)
727
+ .then((hasConfig) => {
728
+ if (!hasConfig) {
729
+ return;
730
+ }
731
+
732
+ return host.getConfig(configKey);
733
+ })
734
+ .then((config) => {
735
+ for (const [name, query] of Object.entries(config)) {
736
+ if (labels.includes(name)) {
737
+ continue;
738
+ }
739
+
740
+ delete config[name];
741
+ }
742
+
743
+ return host.setConfig(configKey, {
744
+ ...config,
745
+ });
746
+ });
747
+ },
748
+ );
707
749
  }
708
750
 
709
751
  /**
710
752
  * @private
711
753
  */
712
754
  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", `
755
+ const element = this[filterTabElementSymbol];
756
+ if (!element) {
757
+ return;
758
+ }
759
+
760
+ const document = getDocument();
761
+ const host = document.querySelector("monster-host");
762
+ if (!(host && this.id)) {
763
+ return;
764
+ }
765
+
766
+ const configKey = getStoredFilterConfigKey.call(this);
767
+ host
768
+ .hasConfig(configKey)
769
+ .then((hasConfig) => {
770
+ if (!hasConfig) {
771
+ return;
772
+ }
773
+
774
+ return host.getConfig(configKey);
775
+ })
776
+ .then((config) => {
777
+ for (const [name, query] of Object.entries(config)) {
778
+ const found = element.querySelector(
779
+ `[data-monster-button-label="${name}"]`,
780
+ );
781
+ if (found) {
782
+ continue;
783
+ }
784
+
785
+ if (query === undefined || query === null) {
786
+ continue;
787
+ }
788
+
789
+ const escapedQuery = escapeAttributeValue(query);
790
+
791
+ element.insertAdjacentHTML(
792
+ "beforeend",
793
+ `
748
794
  <div data-monster-button-label="${name}"
749
795
  data-monster-removable="true"
750
796
  data-monster-query="${escapedQuery}" data-monster-role="filter-tab" >
751
- </div>`);
752
- }
753
- })
797
+ </div>`,
798
+ );
799
+ }
800
+ })
801
+ .catch((error) => {
802
+ if (error instanceof Error) {
803
+ addAttributeToken(
804
+ this,
805
+ ATTRIBUTE_ERRORMESSAGE,
806
+ error.message + " " + error.stack,
807
+ );
808
+ } else {
809
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error + "");
810
+ }
811
+ });
754
812
  }
755
813
 
756
814
  /**
@@ -758,74 +816,77 @@ function updateFilterTabs() {
758
816
  * @param showEffect
759
817
  * @returns {Promise<*>}
760
818
  */
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
- });
819
+ function doSearch({ showEffect } = { showEffect: true }) {;
820
+
821
+ this.resetFailureMessage();
822
+
823
+ if (showEffect) {
824
+ this[searchButtonElementSymbol].setState(
825
+ "activity",
826
+ this.getOption("timeouts.message", 4000),
827
+ );
828
+ }
829
+
830
+ return collectSearchQueries
831
+ .call(this)
832
+ .then((query) => {
833
+ const buildQuery = buildSearchQuery.call(this, query);
834
+ if (buildQuery === "" && !this.getOption("defaultQuery")) {
835
+ const msg = this.getOption("labels.empty-query-and-no-default");
836
+
837
+ if (showEffect) {
838
+ this[searchButtonElementSymbol].removeState();
839
+ this[searchButtonElementSymbol]
840
+ .setMessage(msg)
841
+ .showMessage(this.getOption("timeouts.message", 4000));
842
+ }
843
+
844
+ throw new Error(msg);
845
+ }
846
+
847
+ if (buildQuery === this.getOption("query")) {
848
+ const msg = this.getOption("labels.query-not-changed");
849
+
850
+ if (showEffect) {
851
+ this[searchButtonElementSymbol].removeState();
852
+ this[searchButtonElementSymbol]
853
+ .setMessage(msg)
854
+ .showMessage(this.getOption("timeouts.message", 4000));
855
+ }
856
+
857
+ throw new Error(msg);
858
+ }
859
+
860
+ if (showEffect) {
861
+ this[searchButtonElementSymbol].setState(
862
+ "activity",
863
+ this.getOption("timeouts.message", 4000),
864
+ );
865
+ }
866
+
867
+ this.setOption("query", buildSearchQuery.call(this, query));
868
+ })
869
+ .catch((error) => {
870
+ if (error instanceof Error) {
871
+ addAttributeToken(
872
+ this,
873
+ ATTRIBUTE_ERRORMESSAGE,
874
+ error.message + " " + error.stack,
875
+ );
876
+ } else {
877
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
878
+ }
879
+
880
+ if (showEffect) {
881
+ this[searchButtonElementSymbol].setState(
882
+ "failed",
883
+ this.getOption("timeouts.message", 4000),
884
+ );
885
+ this[searchButtonElementSymbol].setMessage(error.message).showMessage();
886
+ }
887
+
888
+ return Promise.reject(error);
889
+ });
829
890
  }
830
891
 
831
892
  /**
@@ -834,21 +895,21 @@ function doSearch({showEffect} = {showEffect: true}) {
834
895
  * @returns {*|string}
835
896
  */
836
897
  function buildSearchQuery(queries) {
837
- if (!isArray(queries) || queries.length === 0) {
838
- return this.getOption("defaultQuery");
839
- }
898
+ if (!isArray(queries) || queries.length === 0) {
899
+ return this.getOption("defaultQuery");
900
+ }
840
901
 
841
- const joinCallback = this.getOption("queries.join");
842
- if (isFunction(joinCallback)) {
843
- return joinCallback(queries);
844
- }
902
+ const joinCallback = this.getOption("queries.join");
903
+ if (isFunction(joinCallback)) {
904
+ return joinCallback(queries);
905
+ }
845
906
 
846
- const q = queries.join(" ").trim();
847
- if (q.length === 0) {
848
- return this.getOption("defaultQuery");
849
- }
907
+ const q = queries.join(" ").trim();
908
+ if (q.length === 0) {
909
+ return this.getOption("defaultQuery");
910
+ }
850
911
 
851
- return q;
912
+ return q;
852
913
  }
853
914
 
854
915
  /**
@@ -856,200 +917,203 @@ function buildSearchQuery(queries) {
856
917
  * @returns {Promise<unknown>}
857
918
  */
858
919
  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
- });
920
+ const currentHash = parseBracketedKeyValueHash(getGlobal().location.hash);
921
+
922
+ return new Promise((resolve, reject) => {
923
+ const query = [];
924
+ const wrapCallback = this.getOption("queries.wrap");
925
+
926
+ let hasNoIdError = false;
927
+
928
+ getSlottedElements
929
+ .call(this, "label[data-monster-label]")
930
+ .forEach((element) => {
931
+ const label = element.getAttribute("data-monster-label");
932
+ if (!label) {
933
+ throw new Error("no filter label is defined");
934
+ }
935
+
936
+ const id = element.id;
937
+ if (!id) {
938
+ hasNoIdError = true;
939
+ return;
940
+ }
941
+
942
+ //const visible = window.getComputedStyle(element).display !== "none";
943
+ const visible = getVisibilityFromSlotAttribute(element);
944
+ if (!visible) {
945
+ return;
946
+ }
947
+
948
+ let template = element.getAttribute("data-monster-template");
949
+ if (!template) {
950
+ template = "${id}=${value}";
951
+ }
952
+
953
+ const controlValue = getControlValuesFromLabel(element);
954
+ if (!controlValue) {
955
+ if (controlValue === "" && currentHash?.[this.id]?.[id]) {
956
+ delete currentHash[this.id][id];
957
+ }
958
+
959
+ return;
960
+ }
961
+
962
+ if (!isObject(currentHash[this.id])) {
963
+ currentHash[this.id] = {};
964
+ }
965
+ currentHash[this.id][id] = controlValue;
966
+
967
+ const mapping = {
968
+ id,
969
+ value: controlValue,
970
+ label,
971
+ };
972
+
973
+ const formatter = new Formatter(mapping, {
974
+ callbacks: {
975
+ range: (value, key) => {
976
+ return generateRangeComparisonExpression(value, key, {
977
+ urlEncode: true,
978
+ andOp: "AND",
979
+ orOp: "OR",
980
+ eqOp: "=",
981
+ gtOp: ">",
982
+ ltOp: "<",
983
+ });
984
+ },
985
+ "date-range": (value, key) => {
986
+ const query = parseDateInput(value, key);
987
+ if (!query || query === "false") {
988
+ return "";
989
+ }
990
+
991
+ // return query as url encoded
992
+ return encodeURIComponent(query);
993
+ },
994
+ },
995
+ });
996
+
997
+ let queryPart = formatter.format(template);
998
+ if (queryPart) {
999
+ if (isFunction(wrapCallback)) {
1000
+ queryPart = wrapCallback(queryPart, mapping);
1001
+ }
1002
+ query.push(queryPart);
1003
+ }
1004
+ });
1005
+
1006
+ if (hasNoIdError) {
1007
+ reject(new Error("some or all filter elements have no id"));
1008
+ return;
1009
+ }
1010
+
1011
+ getGlobal().location.hash = createBracketedKeyValueHash(currentHash);
1012
+ resolve(query);
1013
+ });
954
1014
  }
955
1015
 
956
-
957
1016
  /**
958
1017
  * @private
959
1018
  * @param label
960
1019
  * @returns {null|Array|undefined|string}
961
1020
  */
962
1021
  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;
1022
+ const foundControl = label.firstElementChild;
1023
+
1024
+ if (foundControl) {
1025
+ if (foundControl.tagName === "INPUT") {
1026
+ if (foundControl.type === "checkbox") {
1027
+ const checkedControls = label.querySelectorAll(
1028
+ `${foundControl}:checked`,
1029
+ );
1030
+ const values = [];
1031
+
1032
+ checkedControls.forEach((checkedControl) => {
1033
+ values.push(checkedControl.value);
1034
+ });
1035
+
1036
+ return values;
1037
+ } else if (foundControl.type === "radio") {
1038
+ const checkedControl = label.querySelector(`${foundControl}:checked`);
1039
+
1040
+ if (checkedControl) {
1041
+ return checkedControl.value;
1042
+ } else {
1043
+ return null;
1044
+ }
1045
+ } else {
1046
+ return foundControl.value;
1047
+ }
1048
+ } else {
1049
+ return foundControl.value;
1050
+ }
1051
+ }
1052
+
1053
+ return null;
993
1054
  }
994
1055
 
995
-
996
1056
  /**
997
1057
  * @private
998
1058
  * @returns {Promise<unknown>}
999
1059
  */
1000
1060
  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
- });
1061
+ const document = getDocument();
1062
+ const host = document.querySelector("monster-host");
1063
+
1064
+ if (!(isInstance(host, Host) && this.id)) {
1065
+ return Promise.resolve();
1066
+ }
1067
+
1068
+ const configKey = getFilterConfigKey.call(this);
1069
+
1070
+ return new Promise((resolve, reject) => {
1071
+ host
1072
+ .getConfig(configKey)
1073
+ .then((config) => {
1074
+ if (config && isObject(config)) {
1075
+ this[settingsSymbol].setOptions(config);
1076
+ }
1077
+ resolve();
1078
+ })
1079
+ .catch((error) => {
1080
+ if (error === undefined) {
1081
+ resolve();
1082
+ return;
1083
+ }
1084
+
1085
+ // config not written
1086
+ if (error?.message?.match(/is not defined/)) {
1087
+ resolve();
1088
+ return;
1089
+ }
1090
+
1091
+ addAttributeToken(
1092
+ this,
1093
+ ATTRIBUTE_ERRORMESSAGE,
1094
+ error?.message || error,
1095
+ );
1096
+ reject(error);
1097
+ });
1098
+ });
1035
1099
  }
1036
1100
 
1037
1101
  /**
1038
1102
  * @private
1039
1103
  */
1040
1104
  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
- }
1105
+ const document = getDocument();
1106
+ const host = document.querySelector("monster-host");
1107
+ if (!(host && this.id)) {
1108
+ return;
1109
+ }
1110
+ const configKey = getFilterConfigKey.call(this);
1111
+
1112
+ try {
1113
+ host.setConfig(configKey, this[settingsSymbol].getOptions());
1114
+ } catch (error) {
1115
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message || error);
1116
+ }
1053
1117
  }
1054
1118
 
1055
1119
  /**
@@ -1057,9 +1121,8 @@ function updateConfig() {
1057
1121
  * @return {string}
1058
1122
  */
1059
1123
  function getTemplate() {
1060
-
1061
- // language=HTML
1062
- return `
1124
+ // language=HTML
1125
+ return `
1063
1126
  <div data-monster-role="control" part="control">
1064
1127
  <div data-monster-role="layout">
1065
1128
  <div data-monster-role="filter">