@schukai/monster 3.88.0 → 3.88.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 (56) hide show
  1. package/CHANGELOG.md +65 -46
  2. package/package.json +1 -1
  3. package/source/components/content/stylesheet/copy.mjs +1 -1
  4. package/source/components/datatable/datatable.mjs +922 -914
  5. package/source/components/datatable/filter.mjs +914 -920
  6. package/source/components/datatable/pagination.mjs +1 -3
  7. package/source/components/datatable/stylesheet/change-button.mjs +1 -1
  8. package/source/components/datatable/stylesheet/column-bar.mjs +13 -6
  9. package/source/components/datatable/stylesheet/dataset.mjs +1 -1
  10. package/source/components/datatable/stylesheet/datatable.mjs +13 -6
  11. package/source/components/datatable/stylesheet/embedded-pagination.mjs +13 -6
  12. package/source/components/datatable/stylesheet/filter-select.mjs +1 -1
  13. package/source/components/datatable/stylesheet/filter.mjs +13 -6
  14. package/source/components/datatable/stylesheet/save-button.mjs +1 -1
  15. package/source/components/datatable/stylesheet/status.mjs +1 -1
  16. package/source/components/form/api-button.mjs +420 -415
  17. package/source/components/form/button-bar.mjs +24 -6
  18. package/source/components/form/popper.mjs +1 -1
  19. package/source/components/form/stylesheet/action-button.mjs +13 -6
  20. package/source/components/form/stylesheet/button.mjs +13 -6
  21. package/source/components/form/stylesheet/confirm-button.mjs +13 -6
  22. package/source/components/form/stylesheet/form.mjs +1 -1
  23. package/source/components/form/stylesheet/popper-button.mjs +13 -6
  24. package/source/components/form/stylesheet/select.mjs +13 -6
  25. package/source/components/form/stylesheet/state-button.mjs +13 -6
  26. package/source/components/form/tree-select.mjs +1 -1
  27. package/source/components/host/stylesheet/call-button.mjs +1 -1
  28. package/source/components/host/stylesheet/host.mjs +1 -1
  29. package/source/components/host/stylesheet/overlay.mjs +1 -1
  30. package/source/components/host/stylesheet/toggle-button.mjs +1 -1
  31. package/source/components/host/stylesheet/viewer.mjs +1 -1
  32. package/source/components/layout/stylesheet/collapse.mjs +1 -1
  33. package/source/components/layout/stylesheet/details.mjs +1 -1
  34. package/source/components/layout/stylesheet/slider.mjs +1 -1
  35. package/source/components/layout/stylesheet/tabs.mjs +1 -1
  36. package/source/components/navigation/stylesheet/table-of-content.mjs +1 -1
  37. package/source/components/navigation/table-of-content.mjs +20 -4
  38. package/source/components/style/common.css +1 -1
  39. package/source/components/style/common.pcss +7 -1
  40. package/source/components/style/mixin/property.pcss +1 -0
  41. package/source/components/style/normalize.css +1 -1
  42. package/source/components/style/normalize.pcss +8 -0
  43. package/source/components/style/property.css +1 -1
  44. package/source/components/stylesheet/common.mjs +7 -14
  45. package/source/components/stylesheet/mixin/property.mjs +6 -13
  46. package/source/components/stylesheet/normalize.mjs +7 -14
  47. package/source/components/stylesheet/property.mjs +1 -1
  48. package/source/components/tree-menu/style/tree-menu.pcss +0 -2
  49. package/source/components/tree-menu/stylesheet/tree-menu.mjs +7 -14
  50. package/source/types/is.mjs +9 -1
  51. package/source/types/version.mjs +1 -1
  52. package/test/cases/monster.mjs +1 -1
  53. package/test/cases/types/is.mjs +59 -1
  54. package/test/util/jsdom.mjs +2 -0
  55. package/test/web/test.html +2 -2
  56. package/test/web/tests.js +83 -21
@@ -12,77 +12,77 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import {Datasource} from "./datasource.mjs";
15
+ import { Datasource } from "./datasource.mjs";
16
16
  import {
17
- assembleMethodSymbol,
18
- CustomElement,
19
- registerCustomElement,
20
- getSlottedElements,
17
+ assembleMethodSymbol,
18
+ CustomElement,
19
+ registerCustomElement,
20
+ getSlottedElements,
21
21
  } from "../../dom/customelement.mjs";
22
22
  import {
23
- findTargetElementFromEvent,
24
- fireCustomEvent,
23
+ findTargetElementFromEvent,
24
+ fireCustomEvent,
25
25
  } from "../../dom/events.mjs";
26
- import {clone} from "../../util/clone.mjs";
26
+ import { clone } from "../../util/clone.mjs";
27
27
  import {
28
- isString,
29
- isFunction,
30
- isInstance,
31
- isObject,
32
- isArray,
28
+ isString,
29
+ isFunction,
30
+ isInstance,
31
+ isObject,
32
+ isArray,
33
33
  } from "../../types/is.mjs";
34
34
  import {
35
- validateArray,
36
- validateInteger,
37
- validateObject,
35
+ validateArray,
36
+ validateInteger,
37
+ validateObject,
38
38
  } from "../../types/validate.mjs";
39
- import {Observer} from "../../types/observer.mjs";
39
+ import { Observer } from "../../types/observer.mjs";
40
40
  import {
41
- ATTRIBUTE_DATATABLE_HEAD,
42
- ATTRIBUTE_DATATABLE_GRID_TEMPLATE,
43
- ATTRIBUTE_DATASOURCE_SELECTOR,
44
- ATTRIBUTE_DATATABLE_ALIGN,
45
- ATTRIBUTE_DATATABLE_SORTABLE,
46
- ATTRIBUTE_DATATABLE_MODE,
47
- ATTRIBUTE_DATATABLE_INDEX,
48
- ATTRIBUTE_DATATABLE_MODE_HIDDEN,
49
- ATTRIBUTE_DATATABLE_MODE_VISIBLE,
50
- ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
51
- ATTRIBUTE_DATATABLE_MODE_FIXED,
41
+ ATTRIBUTE_DATATABLE_HEAD,
42
+ ATTRIBUTE_DATATABLE_GRID_TEMPLATE,
43
+ ATTRIBUTE_DATASOURCE_SELECTOR,
44
+ ATTRIBUTE_DATATABLE_ALIGN,
45
+ ATTRIBUTE_DATATABLE_SORTABLE,
46
+ ATTRIBUTE_DATATABLE_MODE,
47
+ ATTRIBUTE_DATATABLE_INDEX,
48
+ ATTRIBUTE_DATATABLE_MODE_HIDDEN,
49
+ ATTRIBUTE_DATATABLE_MODE_VISIBLE,
50
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
51
+ ATTRIBUTE_DATATABLE_MODE_FIXED,
52
52
  } from "./constants.mjs";
53
- import {instanceSymbol} from "../../constants.mjs";
53
+ import { instanceSymbol } from "../../constants.mjs";
54
54
  import {
55
- Header,
56
- createOrderStatement,
57
- DIRECTION_ASC,
58
- DIRECTION_DESC,
59
- DIRECTION_NONE,
55
+ Header,
56
+ createOrderStatement,
57
+ DIRECTION_ASC,
58
+ DIRECTION_DESC,
59
+ DIRECTION_NONE,
60
60
  } from "./datatable/header.mjs";
61
- import {DatatableStyleSheet} from "./stylesheet/datatable.mjs";
61
+ import { DatatableStyleSheet } from "./stylesheet/datatable.mjs";
62
62
  import {
63
- handleDataSourceChanges,
64
- datasourceLinkedElementSymbol,
63
+ handleDataSourceChanges,
64
+ datasourceLinkedElementSymbol,
65
65
  } from "./util.mjs";
66
66
  import "./columnbar.mjs";
67
67
  import "./filter-button.mjs";
68
68
  import {
69
- findElementWithSelectorUpwards,
70
- getDocument,
71
- getWindow,
69
+ findElementWithSelectorUpwards,
70
+ getDocument,
71
+ getWindow,
72
72
  } from "../../dom/util.mjs";
73
- import {addAttributeToken} from "../../dom/attributes.mjs";
74
- import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
75
- import {getDocumentTranslations} from "../../i18n/translations.mjs";
73
+ import { addAttributeToken } from "../../dom/attributes.mjs";
74
+ import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
75
+ import { getDocumentTranslations } from "../../i18n/translations.mjs";
76
76
  import "../state/state.mjs";
