@schukai/monster 3.54.0 → 3.55.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +1 -1
  3. package/source/components/datatable/datasource/rest.mjs +93 -57
  4. package/source/components/datatable/datatable/header.mjs +8 -0
  5. package/source/components/datatable/datatable.mjs +88 -44
  6. package/source/components/datatable/embedded-pagination.mjs +24 -43
  7. package/source/components/datatable/filter/util.mjs +138 -0
  8. package/source/components/datatable/filter.mjs +418 -174
  9. package/source/components/datatable/pagination.mjs +26 -2
  10. package/source/components/datatable/status.mjs +226 -0
  11. package/source/components/datatable/style/datatable.pcss +1 -0
  12. package/source/components/datatable/style/embedded-pagination.pcss +59 -2
  13. package/source/components/datatable/style/filter.pcss +4 -0
  14. package/source/components/datatable/style/pagination.pcss +28 -4
  15. package/source/components/datatable/style/status.pcss +42 -0
  16. package/source/components/datatable/stylesheet/column-bar.mjs +15 -9
  17. package/source/components/datatable/stylesheet/dataset.mjs +14 -8
  18. package/source/components/datatable/stylesheet/datasource.mjs +14 -8
  19. package/source/components/datatable/stylesheet/datatable.mjs +15 -9
  20. package/source/components/datatable/stylesheet/embedded-pagination.mjs +14 -8
  21. package/source/components/datatable/stylesheet/filter-button.mjs +15 -9
  22. package/source/components/datatable/stylesheet/filter-controls-defaults.mjs +14 -8
  23. package/source/components/datatable/stylesheet/filter-date-range.mjs +14 -8
  24. package/source/components/datatable/stylesheet/filter-range.mjs +14 -8
  25. package/source/components/datatable/stylesheet/filter.mjs +15 -9
  26. package/source/components/datatable/stylesheet/pagination.mjs +15 -9
  27. package/source/components/datatable/stylesheet/select-filter.mjs +14 -8
  28. package/source/components/datatable/stylesheet/status.mjs +33 -0
  29. package/source/components/form/action-button.mjs +3 -1
  30. package/source/components/form/api-button.mjs +1 -1
  31. package/source/components/form/button-bar.mjs +1 -1
  32. package/source/components/form/button.mjs +1 -1
  33. package/source/components/form/confirm-button.mjs +3 -1
  34. package/source/components/form/context-error.mjs +272 -0
  35. package/source/components/form/context-help.mjs +7 -5
  36. package/source/components/form/form.mjs +4 -2
  37. package/source/components/form/message-state-button.mjs +4 -2
  38. package/source/components/form/popper-button.mjs +9 -4
  39. package/source/components/form/popper.mjs +11 -3
  40. package/source/components/form/reload.mjs +1 -1
  41. package/source/components/form/select.mjs +3 -3
  42. package/source/components/form/shadow-reload.mjs +1 -1
  43. package/source/components/form/state-button.mjs +4 -1
  44. package/source/components/form/style/context-error.pcss +32 -0
  45. package/source/components/form/style/context-help.pcss +22 -5
  46. package/source/components/form/stylesheet/action-button.mjs +14 -8
  47. package/source/components/form/stylesheet/api-button.mjs +14 -8
  48. package/source/components/form/stylesheet/button-bar.mjs +14 -8
  49. package/source/components/form/stylesheet/button.mjs +14 -8
  50. package/source/components/form/stylesheet/confirm-button.mjs +14 -8
  51. package/source/components/form/stylesheet/context-error.mjs +33 -0
  52. package/source/components/form/stylesheet/context-help.mjs +15 -9
  53. package/source/components/form/stylesheet/form.mjs +14 -8
  54. package/source/components/form/stylesheet/message-state-button.mjs +14 -8
  55. package/source/components/form/stylesheet/popper-button.mjs +14 -8
  56. package/source/components/form/stylesheet/popper.mjs +14 -8
  57. package/source/components/form/stylesheet/select.mjs +15 -9
  58. package/source/components/form/stylesheet/state-button.mjs +14 -8
  59. package/source/components/form/stylesheet/tabs.mjs +15 -9
  60. package/source/components/form/stylesheet/tree-select.mjs +14 -8
  61. package/source/components/form/tabs.mjs +53 -7
  62. package/source/components/form/template.mjs +1 -1
  63. package/source/components/form/tree-select.mjs +1 -1
  64. package/source/components/host/collapse.mjs +20 -5
  65. package/source/components/host/config-manager.mjs +41 -2
  66. package/source/components/host/host.mjs +14 -0
  67. package/source/components/host/stylesheet/call-button.mjs +15 -9
  68. package/source/components/host/stylesheet/collapse.mjs +14 -8
  69. package/source/components/host/stylesheet/config-manager.mjs +14 -8
  70. package/source/components/host/stylesheet/details.mjs +14 -8
  71. package/source/components/host/stylesheet/host.mjs +14 -8
  72. package/source/components/host/stylesheet/overlay.mjs +15 -9
  73. package/source/components/host/stylesheet/toggle-button.mjs +15 -9
  74. package/source/components/host/stylesheet/viewer.mjs +14 -8
  75. package/source/components/host/util.mjs +6 -1
  76. package/source/components/notify/stylesheet/message.mjs +15 -9
  77. package/source/components/notify/stylesheet/notify.mjs +14 -8
  78. package/source/components/state/stylesheet/log.mjs +14 -8
  79. package/source/components/state/stylesheet/state.mjs +14 -8
  80. package/source/components/stylesheet/badge.mjs +14 -8
  81. package/source/components/stylesheet/border.mjs +14 -8
  82. package/source/components/stylesheet/button.mjs +14 -8
  83. package/source/components/stylesheet/card.mjs +14 -8
  84. package/source/components/stylesheet/color.mjs +14 -8
  85. package/source/components/stylesheet/common.mjs +14 -8
  86. package/source/components/stylesheet/control.mjs +14 -8
  87. package/source/components/stylesheet/data-grid.mjs +14 -8
  88. package/source/components/stylesheet/display.mjs +14 -8
  89. package/source/components/stylesheet/floating-ui.mjs +14 -8
  90. package/source/components/stylesheet/form.mjs +14 -8
  91. package/source/components/stylesheet/host.mjs +14 -8
  92. package/source/components/stylesheet/icons.mjs +15 -9
  93. package/source/components/stylesheet/link.mjs +14 -8
  94. package/source/components/stylesheet/normalize.mjs +14 -8
  95. package/source/components/stylesheet/popper.mjs +14 -8
  96. package/source/components/stylesheet/property.mjs +14 -8
  97. package/source/components/stylesheet/ripple.mjs +14 -8
  98. package/source/components/stylesheet/skeleton.mjs +14 -8
  99. package/source/components/stylesheet/space.mjs +14 -8
  100. package/source/components/stylesheet/spinner.mjs +14 -8
  101. package/source/components/stylesheet/table.mjs +14 -8
  102. package/source/components/stylesheet/theme.mjs +14 -8
  103. package/source/components/stylesheet/typography.mjs +14 -8
  104. package/source/components/tree-menu/stylesheet/tree-menu.mjs +14 -8
  105. package/source/data/transformer.mjs +38 -43
  106. package/source/dom/attributes.mjs +5 -5
  107. package/source/dom/customelement.mjs +1 -1
  108. package/source/dom/updater.mjs +14 -5
  109. package/source/dom/util.mjs +42 -0
  110. package/source/i18n/providers/embed.mjs +3 -3
  111. package/source/monster.mjs +5 -0
  112. package/source/text/formatter.mjs +2 -2
  113. package/source/types/noderecursiveiterator.mjs +9 -7
  114. package/source/types/observer.mjs +1 -1
  115. package/source/types/version.mjs +1 -1
  116. package/source/util/sleep.mjs +17 -0
  117. package/test/cases/components/form/button.mjs +2 -1
  118. package/test/cases/components/form/select.mjs +1 -1
  119. package/test/cases/components/form/tree-select.mjs +1 -1
  120. package/test/cases/data/transformer.mjs +2 -2
  121. package/test/cases/dom/updater.mjs +67 -46
  122. package/test/cases/monster.mjs +1 -1
  123. package/test/web/test.html +2 -2
  124. package/test/web/tests.js +18 -13
