@schukai/monster 3.53.0 → 3.55.0
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.
- package/CHANGELOG.md +16 -0
- package/package.json +1 -1
- package/source/components/datatable/datasource/rest.mjs +358 -309
- package/source/components/datatable/datatable/header.mjs +8 -0
- package/source/components/datatable/datatable.mjs +606 -557
- package/source/components/datatable/embedded-pagination.mjs +50 -62
- package/source/components/datatable/filter/util.mjs +122 -0
- package/source/components/datatable/filter.mjs +893 -708
- package/source/components/datatable/pagination.mjs +335 -310
- package/source/components/datatable/status.mjs +248 -0
- package/source/components/datatable/style/datatable.pcss +1 -0
- package/source/components/datatable/style/embedded-pagination.pcss +59 -2
- package/source/components/datatable/style/filter.pcss +4 -0
- package/source/components/datatable/style/pagination.pcss +28 -4
- package/source/components/datatable/style/status.pcss +42 -0
- package/source/components/datatable/stylesheet/column-bar.mjs +1 -1
- package/source/components/datatable/stylesheet/datatable.mjs +1 -1
- package/source/components/datatable/stylesheet/filter-button.mjs +1 -1
- package/source/components/datatable/stylesheet/filter.mjs +1 -1
- package/source/components/datatable/stylesheet/pagination.mjs +1 -1
- package/source/components/datatable/stylesheet/status.mjs +27 -0
- package/source/components/form/action-button.mjs +1 -1
- package/source/components/form/api-button.mjs +1 -1
- package/source/components/form/button-bar.mjs +1 -1
- package/source/components/form/button.mjs +1 -1
- package/source/components/form/confirm-button.mjs +1 -1
- package/source/components/form/context-error.mjs +275 -0
- package/source/components/form/context-help.mjs +5 -5
- package/source/components/form/form.mjs +2 -2
- package/source/components/form/message-state-button.mjs +2 -2
- package/source/components/form/popper-button.mjs +7 -4
- package/source/components/form/popper.mjs +317 -309
- package/source/components/form/reload.mjs +1 -1
- package/source/components/form/select.mjs +9 -3
- package/source/components/form/shadow-reload.mjs +1 -1
- package/source/components/form/state-button.mjs +2 -1
- package/source/components/form/style/context-error.pcss +32 -0
- package/source/components/form/style/context-help.pcss +22 -5
- package/source/components/form/stylesheet/context-error.mjs +27 -0
- package/source/components/form/stylesheet/context-help.mjs +1 -1
- package/source/components/form/stylesheet/select.mjs +1 -1
- package/source/components/form/stylesheet/tabs.mjs +1 -1
- package/source/components/form/tabs.mjs +757 -707
- package/source/components/form/template.mjs +1 -1
- package/source/components/form/tree-select.mjs +1 -1
- package/source/components/host/collapse.mjs +22 -5
- package/source/components/host/config-manager.mjs +39 -2
- package/source/components/host/host.mjs +14 -0
- package/source/components/host/stylesheet/call-button.mjs +1 -1
- package/source/components/host/stylesheet/overlay.mjs +1 -1
- package/source/components/host/stylesheet/toggle-button.mjs +1 -1
- package/source/components/host/util.mjs +6 -1
- package/source/components/notify/stylesheet/message.mjs +1 -1
- package/source/components/stylesheet/icons.mjs +1 -1
- package/source/data/transformer.mjs +39 -42
- package/source/dom/customelement.mjs +1 -1
- package/source/dom/updater.mjs +700 -688
- package/source/dom/util.mjs +42 -0
- package/source/i18n/providers/embed.mjs +3 -3
- package/source/monster.mjs +6 -0
- package/source/text/formatter.mjs +2 -2
- package/source/types/observer.mjs +1 -1
- package/source/types/version.mjs +1 -1
- package/source/util/sleep.mjs +18 -0
- package/test/cases/components/form/button.mjs +2 -1
- package/test/cases/components/form/select.mjs +1 -1
- package/test/cases/components/form/tree-select.mjs +1 -1
- package/test/cases/data/transformer.mjs +2 -2
- package/test/cases/dom/updater.mjs +67 -46
- package/test/cases/monster.mjs +1 -1
- package/test/web/test.html +2 -2
- package/test/web/tests.js +18 -13
|
@@ -3,35 +3,41 @@
|
|
|
3
3
|
* SPDX-License-Identifier: AGPL-3.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import {instanceSymbol} from "../../constants.mjs";
|
|
7
|
+
import {findTargetElementFromEvent} from "../../dom/events.mjs";
|
|
8
|
+
import {findElementWithIdUpwards, findElementWithSelectorUpwards} from "../../dom/util.mjs";
|
|
8
9
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
assembleMethodSymbol,
|
|
11
|
+
CustomElement,
|
|
12
|
+
getSlottedElements,
|
|
13
|
+
registerCustomElement,
|
|
13
14
|
} from "../../dom/customelement.mjs";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
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";
|
|
23
24
|
import "../form/message-state-button.mjs";
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
25
|
+
import {Formatter} from "../../text/formatter.mjs";
|
|
26
|
+
import {generateRangeComparisonExpression} from "../../text/util.mjs";
|
|
27
|
+
|
|
27
28
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
parseBracketedKeyValueHash,
|
|
30
|
+
createBracketedKeyValueHash,
|
|
30
31
|
} 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
|
+
|
|
36
|
+
import {getStoredFilterConfigKey, getFilterConfigKey, parseDateInput} from "./filter/util.mjs";
|
|
31
37
|
|
|
32
38
|
import "./filter/select.mjs";
|
|
33
39
|
|
|
34
|
-
export {
|
|
40
|
+
export {Filter};
|
|
35
41
|
|
|
36
42
|
/**
|
|
37
43
|
* @private
|
|
@@ -51,12 +57,30 @@ const searchButtonElementSymbol = Symbol("searchButtonElement");
|
|
|
51
57
|
*/
|
|
52
58
|
const resetButtonElementSymbol = Symbol("resetButtonElement");
|
|
53
59
|
|
|
60
|
+
/**
|
|
61
|
+
* @private
|
|
62
|
+
* @type {symbol}
|
|
63
|
+
*/
|
|
64
|
+
const saveButtonElementSymbol = Symbol("saveButtonElement");
|
|
65
|
+
|
|
54
66
|
/**
|
|
55
67
|
* @private
|
|
56
68
|
* @type {symbol}
|
|
57
69
|
*/
|
|
58
70
|
const filterControlElementSymbol = Symbol("filterControlElement");
|
|
59
71
|
|
|
72
|
+
/**
|
|
73
|
+
* @private
|
|
74
|
+
* @type {symbol}
|
|
75
|
+
*/
|
|
76
|
+
const filterSaveActionButtonElementSymbol = Symbol("filterSaveActionButtonElement");
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @private
|
|
80
|
+
* @type {symbol}
|
|
81
|
+
*/
|
|
82
|
+
const filterTabElementSymbol = Symbol("filterTabElement");
|
|
83
|
+
|
|
60
84
|
/**
|
|
61
85
|
* @private
|
|
62
86
|
* @type {symbol}
|
|
@@ -110,158 +134,180 @@ const settingsSymbol = Symbol("settings");
|
|
|
110
134
|
* @summary A data set
|
|
111
135
|
*/
|
|
112
136
|
class Filter extends CustomElement {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
+
}
|
|
265
311
|
}
|
|
266
312
|
|
|
267
313
|
/**
|
|
@@ -269,23 +315,35 @@ class Filter extends CustomElement {
|
|
|
269
315
|
* @return {FilterButton}
|
|
270
316
|
*/
|
|
271
317
|
function initControlReferences() {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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;
|
|
289
347
|
}
|
|
290
348
|
|
|
291
349
|
/**
|
|
@@ -293,65 +351,69 @@ function initControlReferences() {
|
|
|
293
351
|
* @throws {Error} no filter label is defined
|
|
294
352
|
*/
|
|
295
353
|
function initFilter() {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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);
|
|
355
417
|
}
|
|
356
418
|
|
|
357
419
|
/**
|
|
@@ -360,12 +422,18 @@ function initFilter() {
|
|
|
360
422
|
* @returns {*}
|
|
361
423
|
*/
|
|
362
424
|
function escapeAttributeValue(input) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
425
|
+
|
|
426
|
+
if (input === undefined || input === null) {
|
|
427
|
+
debugger
|
|
428
|
+
return input;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return input
|
|
432
|
+
.replace(/&/g, "&")
|
|
433
|
+
.replace(/"/g, """)
|
|
434
|
+
.replace(/'/g, "'")
|
|
435
|
+
.replace(/</g, "<")
|
|
436
|
+
.replace(/>/g, ">");
|
|
369
437
|
}
|
|
370
438
|
|
|
371
439
|
/**
|
|
@@ -374,10 +442,7 @@ function escapeAttributeValue(input) {
|
|
|
374
442
|
* @returns {boolean}
|
|
375
443
|
*/
|
|
376
444
|
function getVisibilityFromSlotAttribute(element) {
|
|
377
|
-
|
|
378
|
-
element.getAttribute("slot") === "hidden"
|
|
379
|
-
? false
|
|
380
|
-
: true;
|
|
445
|
+
return !(element.hasAttribute("slot") && element.getAttribute("slot") === "hidden");
|
|
381
446
|
}
|
|
382
447
|
|
|
383
448
|
/**
|
|
@@ -386,188 +451,404 @@ function getVisibilityFromSlotAttribute(element) {
|
|
|
386
451
|
* @param {boolean} visible
|
|
387
452
|
*/
|
|
388
453
|
function setSlotAttribute(element, visible) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
454
|
+
if (visible) {
|
|
455
|
+
element.removeAttribute("slot");
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
393
458
|
|
|
394
|
-
|
|
459
|
+
element.setAttribute("slot", "hidden");
|
|
395
460
|
}
|
|
396
461
|
|
|
397
462
|
/**
|
|
398
463
|
* @private
|
|
399
464
|
*/
|
|
400
465
|
function initEventHandler() {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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
|
+
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
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
|
+
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* @private
|
|
711
|
+
*/
|
|
712
|
+
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", `
|
|
748
|
+
<div data-monster-button-label="${name}"
|
|
749
|
+
data-monster-removable="true"
|
|
750
|
+
data-monster-query="${escapedQuery}" data-monster-role="filter-tab" >
|
|
751
|
+
</div>`);
|
|
752
|
+
}
|
|
753
|
+
})
|
|
495
754
|
}
|
|
496
755
|
|
|
497
756
|
/**
|
|
498
757
|
* @private
|
|
758
|
+
* @param showEffect
|
|
759
|
+
* @returns {Promise<*>}
|
|
499
760
|
*/
|
|
500
|
-
function doSearch({
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
+
});
|
|
548
829
|
}
|
|
549
830
|
|
|
550
831
|
/**
|
|
551
832
|
* @private
|
|
552
833
|
* @param queries
|
|
553
|
-
* @returns {string
|
|
834
|
+
* @returns {*|string}
|
|
554
835
|
*/
|
|
555
836
|
function buildSearchQuery(queries) {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
837
|
+
if (!isArray(queries) || queries.length === 0) {
|
|
838
|
+
return this.getOption("defaultQuery");
|
|
839
|
+
}
|
|
559
840
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
841
|
+
const joinCallback = this.getOption("queries.join");
|
|
842
|
+
if (isFunction(joinCallback)) {
|
|
843
|
+
return joinCallback(queries);
|
|
844
|
+
}
|
|
564
845
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
846
|
+
const q = queries.join(" ").trim();
|
|
847
|
+
if (q.length === 0) {
|
|
848
|
+
return this.getOption("defaultQuery");
|
|
849
|
+
}
|
|
569
850
|
|
|
570
|
-
|
|
851
|
+
return q;
|
|
571
852
|
}
|
|
572
853
|
|
|
573
854
|
/**
|
|
@@ -575,215 +856,103 @@ function buildSearchQuery(queries) {
|
|
|
575
856
|
* @returns {Promise<unknown>}
|
|
576
857
|
*/
|
|
577
858
|
function collectSearchQueries() {
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
+
});
|
|
665
954
|
}
|
|
666
955
|
|
|
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
956
|
|
|
788
957
|
/**
|
|
789
958
|
* @private
|
|
@@ -791,97 +960,96 @@ function parseDateInput(str, field) {
|
|
|
791
960
|
* @returns {null|Array|undefined|string}
|
|
792
961
|
*/
|
|
793
962
|
function getControlValuesFromLabel(label) {
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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;
|
|
824
993
|
}
|
|
825
994
|
|
|
826
|
-
/**
|
|
827
|
-
* @private
|
|
828
|
-
* @returns {string}
|
|
829
|
-
*/
|
|
830
|
-
function getFilterConfigKey() {
|
|
831
|
-
return generateUniqueConfigKey("datatable", this?.id, "filter");
|
|
832
|
-
}
|
|
833
995
|
|
|
834
996
|
/**
|
|
835
997
|
* @private
|
|
836
998
|
* @returns {Promise<unknown>}
|
|
837
999
|
*/
|
|
838
1000
|
function initFromConfig() {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
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
|
+
});
|
|
867
1035
|
}
|
|
868
1036
|
|
|
869
1037
|
/**
|
|
870
1038
|
* @private
|
|
871
1039
|
*/
|
|
872
1040
|
function updateConfig() {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
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
|
+
}
|
|
885
1053
|
}
|
|
886
1054
|
|
|
887
1055
|
/**
|
|
@@ -889,8 +1057,9 @@ function updateConfig() {
|
|
|
889
1057
|
* @return {string}
|
|
890
1058
|
*/
|
|
891
1059
|
function getTemplate() {
|
|
892
|
-
|
|
893
|
-
|
|
1060
|
+
|
|
1061
|
+
// language=HTML
|
|
1062
|
+
return `
|
|
894
1063
|
<div data-monster-role="control" part="control">
|
|
895
1064
|
<div data-monster-role="layout">
|
|
896
1065
|
<div data-monster-role="filter">
|
|
@@ -898,15 +1067,31 @@ function getTemplate() {
|
|
|
898
1067
|
<slot name="hidden"></slot>
|
|
899
1068
|
</div>
|
|
900
1069
|
<div data-monster-role="select-and-search">
|
|
1070
|
+
<monster-message-state-button data-monster-role="search-button" class="stretched-control"
|
|
1071
|
+
data-monster-replace="path:labels.search">
|
|
1072
|
+
</monster-message-state-button>
|
|
901
1073
|
<monster-select class="stretched-control"
|
|
902
1074
|
data-monster-selected-template="summary"
|
|
903
1075
|
data-monster-option-type="checkbox"
|
|
904
1076
|
data-monster-option-filter-mode="options"
|
|
905
1077
|
data-monster-option-filter-position="popper"
|
|
906
1078
|
data-monster-role="filter-select"></monster-select>
|
|
907
|
-
<monster-
|
|
908
|
-
|
|
909
|
-
|
|
1079
|
+
<monster-popper-button data-monster-role="save-button" class="stretched-control"
|
|
1080
|
+
data-monster-attributes="data-monster-visible path:storedConfig.enabled">
|
|
1081
|
+
<div slot="button">\${filter-save-label}</div>
|
|
1082
|
+
<div class="monster-form" data-monster-role="form">
|
|
1083
|
+
|
|
1084
|
+
<label for="filter-name">\${filter-name-label}
|
|
1085
|
+
<input name="filter-name"
|
|
1086
|
+
type="search"
|
|
1087
|
+
class="monster-margin-bottom-5"></label>
|
|
1088
|
+
<monster-message-state-button
|
|
1089
|
+
data-monster-role="save-action-button"
|
|
1090
|
+
data-monster-option-labels-button="\${filter-save-label}">
|
|
1091
|
+
</monster-message-state-button>
|
|
1092
|
+
|
|
1093
|
+
</div>
|
|
1094
|
+
</monster-popper-button>
|
|
910
1095
|
<monster-button data-monster-role="reset-button" class="stretched-control"
|
|
911
1096
|
data-monster-replace="path:labels.reset">
|
|
912
1097
|
</monster-button>
|