77
77
  import "../host/collapse.mjs";
78
- import {generateUniqueConfigKey} from "../host/util.mjs";
78
+ import { generateUniqueConfigKey } from "../host/util.mjs";
79
79
 
80
80
  import "./datasource/dom.mjs";
81
81
  import "./datasource/rest.mjs";
82
82
 
83
83
  import "../form/context-help.mjs";
84
84
 
85
- export {DataTable};
85
+ export { DataTable };
86
86
 
87
87
  /**
88
88
  * @private
@@ -150,403 +150,404 @@ const resizeObserverSymbol = Symbol("resizeObserver");
150
150
  * @fires monster-datatable-row-added
151
151
  **/
152
152
  class DataTable extends CustomElement {
153
- /**
154
- * This method is called by the `instanceof` operator.
155
- * @return {symbol}
156
- */
157
- static get [instanceSymbol]() {
158
- return Symbol.for("@schukai/monster/components/datatable@@instance");
159
- }
160
-
161
- /**
162
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
163
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
164
- *
165
- * The individual configuration values can be found in the table.
166
- *
167
- * @property {Object} templates Template definitions
168
- * @property {string} templates.main Main template
169
- * @property {Object} datasource Datasource configuration
170
- * @property {string} datasource.selector Selector for the datasource
171
- * @property {Object} mapping Mapping configuration
172
- * @property {string} mapping.data Data mapping
173
- * @property {Array} data Data
174
- * @property {Array} headers Headers
175
- * @property {Object} responsive Responsive configuration
176
- * @property {number} responsive.breakpoint Breakpoint for responsive mode
177
- * @property {Object} labels Labels
178
- * @property {string} labels.theListContainsNoEntries Label for empty state
179
- * @property {Object} classes Classes
180
- * @property {string} classes.container Container class
181
- * @property {Object} features Features
182
- * @property {boolean} features.settings Settings feature
183
- * @property {boolean} features.footer Footer feature
184
- * @property {boolean} features.autoInit Auto init feature (init datasource automatically)
185
- * @property {boolean} features.doubleClickCopyToClipboard Double click copy to clipboard feature
186
- * @property {Object} templateMapping Template mapping
187
- * @property {string} templateMapping.row-key Row key
188
- * @property {string} templateMapping.filter-id Filter id
189
- **/
190
- get defaults() {
191
- return Object.assign(
192
- {},
193
- super.defaults,
194
- {
195
- templates: {
196
- main: getTemplate(),
197
- emptyState: getEmptyTemplate(),
198
- },
199
-
200
- datasource: {
201
- selector: null,
202
- },
203
-
204
- mapping: {
205
- data: "dataset",
206
- },
207
-
208
- data: [],
209
- headers: [],
210
-
211
- responsive: {
212
- breakpoint: 900,
213
- },
214
-
215
- labels: {
216
- theListContainsNoEntries: "The list contains no entries",
217
- copyAll: "Copy all",
218
- helpText: "<p>You can copy the values from individual rows<br>" +
219
- "to the clipboard by double-clicking on the relevant column.</p>" +
220
- "<p>To copy an entire row, hold down the Shift key while clicking.<br>" +
221
- "If you want to copy all rows, you can use the <strong>Copy All</strong> button.</p>",
222
- },
223
-
224
- classes: {
225
- control: "monster-theme-control-container-1",
226
- container: "",
227
- row: "monster-theme-control-row-1",
228
- },
229
-
230
- features: {
231
- settings: true,
232
- footer: true,
233
- autoInit: true,
234
- doubleClickCopyToClipboard: true,
235
- copyAll: true,
236
- help: true,
237
- },
238
-
239
- copy: {
240
- delimiter: ";",
241
- quoteOpen: '"',
242
- quoteClose: '"',
243
- rowBreak: "\n",
244
- },
245
-
246
- templateMapping: {
247
- "row-key": null,
248
- "filter-id": null,
249
- },
250
- },
251
- initOptionsFromArguments.call(this),
252
- );
253
- }
254
-
255
- /**
256
- *
257
- * @param {string} selector
258
- * @return {NodeListOf<*>}
259
- */
260
- getGridElements(selector) {
261
- return this[gridElementSymbol].querySelectorAll(selector);
262
- }
263
-
264
- /**
265
- *
266
- * @return {string}
267
- */
268
- static getTag() {
269
- return "monster-datatable";
270
- }
271
-
272
- /**
273
- * @return {void}
274
- */
275
- disconnectedCallback() {
276
- super.disconnectedCallback();
277
- if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
278
- this[resizeObserverSymbol].disconnect();
279
- }
280
- }
281
-
282
- /**
283
- * @return {void}
284
- */
285
- connectedCallback() {
286
- const self = this;
287
- super.connectedCallback();
288
-
289
- this[resizeObserverSymbol] = new ResizeObserver((entries) => {
290
- updateGrid.call(self);
291
- });
292
-
293
- this[resizeObserverSymbol].observe(this.parentNode);
294
- }
295
-
296
- /**
297
- * @return void
298
- */
299
- [assembleMethodSymbol]() {
300
- const rawKey = this.getOption("templateMapping.row-key");
301
-
302
- if (rawKey === null) {
303
- if (this.id !== null && this.id !== "") {
304
- const rawKey = this.getOption("templateMapping.row-key");
305
- if (rawKey === null) {
306
- this.setOption("templateMapping.row-key", this.id + "-row");
307
- }
308
- } else {
309
- this.setOption("templateMapping.row-key", "row");
310
- }
311
- }
312
-
313
- if (this.id !== null && this.id !== "") {
314
- this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
315
- } else {
316
- this.setOption("templateMapping.filter-id", "filter");
317
- }
318
-
319
- super[assembleMethodSymbol]();
320
-
321
- initControlReferences.call(this);
322
- initEventHandler.call(this);
323
-
324
- const selector = this.getOption("datasource.selector");
325
-
326
- if (isString(selector)) {
327
- const element = findElementWithSelectorUpwards(this, selector);
328
- if (element === null) {
329
- throw new Error("the selector must match exactly one element");
330
- }
331
-
332
- if (!isInstance(element, Datasource)) {
333
- throw new TypeError("the element must be a datasource");
334
- }
335
-
336
- this[datasourceLinkedElementSymbol] = element;
337
-
338
- queueMicrotask(() => {
339
- handleDataSourceChanges.call(this);
340
- element.datasource.attachObserver(
341
- new Observer(handleDataSourceChanges.bind(this)),
342
- );
343
- });
344
- }
345
-
346
- getHostConfig
347
- .call(this, getColumnVisibilityConfigKey)
348
- .then((config) => {
349
- const headerOrderMap = new Map();
350
-
351
- getHostConfig
352
- .call(this, getStoredOrderConfigKey)
353
- .then((orderConfig) => {
354
- if (isArray(orderConfig) || orderConfig.length > 0) {
355
- for (let i = 0; i < orderConfig.length; i++) {
356
- const item = orderConfig[i];
357
- const parts = item.split(" ");
358
- const field = parts[0];
359
- const direction = parts[1] || DIRECTION_ASC;
360
- headerOrderMap.set(field, direction);
361
- }
362
- }
363
- })
364
- .then(() => {
365
- try {
366
- initGridAndStructs.call(this, config, headerOrderMap);
367
- } catch (error) {
368
- addAttributeToken(
369
- this,
370
- ATTRIBUTE_ERRORMESSAGE,
371
- error?.message || error.toString(),
372
- );
373
- }
374
-
375
- updateColumnBar.call(this);
376
- })
377
- .catch((error) => {
378
- addAttributeToken(
379
- this,
380
- ATTRIBUTE_ERRORMESSAGE,
381
- error?.message || error.toString(),
382
- );
383
- });
384
- })
385
- .catch((error) => {
386
- addAttributeToken(
387
- this,
388
- ATTRIBUTE_ERRORMESSAGE,
389
- error?.message || error.toString(),
390
- );
391
- });
392
- }
393
-
394
- /**
395
- * @return {CSSStyleSheet[]}
396
- */
397
- static getCSSStyleSheet() {
398
- return [DatatableStyleSheet];
399
- }
400
-
401
- /**
402
- * Copy a row from the datatable
403
- *
404
- * @param {number|string} fromIndex
405
- * @param {number|string} toIndex
406
- * @return {DataTable}
407
- * @fires monster-datatable-row-copied
408
- */
409
- copyRow(fromIndex, toIndex) {
410
- const datasource = this[datasourceLinkedElementSymbol];
411
- if (!datasource) {
412
- return this;
413
- }
414
- let d = datasource.data;
415
- let c = clone(d);
416
-
417
- let rows = c;
418
- const mapping = this.getOption("mapping.data");
419
-
420
- if (mapping) {
421
- rows = c?.[mapping];
422
- }
423
-
424
- if (rows === undefined || rows === null) {
425
- rows = [];
426
- }
427
-
428
- if (toIndex === undefined) {
429
- toIndex = rows.length;
430
- }
431
-
432
- if (isString(fromIndex)) {
433
- fromIndex = parseInt(fromIndex);
434
- }
435
- if (isString(toIndex)) {
436
- toIndex = parseInt(toIndex);
437
- }
438
-
439
- if (toIndex < 0 || toIndex > rows.length) {
440
- throw new RangeError("index out of bounds");
441
- }
442
-
443
- validateArray(rows);
444
- validateInteger(fromIndex);
445
- validateInteger(toIndex);
446
-
447
- if (fromIndex < 0 || fromIndex >= rows.length) {
448
- throw new RangeError("index out of bounds");
449
- }
450
-
451
- rows.splice(toIndex, 0, clone(rows[fromIndex]));
452
- datasource.data = c;
453
-
454
- fireCustomEvent(this, "monster-datatable-row-copied", {
455
- index: toIndex,
456
- });
457
-
458
- return this;
459
- }
460
-
461
- /**
462
- * Remove a row from the datatable
463
- *
464
- * @param {number|string} index
465
- * @return {DataTable}
466
- * @fires monster-datatable-row-removed
467
- */
468
- removeRow(index) {
469
- const datasource = this[datasourceLinkedElementSymbol];
470
- if (!datasource) {
471
- return this;
472
- }
473
- let d = datasource.data;
474
- let c = clone(d);
475
-
476
- let rows = c;
477
- const mapping = this.getOption("mapping.data");
478
-
479
- if (mapping) {
480
- rows = c?.[mapping];
481
- }
482
-
483
- if (rows === undefined || rows === null) {
484
- rows = [];
485
- }
486
-
487
- if (isString(index)) {
488
- index = parseInt(index);
489
- }
490
-
491
- validateArray(rows);
492
- validateInteger(index);
493
-
494
- if (index < 0 || index >= rows.length) {
495
- throw new RangeError("index out of bounds");
496
- }
497
- if (mapping) {
498
- rows = c?.[mapping];
499
- }
500
-
501
- rows.splice(index, 1);
502
- datasource.data = c;
503
-
504
- fireCustomEvent(this, "monster-datatable-row-removed", {
505
- index: index,
506
- });
507
-
508
- return this;
509
- }
510
-
511
- /**
512
- * Add a row to the datatable
513
- *
514
- * @param {Object} data
515
- * @return {DataTable}
516
- *
517
- * @fires monster-datatable-row-added
518
- **/
519
- addRow(data) {
520
- const datasource = this[datasourceLinkedElementSymbol];
521
- if (!datasource) {
522
- return this;
523
- }
524
- let d = datasource.data;
525
- let c = clone(d);
526
-
527
- let rows = c;
528
-
529
- const mapping = this.getOption("mapping.data");
530
- if (mapping) {
531
- rows = c?.[mapping];
532
- }
533
-
534
- if (rows === undefined || rows === null) {
535
- rows = [];
536
- }
537
-
538
- validateArray(rows);
539
- validateObject(data);
540
-
541
- rows.push(data);
542
- datasource.data = c;
543
-
544
- fireCustomEvent(this, "monster-datatable-row-added", {
545
- index: rows.length - 1,
546
- });
547
-
548
- return this;
549
- }
153
+ /**
154
+ * This method is called by the `instanceof` operator.
155
+ * @return {symbol}
156
+ */
157
+ static get [instanceSymbol]() {
158
+ return Symbol.for("@schukai/monster/components/datatable@@instance");
159
+ }
160
+
161
+ /**
162
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
163
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
164
+ *
165
+ * The individual configuration values can be found in the table.
166
+ *
167
+ * @property {Object} templates Template definitions
168
+ * @property {string} templates.main Main template
169
+ * @property {Object} datasource Datasource configuration
170
+ * @property {string} datasource.selector Selector for the datasource
171
+ * @property {Object} mapping Mapping configuration
172
+ * @property {string} mapping.data Data mapping
173
+ * @property {Array} data Data
174
+ * @property {Array} headers Headers
175
+ * @property {Object} responsive Responsive configuration
176
+ * @property {number} responsive.breakpoint Breakpoint for responsive mode
177
+ * @property {Object} labels Labels
178
+ * @property {string} labels.theListContainsNoEntries Label for empty state
179
+ * @property {Object} classes Classes
180
+ * @property {string} classes.container Container class
181
+ * @property {Object} features Features
182
+ * @property {boolean} features.settings Settings feature
183
+ * @property {boolean} features.footer Footer feature
184
+ * @property {boolean} features.autoInit Auto init feature (init datasource automatically)
185
+ * @property {boolean} features.doubleClickCopyToClipboard Double click copy to clipboard feature
186
+ * @property {Object} templateMapping Template mapping
187
+ * @property {string} templateMapping.row-key Row key
188
+ * @property {string} templateMapping.filter-id Filter id
189
+ **/
190
+ get defaults() {
191
+ return Object.assign(
192
+ {},
193
+ super.defaults,
194
+ {
195
+ templates: {
196
+ main: getTemplate(),
197
+ emptyState: getEmptyTemplate(),
198
+ },
199
+
200
+ datasource: {
201
+ selector: null,
202
+ },
203
+
204
+ mapping: {
205
+ data: "dataset",
206
+ },
207
+
208
+ data: [],
209
+ headers: [],
210
+
211
+ responsive: {
212
+ breakpoint: 900,
213
+ },
214
+
215
+ labels: {
216
+ theListContainsNoEntries: "The list contains no entries",
217
+ copyAll: "Copy all",
218
+ helpText:
219
+ "<p>You can copy the values from individual rows<br>" +
220
+ "to the clipboard by double-clicking on the relevant column.</p>" +
221
+ "<p>To copy an entire row, hold down the Shift key while clicking.<br>" +
222
+ "If you want to copy all rows, you can use the <strong>Copy All</strong> button.</p>",
223
+ },
224
+
225
+ classes: {
226
+ control: "monster-theme-control-container-1",
227
+ container: "",
228
+ row: "monster-theme-control-row-1",
229
+ },
230
+
231
+ features: {
232
+ settings: true,
233
+ footer: true,
234
+ autoInit: true,
235
+ doubleClickCopyToClipboard: true,
236
+ copyAll: true,
237
+ help: true,
238
+ },
239
+
240
+ copy: {
241
+ delimiter: ";",
242
+ quoteOpen: '"',
243
+ quoteClose: '"',
244
+ rowBreak: "\n",
245
+ },
246
+
247
+ templateMapping: {
248
+ "row-key": null,
249
+ "filter-id": null,
250
+ },
251
+ },
252
+ initOptionsFromArguments.call(this),
253
+ );
254
+ }
255
+
256
+ /**
257
+ *
258
+ * @param {string} selector
259
+ * @return {NodeListOf<*>}
260
+ */
261
+ getGridElements(selector) {
262
+ return this[gridElementSymbol].querySelectorAll(selector);
263
+ }
264
+
265
+ /**
266
+ *
267
+ * @return {string}
268
+ */
269
+ static getTag() {
270
+ return "monster-datatable";
271
+ }
272
+
273
+ /**
274
+ * @return {void}
275
+ */
276
+ disconnectedCallback() {
277
+ super.disconnectedCallback();
278
+ if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
279
+ this[resizeObserverSymbol].disconnect();
280
+ }
281
+ }
282
+
283
+ /**
284
+ * @return {void}
285
+ */
286
+ connectedCallback() {
287
+ const self = this;
288
+ super.connectedCallback();
289
+
290
+ this[resizeObserverSymbol] = new ResizeObserver((entries) => {
291
+ updateGrid.call(self);
292
+ });
293
+
294
+ this[resizeObserverSymbol].observe(this.parentNode);
295
+ }
296
+
297
+ /**
298
+ * @return void
299
+ */
300
+ [assembleMethodSymbol]() {
301
+ const rawKey = this.getOption("templateMapping.row-key");
302
+
303
+ if (rawKey === null) {
304
+ if (this.id !== null && this.id !== "") {
305
+ const rawKey = this.getOption("templateMapping.row-key");
306
+ if (rawKey === null) {
307
+ this.setOption("templateMapping.row-key", this.id + "-row");
308
+ }
309
+ } else {
310
+ this.setOption("templateMapping.row-key", "row");
311
+ }
312
+ }
313
+
314
+ if (this.id !== null && this.id !== "") {
315
+ this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
316
+ } else {
317
+ this.setOption("templateMapping.filter-id", "filter");
318
+ }
319
+
320
+ super[assembleMethodSymbol]();
321
+
322
+ initControlReferences.call(this);
323
+ initEventHandler.call(this);
324
+
325
+ const selector = this.getOption("datasource.selector");
326
+
327
+ if (isString(selector)) {
328
+ const element = findElementWithSelectorUpwards(this, selector);
329
+ if (element === null) {
330
+ throw new Error("the selector must match exactly one element");
331
+ }
332
+
333
+ if (!isInstance(element, Datasource)) {
334
+ throw new TypeError("the element must be a datasource");
335
+ }
336
+
337
+ this[datasourceLinkedElementSymbol] = element;
338
+
339
+ queueMicrotask(() => {
340
+ handleDataSourceChanges.call(this);
341
+ element.datasource.attachObserver(
342
+ new Observer(handleDataSourceChanges.bind(this)),
343
+ );
344
+ });
345
+ }
346
+
347
+ getHostConfig
348
+ .call(this, getColumnVisibilityConfigKey)
349
+ .then((config) => {
350
+ const headerOrderMap = new Map();
351
+
352
+ getHostConfig
353
+ .call(this, getStoredOrderConfigKey)
354
+ .then((orderConfig) => {
355
+ if (isArray(orderConfig) || orderConfig.length > 0) {
356
+ for (let i = 0; i < orderConfig.length; i++) {
357
+ const item = orderConfig[i];
358
+ const parts = item.split(" ");
359
+ const field = parts[0];
360
+ const direction = parts[1] || DIRECTION_ASC;
361
+ headerOrderMap.set(field, direction);
362
+ }
363
+ }
364
+ })
365
+ .then(() => {
366
+ try {
367
+ initGridAndStructs.call(this, config, headerOrderMap);
368
+ } catch (error) {
369
+ addAttributeToken(
370
+ this,
371
+ ATTRIBUTE_ERRORMESSAGE,
372
+ error?.message || error.toString(),
373
+ );
374
+ }
375
+
376
+ updateColumnBar.call(this);
377
+ })
378
+ .catch((error) => {
379
+ addAttributeToken(
380
+ this,
381
+ ATTRIBUTE_ERRORMESSAGE,
382
+ error?.message || error.toString(),
383
+ );
384
+ });
385
+ })
386
+ .catch((error) => {
387
+ addAttributeToken(
388
+ this,
389
+ ATTRIBUTE_ERRORMESSAGE,
390
+ error?.message || error.toString(),
391
+ );
392
+ });
393
+ }
394
+
395
+ /**
396
+ * @return {CSSStyleSheet[]}
397
+ */
398
+ static getCSSStyleSheet() {
399
+ return [DatatableStyleSheet];
400
+ }
401
+
402
+ /**
403
+ * Copy a row from the datatable
404
+ *
405
+ * @param {number|string} fromIndex
406
+ * @param {number|string} toIndex
407
+ * @return {DataTable}
408
+ * @fires monster-datatable-row-copied
409
+ */
410
+ copyRow(fromIndex, toIndex) {
411
+ const datasource = this[datasourceLinkedElementSymbol];
412
+ if (!datasource) {
413
+ return this;
414
+ }
415
+ let d = datasource.data;
416
+ let c = clone(d);
417
+
418
+ let rows = c;
419
+ const mapping = this.getOption("mapping.data");
420
+
421
+ if (mapping) {
422
+ rows = c?.[mapping];
423
+ }
424
+
425
+ if (rows === undefined || rows === null) {
426
+ rows = [];
427
+ }
428
+
429
+ if (toIndex === undefined) {
430
+ toIndex = rows.length;
431
+ }
432
+
433
+ if (isString(fromIndex)) {
434
+ fromIndex = parseInt(fromIndex);
435
+ }
436
+ if (isString(toIndex)) {
437
+ toIndex = parseInt(toIndex);
438
+ }
439
+
440
+ if (toIndex < 0 || toIndex > rows.length) {
441
+ throw new RangeError("index out of bounds");
442
+ }
443
+
444
+ validateArray(rows);
445
+ validateInteger(fromIndex);
446
+ validateInteger(toIndex);
447
+
448
+ if (fromIndex < 0 || fromIndex >= rows.length) {
449
+ throw new RangeError("index out of bounds");
450
+ }
451
+
452
+ rows.splice(toIndex, 0, clone(rows[fromIndex]));
453
+ datasource.data = c;
454
+
455
+ fireCustomEvent(this, "monster-datatable-row-copied", {
456
+ index: toIndex,
457
+ });
458
+
459
+ return this;
460
+ }
461
+
462
+ /**
463
+ * Remove a row from the datatable
464
+ *
465
+ * @param {number|string} index
466
+ * @return {DataTable}
467
+ * @fires monster-datatable-row-removed
468
+ */
469
+ removeRow(index) {
470
+ const datasource = this[datasourceLinkedElementSymbol];
471
+ if (!datasource) {
472
+ return this;
473
+ }
474
+ let d = datasource.data;
475
+ let c = clone(d);
476
+
477
+ let rows = c;
478
+ const mapping = this.getOption("mapping.data");
479
+
480
+ if (mapping) {
481
+ rows = c?.[mapping];
482
+ }
483
+
484
+ if (rows === undefined || rows === null) {
485
+ rows = [];
486
+ }
487
+
488
+ if (isString(index)) {
489
+ index = parseInt(index);
490
+ }
491
+
492
+ validateArray(rows);
493
+ validateInteger(index);
494
+
495
+ if (index < 0 || index >= rows.length) {
496
+ throw new RangeError("index out of bounds");
497
+ }
498
+ if (mapping) {
499
+ rows = c?.[mapping];
500
+ }
501
+
502
+ rows.splice(index, 1);
503
+ datasource.data = c;
504
+
505
+ fireCustomEvent(this, "monster-datatable-row-removed", {
506
+ index: index,
507
+ });
508
+
509
+ return this;
510
+ }
511
+
512
+ /**
513
+ * Add a row to the datatable
514
+ *
515
+ * @param {Object} data
516
+ * @return {DataTable}
517
+ *
518
+ * @fires monster-datatable-row-added
519
+ **/
520
+ addRow(data) {
521
+ const datasource = this[datasourceLinkedElementSymbol];
522
+ if (!datasource) {
523
+ return this;
524
+ }
525
+ let d = datasource.data;
526
+ let c = clone(d);
527
+
528
+ let rows = c;
529
+
530
+ const mapping = this.getOption("mapping.data");
531
+ if (mapping) {
532
+ rows = c?.[mapping];
533
+ }
534
+
535
+ if (rows === undefined || rows === null) {
536
+ rows = [];
537
+ }
538
+
539
+ validateArray(rows);
540
+ validateObject(data);
541
+
542
+ rows.push(data);
543
+ datasource.data = c;
544
+
545
+ fireCustomEvent(this, "monster-datatable-row-added", {
546
+ index: rows.length - 1,
547
+ });
548
+
549
+ return this;
550
+ }
550
551
  }