@@ -4,7 +4,11 @@
4
4
  */
5
5
 
6
6
  import { instanceSymbol } from "../../constants.mjs";
7
- import { findElementWithIdUpwards } from "../../dom/util.mjs";
7
+ import { findTargetElementFromEvent } from "../../dom/events.mjs";
8
+ import {
9
+ findElementWithIdUpwards,
10
+ findElementWithSelectorUpwards,
11
+ } from "../../dom/util.mjs";
8
12
  import {
9
13
  assembleMethodSymbol,
10
14
  CustomElement,
@@ -23,11 +27,20 @@ import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
23
27
  import "../form/message-state-button.mjs";
24
28
  import { Formatter } from "../../text/formatter.mjs";
25
29
  import { generateRangeComparisonExpression } from "../../text/util.mjs";
26
- import { generateUniqueConfigKey } from "../host/util.mjs";
30
+
27
31
  import {
28
32
  parseBracketedKeyValueHash,
29
33
  createBracketedKeyValueHash,
30
34
  } from "../../text/bracketed-key-value-hash.mjs";
35
+ import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
36
+ import { SpaceStyleSheet } from "../stylesheet/space.mjs";
37
+ import { FormStyleSheet } from "../stylesheet/form.mjs";
38
+
39
+ import {
40
+ getStoredFilterConfigKey,
41
+ getFilterConfigKey,
42
+ parseDateInput,
43
+ } from "./filter/util.mjs";
31
44
 
32
45
  import "./filter/select.mjs";
33
46
 
@@ -51,12 +64,32 @@ const searchButtonElementSymbol = Symbol("searchButtonElement");
51
64
  */
52
65
  const resetButtonElementSymbol = Symbol("resetButtonElement");
53
66
 
67
+ /**
68
+ * @private
69
+ * @type {symbol}
70
+ */
71
+ const saveButtonElementSymbol = Symbol("saveButtonElement");
72
+
54
73
  /**
55
74
  * @private
56
75
  * @type {symbol}
57
76
  */
58
77
  const filterControlElementSymbol = Symbol("filterControlElement");
59
78
 
79
+ /**
80
+ * @private
81
+ * @type {symbol}
82
+ */
83
+ const filterSaveActionButtonElementSymbol = Symbol(
84
+ "filterSaveActionButtonElement",
85
+ );
86
+
87
+ /**
88
+ * @private
89
+ * @type {symbol}
90
+ */
91
+ const filterTabElementSymbol = Symbol("filterTabElement");
92
+
60
93
  /**
61
94
  * @private
62
95
  * @type {symbol}
@@ -132,10 +165,13 @@ class Filter extends CustomElement {
132
165
  * @returns {Monster.Components.Datatable.Filter}
133
166
  */
134
167
  showFailureMessage(message) {
135
- this[searchButtonElementSymbol].setState("failed", 10000);
168
+ this[searchButtonElementSymbol].setState(
169
+ "failed",
170
+ this.getOption("timeouts.message", 4000),
171
+ );
136
172
  this[searchButtonElementSymbol]
137
- .setMessage(String(message))
138
- .showMessage(10000);
173
+ .setMessage(message.toString())
174
+ .showMessage(this.getOption("timeouts.message", 4000));
139
175
  return this;
140
176
  }
141
177
 
@@ -154,7 +190,10 @@ class Filter extends CustomElement {
154
190
  * @returns {Monster.Components.Datatable.Filter}
155
191
  */
156
192
  showSuccess() {
157
- this[searchButtonElementSymbol].setState("successful", 5000);
193
+ this[searchButtonElementSymbol].setState(
194
+ "successful",
195
+ this.getOption("timeouts.message", 4000),
196
+ );
158
197
  return this;
159
198
  }
160
199
 
@@ -175,17 +214,34 @@ class Filter extends CustomElement {
175
214
  * @property {string} defaultQuery Default query
176
215
  */
177
216
  get defaults() {
178
- const obj = Object.assign({}, super.defaults, {
217
+ return Object.assign({}, super.defaults, {
179
218
  templates: {
180
219
  main: getTemplate(),
181
220
  },
221
+
182
222
  labels: {
183
223
  search: "Search",
184
224
  reset: "Reset",
225
+ save: "Save",
226
+ "filter-name": "Filter name",
185
227
  "empty-query-and-no-default": "Please select a filter",
186
228
  "query-not-changed": "The query has not changed",
187
229
  },
188
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
+
189
245
  queries: {
190
246
  wrap: (value, definition) => {
191
247
  return value;
@@ -201,8 +257,6 @@ class Filter extends CustomElement {
201
257
  query: "",
202
258
  defaultQuery: "",
203
259
  });
204
-
205
- return obj;
206
260
  }
207
261
 
208
262
  /**
@@ -217,6 +271,15 @@ class Filter extends CustomElement {
217
271
  * @return {FilterButton}
218
272
  */
219
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
+
220
283
  super[assembleMethodSymbol]();
221
284
 
222
285
  initControlReferences.call(this);
@@ -226,9 +289,10 @@ class Filter extends CustomElement {
226
289
  .call(this)
227
290
  .then(() => {
228
291
  initFilter.call(this);
292
+ updateFilterTabs.call(this);
229
293
  })
230
294
  .catch((error) => {
231
- console.error(error);
295
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message);
232
296
  });
233
297
  }
234
298
 
@@ -260,7 +324,7 @@ class Filter extends CustomElement {
260
324
  * @return {Array<CSSStyleSheet>}
261
325
  */
262
326
  static getCSSStyleSheet() {
263
- return [FilterStyleSheet];
327
+ return [FilterStyleSheet, FormStyleSheet, ThemeStyleSheet, SpaceStyleSheet];
264
328
  }
265
329
  }
266
330
 
@@ -285,6 +349,20 @@ function initControlReferences() {
285
349
  this[resetButtonElementSymbol] = this.shadowRoot.querySelector(
286
350
  "[data-monster-role=reset-button]",
287
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
+
288
366
  return this;
289
367
  }
290
368
 
@@ -329,7 +407,8 @@ function initFilter() {
329
407
  const v = escapeAttributeValue(valuesFromHash[element.id]);
330
408
  const searchInput = element.firstElementChild;
331
409
  try {
332
- searchInput.value = valuesFromHash[element.id];
410
+ // searchInput.value = valuesFromHash[element.id];
411
+ searchInput.value = v; //valuesFromHash[element.id];
333
412
  } catch (error) {}
334
413
  }
335
414
 
@@ -349,6 +428,7 @@ function initFilter() {
349
428
  "options",
350
429
  this[settingsSymbol].getOptions(),
351
430
  );
431
+
352
432
  setTimeout(() => {
353
433
  this[filterSelectElementSymbol].value = this[settingsSymbol].getSelected();
354
434
  }, 10);
@@ -360,6 +440,11 @@ function initFilter() {
360
440
  * @returns {*}
361
441
  */
362
442
  function escapeAttributeValue(input) {
443
+ if (input === undefined || input === null) {
444
+ debugger;
445
+ return input;
446
+ }
447
+
363
448
  return input
364
449
  .replace(/&/g, "&amp;")
365
450
  .replace(/"/g, "&quot;")
@@ -374,10 +459,9 @@ function escapeAttributeValue(input) {
374
459
  * @returns {boolean}
375
460
  */
376
461
  function getVisibilityFromSlotAttribute(element) {
377
- return element.hasAttribute("slot") &&
378
- element.getAttribute("slot") === "hidden"
379
- ? false
380
- : true;
462
+ return !(
463
+ element.hasAttribute("slot") && element.getAttribute("slot") === "hidden"
464
+ );
381
465
  }
382
466
 
383
467
  /**
@@ -405,7 +489,7 @@ function initEventHandler() {
405
489
  if (self[filterSelectElementSymbol]) {
406
490
  self[filterSelectElementSymbol].addEventListener(
407
491
  "monster-selection-cleared",
408
- function (event) {
492
+ function () {
409
493
  const settings = self[settingsSymbol].getOptions();
410
494
 
411
495
  for (const setting of settings) {
@@ -443,8 +527,109 @@ function initEventHandler() {
443
527
  );
444
528
  }
445
529
 
530
+ if (self[filterSaveActionButtonElementSymbol]) {
531
+ self[filterSaveActionButtonElementSymbol].setOption(
532
+ "actions.click",
533
+ function (event) {
534
+ const button = findTargetElementFromEvent(
535
+ event,
536
+ "data-monster-role",
537
+ "save-action-button",
538
+ );
539
+ const form = button.closest("[data-monster-role=form]");
540
+
541
+ if (!form) {
542
+ button.setState("failed", self.getOption("timeouts.message", 4000));
543
+ return;
544
+ }
545
+
546
+ const input = form.querySelector("input[name=filter-name]");
547
+ if (!input) {
548
+ button.setState("failed", self.getOption("timeouts.message", 4000));
549
+ return;
550
+ }
551
+
552
+ const name = input.value;
553
+ if (!name) {
554
+ button.setState("failed", self.getOption("timeouts.message", 4000));
555
+ button.setMessage("Please enter a name").showMessage();
556
+ return;
557
+ }
558
+
559
+ doSearch
560
+ .call(self, { showEffect: false })
561
+ .then(() => {
562
+ const configKey = getStoredFilterConfigKey.call(self);
563
+ const host = getDocument().querySelector("monster-host");
564
+ if (!host) {
565
+ return;
566
+ }
567
+
568
+ const query = self.getOption("query");
569
+ if (!query) {
570
+ button.setState(
571
+ "failed",
572
+ self.getOption(
573
+ "timeouts.message",
574
+ self.getOption("timeouts.message", 4000),
575
+ ),
576
+ );
577
+ button
578
+ .setMessage("No query found")
579
+ .showMessage(self.getOption("timeouts.message", 4000));
580
+ return;
581
+ }
582
+
583
+ host
584
+ .hasConfig(configKey)
585
+ .then((hasConfig) => {
586
+ return new Promise((resolve, reject) => {
587
+ if (hasConfig) {
588
+ host.getConfig(configKey).then(resolve).catch(reject);
589
+ return;
590
+ }
591
+ return resolve({});
592
+ });
593
+ })
594
+ .then((config) => {
595
+ config[name] = query;
596
+ return host.setConfig(configKey, {
597
+ ...config,
598
+ });
599
+ })
600
+ .then(() => {
601
+ button.setState(
602
+ "successful",
603
+ self.getOption("timeouts.message", 4000),
604
+ );
605
+ updateFilterTabs.call(self);
606
+ })
607
+ .catch((error) => {
608
+ button.setState(
609
+ "failed",
610
+ self.getOption("timeouts.message", 4000),
611
+ );
612
+ button
613
+ .setMessage(error.message)
614
+ .showMessage(self.getOption("timeouts.message", 4000));
615
+ });
616
+ })
617
+ .catch((error) => {
618
+ button.setState("failed", self.getOption("timeouts.message", 4000));
619
+ const msg = error.message || error;
620
+ button
621
+ .setMessage(msg)
622
+ .showMessage(self.getOption("timeouts.message", 4000));
623
+ });
624
+ },
625
+ );
626
+ }
627
+
446
628
  self[searchButtonElementSymbol].setOption("actions.click", () => {
447
- doSearch.call(self);
629
+ doSearch
630
+ .call(self)
631
+ .then(() => {})
632
+ .catch((error) => {});
448
633
  });
449
634
 
450
635
  // the reset button should reset the filter and the search query
@@ -492,46 +677,192 @@ function initEventHandler() {
492
677
  doSearch.call(self, { showEffect: false });
493
678
  }
494
679
  });
680
+
681
+ // tabs
682
+ const element = this[filterTabElementSymbol];
683
+ if (element) {
684
+ initTabEvents.call(this);
685
+ }
686
+ }
687
+
688
+ function initTabEvents() {
689
+ this[filterTabElementSymbol].addEventListener(
690
+ "monster-tab-changed",
691
+ (event) => {
692
+ const query = event?.detail?.data?.["data-monster-query"];
693
+ this.setOption("query", query);
694
+ },
695
+ );
696
+
697
+ this[filterTabElementSymbol].addEventListener(
698
+ "monster-tab-remove",
699
+ (event) => {
700
+ const labels = [];
701
+ const buttons = this[filterTabElementSymbol].getOption("buttons");
702
+
703
+ const keys = ["popper", "standard"];
704
+ for (let i = 0; i < keys.length; i++) {
705
+ const key = keys[i];
706
+
707
+ for (const button of buttons[key]) {
708
+ if (button.label !== event.detail.label) {
709
+ labels.push(button.label);
710
+ }
711
+ }
712
+ }
713
+
714
+ const document = getDocument();
715
+ const host = document.querySelector("monster-host");
716
+ if (!(host && this.id)) {
717
+ return;
718
+ }
719
+
720
+ const configKey = getStoredFilterConfigKey.call(this);
721
+ host
722
+ .hasConfig(configKey)
723
+ .then((hasConfig) => {
724
+ if (!hasConfig) {
725
+ return;
726
+ }
727
+
728
+ return host.getConfig(configKey);
729
+ })
730
+ .then((config) => {
731
+ for (const [name, query] of Object.entries(config)) {
732
+ if (labels.includes(name)) {
733
+ continue;
734
+ }
735
+
736
+ delete config[name];
737
+ }
738
+
739
+ return host.setConfig(configKey, {
740
+ ...config,
741
+ });
742
+ });
743
+ },
744
+ );
495
745
  }
496
746
 
497
747
  /**
498
748
  * @private
499
749
  */
500
- function doSearch({ showEffect } = { showEffect: true }) {
750
+ function updateFilterTabs() {
751
+ const element = this[filterTabElementSymbol];
752
+ if (!element) {
753
+ return;
754
+ }
755
+
756
+ const document = getDocument();
757
+ const host = document.querySelector("monster-host");
758
+ if (!(host && this.id)) {
759
+ return;
760
+ }
761
+
762
+ const configKey = getStoredFilterConfigKey.call(this);
763
+ host
764
+ .hasConfig(configKey)
765
+ .then((hasConfig) => {
766
+ if (!hasConfig) {
767
+ return;
768
+ }
769
+
770
+ return host.getConfig(configKey);
771
+ })
772
+ .then((config) => {
773
+ for (const [name, query] of Object.entries(config)) {
774
+ const found = element.querySelector(
775
+ `[data-monster-button-label="${name}"]`,
776
+ );
777
+ if (found) {
778
+ continue;
779
+ }
780
+
781
+ if (query === undefined || query === null) {
782
+ continue;
783
+ }
784
+
785
+ const escapedQuery = escapeAttributeValue(query);
786
+
787
+ element.insertAdjacentHTML(
788
+ "beforeend",
789
+ `
790
+ <div data-monster-button-label="${name}"
791
+ data-monster-removable="true"
792
+ data-monster-query="${escapedQuery}" data-monster-role="filter-tab" >
793
+ </div>`,
794
+ );
795
+ }
796
+ })
797
+ .catch((error) => {
798
+ if (error instanceof Error) {
799
+ addAttributeToken(
800
+ this,
801
+ ATTRIBUTE_ERRORMESSAGE,
802
+ error.message + " " + error.stack,
803
+ );
804
+ } else {
805
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error + "");
806
+ }
807
+ });
808
+ }
809
+
810
+ /**
811
+ * @private
812
+ * @param showEffect
813
+ * @returns {Promise<*>}
814
+ */
815
+ function doSearch({ showEffect } = { showEffect: true }) {;
816
+
501
817
  this.resetFailureMessage();
502
818
 
503
819
  if (showEffect) {
504
- this[searchButtonElementSymbol].setState("activity", 10000);
820
+ this[searchButtonElementSymbol].setState(
821
+ "activity",
822
+ this.getOption("timeouts.message", 4000),
823
+ );
505
824
  }
506
825
 
507
- collectSearchQueries
826
+ return collectSearchQueries
508
827
  .call(this)
509
828
  .then((query) => {
510
829
  const buildQuery = buildSearchQuery.call(this, query);
511
830
  if (buildQuery === "" && !this.getOption("defaultQuery")) {
512
- this[searchButtonElementSymbol].removeState();
513
- this[searchButtonElementSymbol]
514
- .setMessage(this.getOption("labels.empty-query-and-no-default"))
515
- .showMessage(5000);
516
- return;
831
+ const msg = this.getOption("labels.empty-query-and-no-default");
832
+
833
+ if (showEffect) {
834
+ this[searchButtonElementSymbol].removeState();
835
+ this[searchButtonElementSymbol]
836
+ .setMessage(msg)
837
+ .showMessage(this.getOption("timeouts.message", 4000));
838
+ }
839
+
840
+ throw new Error(msg);
517
841
  }
518
842
 
519
843
  if (buildQuery === this.getOption("query")) {
520
- this[searchButtonElementSymbol].removeState();
521
- this[searchButtonElementSymbol]
522
- .setMessage(this.getOption("labels.query-not-changed"))
523
- .showMessage(5000);
524
- return;
844
+ const msg = this.getOption("labels.query-not-changed");
845
+
846
+ if (showEffect) {
847
+ this[searchButtonElementSymbol].removeState();
848
+ this[searchButtonElementSymbol]
849
+ .setMessage(msg)
850
+ .showMessage(this.getOption("timeouts.message", 4000));
851
+ }
852
+
853
+ throw new Error(msg);
525
854
  }
526
855
 
527
856
  if (showEffect) {
528
- this[searchButtonElementSymbol].setState("activity", 2000);
857
+ this[searchButtonElementSymbol].setState(
858
+ "activity",
859
+ this.getOption("timeouts.message", 4000),
860
+ );
529
861
  }
530
862
 
531
863
  this.setOption("query", buildSearchQuery.call(this, query));
532
864
  })
533
865
  .catch((error) => {
534
- console.error(error);
535
866
  if (error instanceof Error) {
536
867
  addAttributeToken(
537
868
  this,
@@ -542,15 +873,22 @@ function doSearch({ showEffect } = { showEffect: true }) {
542
873
  addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
543
874
  }
544
875
 
545
- this[searchButtonElementSymbol].setState("failed", 10000);
546
- this[searchButtonElementSymbol].setMessage(String(error)).showMessage();
876
+ if (showEffect) {
877
+ this[searchButtonElementSymbol].setState(
878
+ "failed",
879
+ this.getOption("timeouts.message", 4000),
880
+ );
881
+ this[searchButtonElementSymbol].setMessage(error.message).showMessage();
882
+ }
883
+
884
+ return Promise.reject(error);
547
885
  });
548
886
  }
549
887
 
550
888
  /**
551
889
  * @private
552
890
  * @param queries
553
- * @returns {string|undefind}
891
+ * @returns {*|string}
554
892
  */
555
893
  function buildSearchQuery(queries) {
556
894
  if (!isArray(queries) || queries.length === 0) {
@@ -581,6 +919,8 @@ function collectSearchQueries() {
581
919
  const query = [];
582
920
  const wrapCallback = this.getOption("queries.wrap");
583
921
 
922
+ let hasNoIdError = false;
923
+
584
924
  getSlottedElements
585
925
  .call(this, "label[data-monster-label]")
586
926
  .forEach((element) => {
@@ -591,7 +931,7 @@ function collectSearchQueries() {
591
931
 
592
932
  const id = element.id;
593
933
  if (!id) {
594
- reject(new Error("no filter id is defined"));
934
+ hasNoIdError = true;
595
935
  return;
596
936
  }
597
937
 
@@ -659,132 +999,16 @@ function collectSearchQueries() {
659
999
  }
660
1000
  });
661
1001
 
1002
+ if (hasNoIdError) {
1003
+ reject(new Error("some or all filter elements have no id"));
1004
+ return;
1005
+ }
1006
+
662
1007
  getGlobal().location.hash = createBracketedKeyValueHash(currentHash);
663
1008
  resolve(query);
664
1009
  });
665
1010
  }
666
1011
 
667
- /**
668
- * @private
669
- * @param {String} str
670
- * @param {String} field
671
- * @returns {String}
672
- * @throws {Error} if no field is defined
673
- */
674
- function parseDateInput(str, field) {
675
- if (!str) {
676
- return "";
677
- }
678
-
679
- if (!field) {
680
- throw new Error("no field is defined");
681
- }
682
-
683
- // Define the supported formats
684
- //let formats = ['DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', 'YYYY/MM/DD', 'DD.MM.YYYY'];
685
- const formats = ["YYYY-MM-DD"];
686
- // Determine the current date format of the localization
687
- const currentDateFormat = new Intl.DateTimeFormat()
688
- .format(new Date())
689
- .replace(/\d/g, "D");
690
- // formats.push(currentDateFormat);
691
-
692
- // Run through the supported formats and try to parse the date
693
- for (let i = 0; i < formats.length; i++) {
694
- const format = formats[i];
695
- // Replace the corresponding placeholders in the format string with regular expressions
696
-
697
- try {
698
- const pattern = format
699
- .replace("DD", "\\d{2}")
700
- .replace("MM", "\\d{2}")
701
- .replace("YYYY", "\\d{4}");
702
- const rangePattern =
703
- "(?<from>" + pattern + ")\\s*-\\s*(?<to>" + pattern + ")";
704
-
705
- const rangeRegex = new RegExp("^" + rangePattern + "$", "g");
706
-
707
- if (rangeRegex.test(str)) {
708
- rangeRegex.lastIndex = 0;
709
-
710
- const rangeResult = rangeRegex.exec(str);
711
-
712
- if (!rangeResult) {
713
- continue;
714
- }
715
-
716
- const from = rangeResult?.groups?.from;
717
- const to = rangeResult?.groups?.to;
718
-
719
- if (from && to) {
720
- return (
721
- "(" +
722
- field +
723
- ">='" +
724
- from +
725
- " 00:00:00' AND " +
726
- field +
727
- "<='" +
728
- to +
729
- " 23:59:59')"
730
- );
731
- }
732
-
733
- if (from) {
734
- return "(" + field + ">='" + from + " 00:00:00')";
735
- } else if (to) {
736
- return "(" + field + "<='" + to + "' 23:59:59')";
737
- }
738
-
739
- return "false";
740
- }
741
-
742
- const prefix = str.substring(0, 1) === "-";
743
- const suffix = str.substring(str.length - 1, str.length) === "-";
744
-
745
- if (prefix) {
746
- str = str.substring(1, str.length);
747
- } else if (suffix) {
748
- str = str.substring(0, str.length - 1);
749
- }
750
-
751
- const regex = new RegExp("^(?<date>" + pattern + ")$", "g");
752
- if (regex.test(str)) {
753
- regex.lastIndex = 0;
754
- const result = regex.exec(str);
755
-
756
- if (!result) {
757
- continue;
758
- }
759
-
760
- const date = result?.groups?.date;
761
- if (date) {
762
- if (prefix) {
763
- return "(" + field + "<='" + date + " 23:59:59')";
764
- } else if (suffix) {
765
- return "(" + field + ">='" + date + "' 00:00:00')";
766
- }
767
- return (
768
- "(" +
769
- field +
770
- ">='" +
771
- date +
772
- " 00:00:00' AND " +
773
- field +
774
- "<='" +
775
- date +
776
- " 23:59:59')"
777
- );
778
- } else {
779
- return "false";
780
- }
781
- }
782
- } catch (e) {}
783
- }
784
-
785
- return "false";
786
- }
787
-
788
1012
  /**
789
1013
  * @private
790
1014
  * @param label
@@ -796,7 +1020,9 @@ function getControlValuesFromLabel(label) {
796
1020
  if (foundControl) {
797
1021
  if (foundControl.tagName === "INPUT") {
798
1022
  if (foundControl.type === "checkbox") {
799
- const checkedControls = label.querySelectorAll(`${control}:checked`);
1023
+ const checkedControls = label.querySelectorAll(
1024
+ `${foundControl}:checked`,
1025
+ );
800
1026
  const values = [];
801
1027
 
802
1028
  checkedControls.forEach((checkedControl) => {
@@ -805,7 +1031,7 @@ function getControlValuesFromLabel(label) {
805
1031
 
806
1032
  return values;
807
1033
  } else if (foundControl.type === "radio") {
808
- const checkedControl = label.querySelector(`${control}:checked`);
1034
+ const checkedControl = label.querySelector(`${foundControl}:checked`);
809
1035
 
810
1036
  if (checkedControl) {
811
1037
  return checkedControl.value;
@@ -823,14 +1049,6 @@ function getControlValuesFromLabel(label) {
823
1049
  return null;
824
1050
  }
825
1051
 
826
- /**
827
- * @private
828
- * @returns {string}
829
- */
830
- function getFilterConfigKey() {
831
- return generateUniqueConfigKey("datatable", this?.id, "filter");
832
- }
833
-
834
1052
  /**
835
1053
  * @private
836
1054
  * @returns {Promise<unknown>}
@@ -849,7 +1067,7 @@ function initFromConfig() {
849
1067
  host
850
1068
  .getConfig(configKey)
851
1069
  .then((config) => {
852
- if (config) {
1070
+ if (config && isObject(config)) {
853
1071
  this[settingsSymbol].setOptions(config);
854
1072
  }
855
1073
  resolve();
@@ -860,7 +1078,17 @@ function initFromConfig() {
860
1078
  return;
861
1079
  }
862
1080
 
863
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
1081
+ // config not written
1082
+ if (error?.message?.match(/is not defined/)) {
1083
+ resolve();
1084
+ return;
1085
+ }
1086
+
1087
+ addAttributeToken(
1088
+ this,
1089
+ ATTRIBUTE_ERRORMESSAGE,
1090
+ error?.message || error,
1091
+ );
864
1092
  reject(error);
865
1093
  });
866
1094
  });
@@ -880,7 +1108,7 @@ function updateConfig() {
880
1108
  try {
881
1109
  host.setConfig(configKey, this[settingsSymbol].getOptions());
882
1110
  } catch (error) {
883
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
1111
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, error?.message || error);
884
1112
  }
885
1113
  }
886
1114
 
@@ -898,15 +1126,31 @@ function getTemplate() {
898
1126
  <slot name="hidden"></slot>
899
1127
  </div>
900
1128
  <div data-monster-role="select-and-search">
1129
+ <monster-message-state-button data-monster-role="search-button" class="stretched-control"
1130
+ data-monster-replace="path:labels.search">
1131
+ </monster-message-state-button>
901
1132
  <monster-select class="stretched-control"
902
1133
  data-monster-selected-template="summary"
903
1134
  data-monster-option-type="checkbox"
904
1135
  data-monster-option-filter-mode="options"
905
1136
  data-monster-option-filter-position="popper"
906
1137
  data-monster-role="filter-select"></monster-select>
907
- <monster-message-state-button data-monster-role="search-button" class="stretched-control"
908
- data-monster-replace="path:labels.search">
909
- </monster-message-state-button>
1138
+ <monster-popper-button data-monster-role="save-button" class="stretched-control"
1139
+ data-monster-attributes="data-monster-visible path:storedConfig.enabled">
1140
+ <div slot="button">\${filter-save-label}</div>
1141
+ <div class="monster-form" data-monster-role="form">
1142
+
1143
+ <label for="filter-name">\${filter-name-label}
1144
+ <input name="filter-name"
1145
+ type="search"
1146
+ class="monster-margin-bottom-5"></label>
1147
+ <monster-message-state-button
1148
+ data-monster-role="save-action-button"
1149
+ data-monster-option-labels-button="\${filter-save-label}">
1150
+ </monster-message-state-button>
1151
+
1152
+ </div>
1153
+ </monster-popper-button>
910
1154
  <monster-button data-monster-role="reset-button" class="stretched-control"
911
1155
  data-monster-replace="path:labels.reset">
912
1156
  </monster-button>