@schukai/monster 3.54.0 → 3.55.1

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