551
552
 
552
553
  /**
@@ -554,7 +555,7 @@ class DataTable extends CustomElement {
554
555
  * @return {string}
555
556
  */
556
557
  function getColumnVisibilityConfigKey() {
557
- return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
558
+ return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
558
559
  }
559
560
 
560
561
  /**
@@ -562,7 +563,7 @@ function getColumnVisibilityConfigKey() {
562
563
  * @return {string}
563
564
  */
564
565
  function getFilterConfigKey() {
565
- return generateUniqueConfigKey("datatable", this?.id, "filter");
566
+ return generateUniqueConfigKey("datatable", this?.id, "filter");
566
567
  }
567
568
 
568
569
  /**
@@ -570,403 +571,410 @@ function getFilterConfigKey() {
570
571
  * @return {Promise}
571
572
  */
572
573
  function getHostConfig(callback) {
573
- const host = findElementWithSelectorUpwards(this, "monster-host");
574
-
575
- if (!(host && this.id)) {
576
- return Promise.resolve({});
577
- }
578
-
579
- if (!host || !isFunction(host?.getConfig)) {
580
- throw new TypeError("the host must be a monster-host");
581
- }
582
-
583
- const configKey = callback.call(this);
584
- return host.hasConfig(configKey).then((hasConfig) => {
585
- if (hasConfig) {
586
- return host.getConfig(configKey);
587
- } else {
588
- return {};
589
- }
590
- });
574
+ const host = findElementWithSelectorUpwards(this, "monster-host");
575
+
576
+ if (!(host && this.id)) {
577
+ return Promise.resolve({});
578
+ }
579
+
580
+ if (!host || !isFunction(host?.getConfig)) {
581
+ throw new TypeError("the host must be a monster-host");
582
+ }
583
+
584
+ const configKey = callback.call(this);
585
+ return host.hasConfig(configKey).then((hasConfig) => {
586
+ if (hasConfig) {
587
+ return host.getConfig(configKey);
588
+ } else {
589
+ return {};
590
+ }
591
+ });
591
592
  }
