@schukai/monster 3.55.0 → 3.55.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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">