592
593
 
593
594
  /**
594
595
  * @private
595
596
  */
596
597
  function updateColumnBar() {
597
- if (!this[columnBarElementSymbol]) {
598
- return;
599
- }
600
-
601
- const columns = [];
602
- for (const header of this.getOption("headers")) {
603
- const mode = header.getInternal("mode");
604
-
605
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
606
- continue;
607
- }
608
-
609
- columns.push({
610
- visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
611
- name: header.label,
612
- index: header.index,
613
- });
614
- }
615
-
616
- this[columnBarElementSymbol].setOption("columns", columns);
598
+ if (!this[columnBarElementSymbol]) {
599
+ return;
600
+ }
601
+
602
+ const columns = [];
603
+ for (const header of this.getOption("headers")) {
604
+ const mode = header.getInternal("mode");
605
+
606
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
607
+ continue;
608
+ }
609
+
610
+ columns.push({
611
+ visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
612
+ name: header.label,
613
+ index: header.index,
614
+ });
615
+ }
616
+
617
+ this[columnBarElementSymbol].setOption("columns", columns);
617
618
  }
618
619
 
619
620
  /**
620
621
  * @private
621
622
  */
622
623
  function updateHeaderFromColumnBar() {
623
- if (!this[columnBarElementSymbol]) {
624
- return;
625
- }
624
+ if (!this[columnBarElementSymbol]) {
625
+ return;
626
+ }
626
627
 
627
- const options = this[columnBarElementSymbol].getOption("columns");
628
- if (!isArray(options)) return;
628
+ const options = this[columnBarElementSymbol].getOption("columns");
629
+ if (!isArray(options)) return;
629
630
 
630
- const invisibleMap = {};
631
+ const invisibleMap = {};
631
632
 
632
- for (let i = 0; i < options.length; i++) {
633
- const option = options[i];
634
- invisibleMap[option.index] = option.visible;
635
- }
633
+ for (let i = 0; i < options.length; i++) {
634
+ const option = options[i];
635
+ invisibleMap[option.index] = option.visible;
636
+ }
636
637
 
637
- for (const header of this.getOption("headers")) {
638
- const mode = header.getInternal("mode");
638
+ for (const header of this.getOption("headers")) {
639
+ const mode = header.getInternal("mode");
639
640
 
640
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
641
- continue;
642
- }
641
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
642
+ continue;
643
+ }
643
644
 
644
- if (invisibleMap[header.index] === false) {
645
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
646
- } else {
647
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
648
- }
649
- }
645
+ if (invisibleMap[header.index] === false) {
646
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
647
+ } else {
648
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
649
+ }
650
+ }
650
651
  }
651
652
 
652
653
  /**
653
654
  * @private
654
655
  */
655
656
  function updateConfigColumnBar() {
656
- if (!this[columnBarElementSymbol]) {
657
- return;
658
- }
659
-
660
- const options = this[columnBarElementSymbol].getOption("columns");
661
- if (!isArray(options)) return;
662
-
663
- const map = {};
664
- for (let i = 0; i < options.length; i++) {
665
- const option = options[i];
666
- map[option.name] = option.visible;
667
- }
668
-
669
- const host = findElementWithSelectorUpwards(this, "monster-host");
670
- if (!(host && this.id)) {
671
- return;
672
- }
673
- const configKey = getColumnVisibilityConfigKey.call(this);
674
-
675
- try {
676
- host.setConfig(configKey, map);
677
- } catch (error) {
678
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
679
- }
657
+ if (!this[columnBarElementSymbol]) {
658
+ return;
659
+ }
660
+
661
+ const options = this[columnBarElementSymbol].getOption("columns");
662
+ if (!isArray(options)) return;
663
+
664
+ const map = {};
665
+ for (let i = 0; i < options.length; i++) {
666
+ const option = options[i];
667
+ map[option.name] = option.visible;
668
+ }
669
+
670
+ const host = findElementWithSelectorUpwards(this, "monster-host");
671
+ if (!(host && this.id)) {
672
+ return;
673
+ }
674
+ const configKey = getColumnVisibilityConfigKey.call(this);
675
+
676
+ try {
677
+ host.setConfig(configKey, map);
678
+ } catch (error) {
679
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
680
+ }
680
681
  }
681
682
 
682
683
  /**
683
684
  * @private
684
685
  */
685
686
  function initEventHandler() {
686
- const self = this;
687
-
688
- const quoteOpenChar = this.getOption("copy.quoteOpen");
689
- const quoteCloseChar = this.getOption("copy.quoteClose");
690
- const delimiterChar = this.getOption("copy.delimiter");
691
- const rowBreak = this.getOption("copy.rowBreak");
692
-
693
- self[columnBarElementSymbol].attachObserver(
694
- new Observer((e) => {
695
- updateHeaderFromColumnBar.call(self);
696
- updateGrid.call(self);
697
- updateConfigColumnBar.call(self);
698
- }),
699
- );
700
-
701
- self[gridHeadersElementSymbol].addEventListener("click", function (event) {
702
- let element = null;
703
- const datasource = self[datasourceLinkedElementSymbol];
704
- if (!datasource) {
705
- return;
706
- }
707
-
708
- element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
709
- if (element) {
710
- const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
711
- const headers = self.getOption("headers");
712
-
713
- event.preventDefault();
714
-
715
- headers[index].changeDirection();
716
-
717
- queueMicrotask(function () {
718
- /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
719
- element.setAttribute(
720
- ATTRIBUTE_DATATABLE_SORTABLE,
721
- `${headers[index].field} ${headers[index].direction}`,
722
- );
723
-
724
- storeOrderStatement.call(self, true);
725
- });
726
- }
727
- });
728
-
729
-
730
- const eventHandlerDoubleClickCopyToClipboard = (event) => {
731
- const element = findTargetElementFromEvent(event, "data-monster-head");
732
- if (element) {
733
-
734
-
735
- let text = "";
736
-
737
- if (event.shiftKey) {
738
- const index = element.getAttribute("data-monster-insert-reference");
739
- if (index) {
740
- const cols = self.getGridElements(`[data-monster-insert-reference="${index}"]`);
741
-
742
- const colTexts = []
743
- for (let i = 0; i < cols.length; i++) {
744
- const col = cols[i];
745
-
746
- if (col.querySelector("monster-button-bar") || col.querySelector("monster-button")) {
747
- continue;
748
- }
749
-
750
- if (col.textContent) {
751
- colTexts.push(quoteOpenChar+col.textContent.trim()+quoteCloseChar);
752
- }
753
- }
754
-
755
- text = colTexts.join(delimiterChar);
756
-
757
- }
758
-
759
-
760
- } else {
761
- if (element.querySelector("monster-button-bar") || element.querySelector("monster-button")) {
762
- return;
763
- }
764
-
765
- text = element.textContent.trim();
766
-
767
- }
768
-
769
- if (getWindow().navigator.clipboard && text) {
770
- getWindow().navigator.clipboard.writeText(text).then(
771
- () => {
772
- },
773
- (err) => {
774
- }
775
- );
776
- }
777
- }
778
- }
779
-
780
- if (self.getOption("features.doubleClickCopyToClipboard")) {
781
- self[gridElementSymbol].addEventListener("dblclick", eventHandlerDoubleClickCopyToClipboard);
782
- }
783
-
784
- if (self.getOption("features.copyAll") && this[copyAllElementSymbol]) {
785
- this[copyAllElementSymbol].addEventListener("click", (event) => {
786
- event.preventDefault();
787
-
788
- const table = [];
789
- let currentRow = [];
790
- let currentIndex= null;
791
-
792
- const cols = self.getGridElements(`[data-monster-insert-reference]`);
793
- const rowIndexes = new Map();
794
- cols.forEach((col) => {
795
- const index = col.getAttribute("data-monster-insert-reference");
796
- rowIndexes.set(index, true);
797
- });
798
-
799
- rowIndexes.forEach((value, key) => {
800
- const cols = self.getGridElements(`[data-monster-insert-reference="${key}"]`);
801
-
802
- for (let i = 0; i < cols.length; i++) {
803
- const col = cols[i];
804
-
805
- if (col.querySelector("monster-button-bar") || col.querySelector("monster-button")) {
806
- continue;
807
- }
808
-
809
- if (col.textContent) {
810
- currentRow.push(quoteOpenChar+col.textContent.trim()+quoteCloseChar);
811
- }
812
- }
813
-
814
- if(currentRow.length > 0) {
815
- table.push(currentRow);
816
- }
817
- currentRow = [];
818
- });
819
-
820
- if(table.length > 0) {
821
- const text = table.map(row => row.join(delimiterChar)).join(rowBreak);
822
- if (getWindow().navigator.clipboard && text) {
823
- getWindow().navigator.clipboard.writeText(text).then(
824
- () => {
825
- },
826
- (err) => {
827
- }
828
- );
829
- }
830
- }
831
-
832
- });
833
-
834
-
835
- }
836
-
837
-
838
-
687
+ const self = this;
688
+
689
+ const quoteOpenChar = this.getOption("copy.quoteOpen");
690
+ const quoteCloseChar = this.getOption("copy.quoteClose");
691
+ const delimiterChar = this.getOption("copy.delimiter");
692
+ const rowBreak = this.getOption("copy.rowBreak");
693
+
694
+ self[columnBarElementSymbol].attachObserver(
695
+ new Observer((e) => {
696
+ updateHeaderFromColumnBar.call(self);
697
+ updateGrid.call(self);
698
+ updateConfigColumnBar.call(self);
699
+ }),
700
+ );
701
+
702
+ self[gridHeadersElementSymbol].addEventListener("click", function (event) {
703
+ let element = null;
704
+ const datasource = self[datasourceLinkedElementSymbol];
705
+ if (!datasource) {
706
+ return;
707
+ }
708
+
709
+ element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
710
+ if (element) {
711
+ const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
712
+ const headers = self.getOption("headers");
713
+
714
+ event.preventDefault();
715
+
716
+ headers[index].changeDirection();
717
+
718
+ queueMicrotask(function () {
719
+ /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
720
+ element.setAttribute(
721
+ ATTRIBUTE_DATATABLE_SORTABLE,
722
+ `${headers[index].field} ${headers[index].direction}`,
723
+ );
724
+
725
+ storeOrderStatement.call(self, true);
726
+ });
727
+ }
728
+ });
729
+
730
+ const eventHandlerDoubleClickCopyToClipboard = (event) => {
731
+ const element = findTargetElementFromEvent(event, "data-monster-head");
732
+ if (element) {
733
+ let text = "";
734
+
735
+ if (event.shiftKey) {
736
+ const index = element.getAttribute("data-monster-insert-reference");
737
+ if (index) {
738
+ const cols = self.getGridElements(
739
+ `[data-monster-insert-reference="${index}"]`,
740
+ );
741
+
742
+ const colTexts = [];
743
+ for (let i = 0; i < cols.length; i++) {
744
+ const col = cols[i];
745
+
746
+ if (
747
+ col.querySelector("monster-button-bar") ||
748
+ col.querySelector("monster-button")
749
+ ) {
750
+ continue;
751
+ }
752
+
753
+ if (col.textContent) {
754
+ colTexts.push(
755
+ quoteOpenChar + col.textContent.trim() + quoteCloseChar,
756
+ );
757
+ }
758
+ }
759
+
760
+ text = colTexts.join(delimiterChar);
761
+ }
762
+ } else {
763
+ if (
764
+ element.querySelector("monster-button-bar") ||
765
+ element.querySelector("monster-button")
766
+ ) {
767
+ return;
768
+ }
769
+
770
+ text = element.textContent.trim();
771
+ }
772
+
773
+ if (getWindow().navigator.clipboard && text) {
774
+ getWindow()
775
+ .navigator.clipboard.writeText(text)
776
+ .then(
777
+ () => {},
778
+ (err) => {},
779
+ );
780
+ }
781
+ }
782
+ };
783
+
784
+ if (self.getOption("features.doubleClickCopyToClipboard")) {
785
+ self[gridElementSymbol].addEventListener(
786
+ "dblclick",
787
+ eventHandlerDoubleClickCopyToClipboard,
788
+ );
789
+ }
790
+
791
+ if (self.getOption("features.copyAll") && this[copyAllElementSymbol]) {
792
+ this[copyAllElementSymbol].addEventListener("click", (event) => {
793
+ event.preventDefault();
794
+
795
+ const table = [];
796
+ let currentRow = [];
797
+ let currentIndex = null;
798
+
799
+ const cols = self.getGridElements(`[data-monster-insert-reference]`);
800
+ const rowIndexes = new Map();
801
+ cols.forEach((col) => {
802
+ const index = col.getAttribute("data-monster-insert-reference");
803
+ rowIndexes.set(index, true);
804
+ });
805
+
806
+ rowIndexes.forEach((value, key) => {
807
+ const cols = self.getGridElements(
808
+ `[data-monster-insert-reference="${key}"]`,
809
+ );
810
+
811
+ for (let i = 0; i < cols.length; i++) {
812
+ const col = cols[i];
813
+
814
+ if (
815
+ col.querySelector("monster-button-bar") ||
816
+ col.querySelector("monster-button")
817
+ ) {
818
+ continue;
819
+ }
820
+
821
+ if (col.textContent) {
822
+ currentRow.push(
823
+ quoteOpenChar + col.textContent.trim() + quoteCloseChar,
824
+ );
825
+ }
826
+ }
827
+
828
+ if (currentRow.length > 0) {
829
+ table.push(currentRow);
830
+ }
831
+ currentRow = [];
832
+ });
833
+
834
+ if (table.length > 0) {
835
+ const text = table.map((row) => row.join(delimiterChar)).join(rowBreak);
836
+ if (getWindow().navigator.clipboard && text) {
837
+ getWindow()
838
+ .navigator.clipboard.writeText(text)
839
+ .then(
840
+ () => {},
841
+ (err) => {},
842
+ );
843
+ }
844
+ }
845
+ });
846
+ }
839
847
  }
840
848
 
841
849
  /**
842
850
  * @private
843
851
  */
844
852
  function initGridAndStructs(hostConfig, headerOrderMap) {
845
- const rowID = this.getOption("templateMapping.row-key");
846
-
847
- if (!this[gridElementSymbol]) {
848
- throw new Error("no grid element is defined");
849
- }
850
-
851
- let template;
852
- getSlottedElements.call(this).forEach((e) => {
853
- if (e instanceof HTMLTemplateElement && e.id === rowID) {
854
- template = e;
855
- }
856
- });
857
-
858
- if (!template) {
859
- throw new Error("no template is defined");
860
- }
861
-
862
- const rowCount = template.content.children.length;
863
-
864
- const headers = [];
865
-
866
- for (let i = 0; i < rowCount; i++) {
867
- let hClass = "";
868
- const row = template.content.children[i];
869
-
870
- let mode = "";
871
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
872
- mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
873
- }
874
-
875
- let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
876
- if (!grid || grid === "" || grid === "auto") {
877
- grid = "minmax(0, 1fr)";
878
- }
879
-
880
- let label = "";
881
- let labelKey = "";
882
-
883
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
884
- label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
885
- labelKey = label;
886
-
887
- try {
888
- if (label.startsWith("i18n:")) {
889
- label = label.substring(5, label.length);
890
- label = getDocumentTranslations().getText(label, label);
891
- }
892
- } catch (e) {
893
- label = "i18n error " + label;
894
- }
895
- }
896
-
897
- if (!label) {
898
- label = i + 1 + "";
899
- mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
900
- labelKey = label;
901
- }
902
-
903
- if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
904
- if (hostConfig[label] === false) {
905
- mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
906
- } else {
907
- mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
908
- }
909
- }
910
-
911
- let align = "";
912
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
913
- align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
914
- }
915
-
916
- switch (align) {
917
- case "center":
918
- hClass = "flex-center";
919
- break;
920
- case "end":
921
- hClass = "flex-end";
922
- break;
923
- case "start":
924
- hClass = "flex-start";
925
- break;
926
- default:
927
- hClass = "flex-start";
928
- }
929
-
930
- let field = "";
931
- let direction = DIRECTION_NONE;
932
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
933
- field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE).trim();
934
- const parts = field.split(" ").map((item) => item.trim());
935
- field = parts[0];
936
-
937
- if (headerOrderMap.has(field)) {
938
- direction = headerOrderMap.get(field);
939
- } else if (
940
- parts.length === 2 &&
941
- [DIRECTION_ASC, DIRECTION_DESC].indexOf(parts[1]) !== -1
942
- ) {
943
- direction = parts[1];
944
- }
945
- }
946
-
947
- if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
948
- hClass += " hidden";
949
- }
950
-
951
- const header = new Header();
952
- header.setInternals({
953
- field: field,
954
- label: label,
955
- classes: hClass,
956
- index: i,
957
- mode: mode,
958
- grid: grid,
959
- labelKey: labelKey,
960
- direction: direction,
961
- });
962
-
963
- headers.push(header);
964
- }
965
-
966
- this.setOption("headers", headers);
967
- queueMicrotask(() => {
968
- storeOrderStatement.call(this, this.getOption("features.autoInit"));
969
- });
853
+ const rowID = this.getOption("templateMapping.row-key");
854
+
855
+ if (!this[gridElementSymbol]) {
856
+ throw new Error("no grid element is defined");
857
+ }
858
+
859
+ let template;
860
+ getSlottedElements.call(this).forEach((e) => {
861
+ if (e instanceof HTMLTemplateElement && e.id === rowID) {
862
+ template = e;
863
+ }
864
+ });
865
+
866
+ if (!template) {
867
+ throw new Error("no template is defined");
868
+ }
869
+
870
+ const rowCount = template.content.children.length;
871
+
872
+ const headers = [];
873
+
874
+ for (let i = 0; i < rowCount; i++) {
875
+ let hClass = "";
876
+ const row = template.content.children[i];
877
+
878
+ let mode = "";
879
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
880
+ mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
881
+ }
882
+
883
+ let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
884
+ if (!grid || grid === "" || grid === "auto") {
885
+ grid = "minmax(0, 1fr)";
886
+ }
887
+
888
+ let label = "";
889
+ let labelKey = "";
890
+
891
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
892
+ label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
893
+ labelKey = label;
894
+
895
+ try {
896
+ if (label.startsWith("i18n:")) {
897
+ label = label.substring(5, label.length);
898
+ label = getDocumentTranslations().getText(label, label);
899
+ }
900
+ } catch (e) {
901
+ label = "i18n error " + label;
902
+ }
903
+ }
904
+
905
+ if (!label) {
906
+ label = i + 1 + "";
907
+ mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
908
+ labelKey = label;
909
+ }
910
+
911
+ if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
912
+ if (hostConfig[label] === false) {
913
+ mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
914
+ } else {
915
+ mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
916
+ }
917
+ }
918
+
919
+ let align = "";
920
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
921
+ align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
922
+ }
923
+
924
+ switch (align) {
925
+ case "center":
926
+ hClass = "flex-center";
927
+ break;
928
+ case "end":
929
+ hClass = "flex-end";
930
+ break;
931
+ case "start":
932
+ hClass = "flex-start";
933
+ break;
934
+ default:
935
+ hClass = "flex-start";
936
+ }
937
+
938
+ let field = "";
939
+ let direction = DIRECTION_NONE;
940
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
941
+ field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE).trim();
942
+ const parts = field.split(" ").map((item) => item.trim());
943
+ field = parts[0];
944
+
945
+ if (headerOrderMap.has(field)) {
946
+ direction = headerOrderMap.get(field);
947
+ } else if (
948
+ parts.length === 2 &&
949
+ [DIRECTION_ASC, DIRECTION_DESC].indexOf(parts[1]) !== -1
950
+ ) {
951
+ direction = parts[1];
952
+ }
953
+ }
954
+
955
+ if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
956
+ hClass += " hidden";
957
+ }
958
+
959
+ const header = new Header();
960
+ header.setInternals({
961
+ field: field,
962
+ label: label,
963
+ classes: hClass,
964
+ index: i,
965
+ mode: mode,
966
+ grid: grid,
967
+ labelKey: labelKey,
968
+ direction: direction,
969
+ });
970
+
971
+ headers.push(header);
972
+ }
973
+
974
+ this.setOption("headers", headers);
975
+ queueMicrotask(() => {
976
+ storeOrderStatement.call(this, this.getOption("features.autoInit"));
977
+ });
970
978
  }
971
979
 
972
980
  /**
@@ -974,79 +982,79 @@ function initGridAndStructs(hostConfig, headerOrderMap) {
974
982
  * @return {string}
975
983
  */
976
984
  export function getStoredOrderConfigKey() {
977
- return generateUniqueConfigKey("datatable", this?.id, "stored-order");
985
+ return generateUniqueConfigKey("datatable", this?.id, "stored-order");
978
986
  }
979
987
 
980
988
  /**
981
989
  * @private
982
990
  */
983
991
  function storeOrderStatement(doFetch) {
984
- const headers = this.getOption("headers");
985
- const statement = createOrderStatement(headers);
986
- setDataSource.call(this, {orderBy: statement}, doFetch);
992
+ const headers = this.getOption("headers");
993
+ const statement = createOrderStatement(headers);
994
+ setDataSource.call(this, { orderBy: statement }, doFetch);
987
995
 
988
- const host = findElementWithSelectorUpwards(this, "monster-host");
989
- if (!(host && this.id)) {
990
- return;
991
- }
996
+ const host = findElementWithSelectorUpwards(this, "monster-host");
997
+ if (!(host && this.id)) {
998
+ return;
999
+ }
992
1000
 
993
- const configKey = getStoredOrderConfigKey.call(this);
1001
+ const configKey = getStoredOrderConfigKey.call(this);
994
1002
 
995
- // statement explode with , and remove all empty
996
- const list = statement.split(",").filter((item) => item.trim() !== "");
997
- if (list.length === 0) {
998
- return;
999
- }
1003
+ // statement explode with , and remove all empty
1004
+ const list = statement.split(",").filter((item) => item.trim() !== "");
1005
+ if (list.length === 0) {
1006
+ return;
1007
+ }
1000
1008
 
1001
- host.setConfig(configKey, list);
1009
+ host.setConfig(configKey, list);
1002
1010
  }
1003
1011
 
1004
1012
  /**
1005
1013
  * @private
1006
1014
  */
1007
1015
  function updateGrid() {
1008
- if (!this[gridElementSymbol]) {
1009
- throw new Error("no grid element is defined");
1010
- }
1011
-
1012
- let gridTemplateColumns = "";
1013
-
1014
- const headers = this.getOption("headers");
1015
-
1016
- let styles = "";
1017
-
1018
- for (let i = 0; i < headers.length; i++) {
1019
- const header = headers[i];
1020
-
1021
- if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
1022
- styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
1023
- styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
1024
- } else {
1025
- gridTemplateColumns += `${header.grid} `;
1026
- }
1027
- }
1028
-
1029
- const sheet = new CSSStyleSheet();
1030
- if (styles !== "") sheet.replaceSync(styles);
1031
- this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
1032
-
1033
- const bodyWidth = this.parentNode.clientWidth;
1034
-
1035
- const breakpoint = this.getOption("responsive.breakpoint");
1036
- this[dataControlElementSymbol].classList.toggle(
1037
- "small",
1038
- bodyWidth <= breakpoint,
1039
- );
1040
-
1041
- if (bodyWidth > breakpoint) {
1042
- this[gridElementSymbol].style.gridTemplateColumns =
1043
- `${gridTemplateColumns}`;
1044
- this[gridHeadersElementSymbol].style.gridTemplateColumns =
1045
- `${gridTemplateColumns}`;
1046
- } else {
1047
- this[gridElementSymbol].style.gridTemplateColumns = "auto";
1048
- this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
1049
- }
1016
+ if (!this[gridElementSymbol]) {
1017
+ throw new Error("no grid element is defined");
1018
+ }
1019
+
1020
+ let gridTemplateColumns = "";
1021
+
1022
+ const headers = this.getOption("headers");
1023
+
1024
+ let styles = "";
1025
+
1026
+ for (let i = 0; i < headers.length; i++) {
1027
+ const header = headers[i];
1028
+
1029
+ if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
1030
+ styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
1031
+ styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
1032
+ } else {
1033
+ gridTemplateColumns += `${header.grid} `;
1034
+ }
1035
+ }
1036
+
1037
+ const sheet = new CSSStyleSheet();
1038
+ if (styles !== "") sheet.replaceSync(styles);
1039
+ this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
1040
+
1041
+ const bodyWidth = this.parentNode.clientWidth;
1042
+
1043
+ const breakpoint = this.getOption("responsive.breakpoint");
1044
+ this[dataControlElementSymbol].classList.toggle(
1045
+ "small",
1046
+ bodyWidth <= breakpoint,
1047
+ );
1048
+
1049
+ if (bodyWidth > breakpoint) {
1050
+ this[gridElementSymbol].style.gridTemplateColumns =
1051
+ `${gridTemplateColumns}`;
1052
+ this[gridHeadersElementSymbol].style.gridTemplateColumns =
1053
+ `${gridTemplateColumns}`;
1054
+ } else {
1055
+ this[gridElementSymbol].style.gridTemplateColumns = "auto";
1056
+ this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
1057
+ }
1050
1058
  }
1051
1059
 
1052
1060
  /**
@@ -1054,20 +1062,20 @@ function updateGrid() {
1054
1062
  * @param {Header[]} headers
1055
1063
  * @param {bool} doFetch
1056
1064
  */
1057
- function setDataSource({orderBy}, doFetch) {
1058
- const datasource = this[datasourceLinkedElementSymbol];
1065
+ function setDataSource({ orderBy }, doFetch) {
1066
+ const datasource = this[datasourceLinkedElementSymbol];
1059
1067
 
1060
- if (!datasource) {
1061
- return;
1062
- }
1068
+ if (!datasource) {
1069
+ return;
1070
+ }
1063
1071
 
1064
- if (isFunction(datasource?.setParameters)) {
1065
- datasource.setParameters({orderBy});
1066
- }
1072
+ if (isFunction(datasource?.setParameters)) {
1073
+ datasource.setParameters({ orderBy });
1074
+ }
1067
1075
 
1068
- if (doFetch !== false && isFunction(datasource?.fetch)) {
1069
- datasource.fetch();
1070
- }
1076
+ if (doFetch !== false && isFunction(datasource?.fetch)) {
1077
+ datasource.fetch();
1078
+ }
1071
1079
  }
1072
1080
 
1073
1081
  /**
@@ -1075,28 +1083,28 @@ function setDataSource({orderBy}, doFetch) {
1075
1083
  * @return {DataTable}
1076
1084
  */
1077
1085
  function initControlReferences() {
1078
- if (!this.shadowRoot) {
1079
- throw new Error("no shadow-root is defined");
1080
- }
1081
-
1082
- this[dataControlElementSymbol] = this.shadowRoot.querySelector(
1083
- "[data-monster-role=control]",
1084
- );
1085
-
1086
- this[gridElementSymbol] = this.shadowRoot.querySelector(
1087
- "[data-monster-role=datatable]",
1088
- );
1089
- this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
1090
- "[data-monster-role=datatable-headers]",
1091
- );
1092
- this[columnBarElementSymbol] =
1093
- this.shadowRoot.querySelector("monster-column-bar");
1094
-
1095
- this[copyAllElementSymbol] = this.shadowRoot.querySelector(
1096
- "[data-monster-role=copy-all]",
1097
- );
1098
-
1099
- return this;
1086
+ if (!this.shadowRoot) {
1087
+ throw new Error("no shadow-root is defined");
1088
+ }
1089
+
1090
+ this[dataControlElementSymbol] = this.shadowRoot.querySelector(
1091
+ "[data-monster-role=control]",
1092
+ );
1093
+
1094
+ this[gridElementSymbol] = this.shadowRoot.querySelector(
1095
+ "[data-monster-role=datatable]",
1096
+ );
1097
+ this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
1098
+ "[data-monster-role=datatable-headers]",
1099
+ );
1100
+ this[columnBarElementSymbol] =
1101
+ this.shadowRoot.querySelector("monster-column-bar");
1102
+
1103
+ this[copyAllElementSymbol] = this.shadowRoot.querySelector(
1104
+ "[data-monster-role=copy-all]",
1105
+ );
1106
+
1107
+ return this;
1100
1108
  }
1101
1109
 
1102
1110
  /**
@@ -1106,22 +1114,22 @@ function initControlReferences() {
1106
1114
  * @throws {Error} the datasource could not be initialized
1107
1115
  */
1108
1116
  function initOptionsFromArguments() {
1109
- const options = {};
1110
- const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
1111
-
1112
- if (selector) {
1113
- options.datasource = {selector: selector};
1114
- }
1115
-
1116
- const breakpoint = this.getAttribute(
1117
- ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
1118
- );
1119
- if (breakpoint) {
1120
- options.responsive = {};
1121
- options.responsive.breakpoint = parseInt(breakpoint);
1122
- }
1123
-
1124
- return options;
1117
+ const options = {};
1118
+ const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
1119
+
1120
+ if (selector) {
1121
+ options.datasource = { selector: selector };
1122
+ }
1123
+
1124
+ const breakpoint = this.getAttribute(
1125
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
1126
+ );
1127
+ if (breakpoint) {
1128
+ options.responsive = {};
1129
+ options.responsive.breakpoint = parseInt(breakpoint);
1130
+ }
1131
+
1132
+ return options;
1125
1133
  }
1126
1134
 
1127
1135
  /**
@@ -1129,7 +1137,7 @@ function initOptionsFromArguments() {
1129
1137
  * @return {string}
1130
1138
  */
1131
1139
  function getEmptyTemplate() {
1132
- return `<monster-state data-monster-role="empty-without-action">
1140
+ return `<monster-state data-monster-role="empty-without-action">
1133
1141
  <div part="visual">
1134
1142
  <svg width="4rem" height="4rem" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
1135
1143
  <path d="m21.5 22h-19c-1.378 0-2.5-1.121-2.5-2.5v-7c0-.07.015-.141.044-.205l3.969-8.82c.404-.896 1.299-1.475 2.28-1.475h11.414c.981 0 1.876.579 2.28 1.475l3.969 8.82c.029.064.044.135.044.205v7c0 1.379-1.122 2.5-2.5 2.5zm-20.5-9.393v6.893c0 .827.673 1.5 1.5 1.5h19c.827 0 1.5-.673 1.5-1.5v-6.893l-3.925-8.723c-.242-.536-.779-.884-1.368-.884h-11.414c-.589 0-1.126.348-1.368.885z"/>
@@ -1147,8 +1155,8 @@ function getEmptyTemplate() {
1147
1155
  * @return {string}
1148
1156
  */
1149
1157
  function getTemplate() {
1150
- // language=HTML
1151
- return `
1158
+ // language=HTML
1159
+ return `
1152
1160
  <div data-monster-role="control" part="control" data-monster-attributes="class path:classes.control">
1153
1161
  <template id="headers-row">
1154
1162
  <div data-monster-attributes="class path:headers-row.classes,