@schukai/monster 3.97.1 → 3.98.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 (30) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +1 -1
  3. package/source/components/accessibility/locale-picker.mjs +549 -543
  4. package/source/components/datatable/columnbar.mjs +50 -3
  5. package/source/components/datatable/constants.mjs +7 -0
  6. package/source/components/datatable/datatable/header.mjs +1 -0
  7. package/source/components/datatable/datatable.mjs +1168 -942
  8. package/source/components/datatable/filter/date-range.mjs +145 -14
  9. package/source/components/datatable/filter/input.mjs +50 -3
  10. package/source/components/datatable/filter/range.mjs +92 -7
  11. package/source/components/datatable/filter-button.mjs +46 -3
  12. package/source/components/datatable/filter.mjs +95 -10
  13. package/source/components/datatable/pagination.mjs +82 -7
  14. package/source/components/datatable/save-button.mjs +46 -3
  15. package/source/components/datatable/style/datatable.pcss +1 -0
  16. package/source/components/datatable/stylesheet/datatable.mjs +7 -14
  17. package/source/components/form/field-set.mjs +77 -6
  18. package/source/components/form/select.mjs +149 -30
  19. package/source/components/layout/details.mjs +50 -3
  20. package/source/components/layout/tabs.mjs +50 -3
  21. package/source/components/notify/monitor-attribute-errors.mjs +235 -0
  22. package/source/components/notify/style/monitor-attribute-errors.pcss +0 -0
  23. package/source/components/notify/stylesheet/monitor-attribute-errors.mjs +38 -0
  24. package/source/dom/customelement.mjs +3 -3
  25. package/source/i18n/util.mjs +122 -122
  26. package/source/types/version.mjs +1 -1
  27. package/test/cases/monster.mjs +1 -1
  28. package/test/web/import.js +1 -0
  29. package/test/web/test.html +2 -2
  30. package/test/web/tests.js +432 -13
@@ -12,77 +12,80 @@
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_FEATURES,
50
+ ATTRIBUTE_DATATABLE_MODE_VISIBLE,
51
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
52
+ ATTRIBUTE_DATATABLE_MODE_FIXED,
52
53
  } from "./constants.mjs";
53
- import { instanceSymbol } from "../../constants.mjs";
54
+ import {instanceSymbol} from "../../constants.mjs";
54
55
  import {
55
- Header,
56
- createOrderStatement,
57
- DIRECTION_ASC,
58
- DIRECTION_DESC,
59
- DIRECTION_NONE,
56
+ Header,
57
+ createOrderStatement,
58
+ DIRECTION_ASC,
59
+ DIRECTION_DESC,
60
+ DIRECTION_NONE,
60
61
  } from "./datatable/header.mjs";
61
- import { DatatableStyleSheet } from "./stylesheet/datatable.mjs";
62
+ import {DatatableStyleSheet} from "./stylesheet/datatable.mjs";
62
63
  import {
63
- handleDataSourceChanges,
64
- datasourceLinkedElementSymbol,
64
+ handleDataSourceChanges,
65
+ datasourceLinkedElementSymbol,
65
66
  } from "./util.mjs";
66
67
  import "./columnbar.mjs";
67
68
  import "./filter-button.mjs";
68
69
  import {
69
- findElementWithSelectorUpwards,
70
- getDocument,
71
- getWindow,
70
+ findElementWithSelectorUpwards,
71
+ getDocument,
72
+ getWindow,
72
73
  } 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";
74
+ import {addAttributeToken} from "../../dom/attributes.mjs";
75
+ import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
76
+ import {getDocumentTranslations} from "../../i18n/translations.mjs";
76
77
  import "../state/state.mjs";
77
78
  import "../host/collapse.mjs";
78
- import { generateUniqueConfigKey } from "../host/util.mjs";
79
+ import {generateUniqueConfigKey} from "../host/util.mjs";
79
80
 
80
81
  import "./datasource/dom.mjs";
81
82
  import "./datasource/rest.mjs";
82
83
 
83
84
  import "../form/context-help.mjs";
85
+ import {getLocaleOfDocument} from "../../dom/locale.mjs";
84
86
 
85
- export { DataTable };
87
+
88
+ export {DataTable};
86
89
 
87
90
  /**
88
91
  * @private
@@ -120,436 +123,445 @@ const copyAllElementSymbol = Symbol("copyAllElement");
120
123
  */
121
124
  const resizeObserverSymbol = Symbol("resizeObserver");
122
125
 
123
- /**
124
- * The DataTable component is used to show the data from a data source.
125
- *
126
- * @copyright schukai GmbH
127
- * @summary A data table
128
-
129
- */
130
-
131
126
  /**
132
127
  * A DataTable
133
128
  *
134
129
  * @fragments /fragments/components/datatable/datatable/
135
130
  *
136
- * @example /examples/components/datatable/empty
137
- * @example /examples/components/datatable/data-using-javascript
138
- * @example /examples/components/datatable/alignment
139
- * @example /examples/components/datatable/row-mode
140
- * @example /examples/components/datatable/grid-template
141
- * @example /examples/components/datatable/overview-class
142
- * @example /examples/components/datatable/datasource
143
- * @example /examples/components/datatable/pagination
144
- * @example /examples/components/datatable/filter
131
+ * @example /examples/components/datatable/empty The empty state
132
+ * @example /examples/components/datatable/data-using-javascript The data using javascript
133
+ * @example /examples/components/datatable/alignment The alignment
134
+ * @example /examples/components/datatable/row-mode The row mode
135
+ * @example /examples/components/datatable/grid-template The grid template
136
+ * @example /examples/components/datatable/overview-class The overview class
137
+ * @example /examples/components/datatable/datasource Use a datasource
138
+ * @example /examples/components/datatable/pagination Use pagination
139
+ * @example /examples/components/datatable/filter Filer the data
140
+ * @example /examples/components/datatable/ Select rows
145
141
  *
146
142
  * @copyright schukai GmbH
147
143
  * @summary A beautiful and highly customizable data table. It can be used to display data from a data source.
148
144
  * @fires monster-datatable-row-copied
149
145
  * @fires monster-datatable-row-removed
150
146
  * @fires monster-datatable-row-added
147
+ * @fires monster-datatable-row-selected
148
+ * @fires monster-datatable-row-deselected
149
+ * @fires monster-datatable-all-rows-selected
150
+ * @fires monster-datatable-all-rows-deselected
151
+ * @fires monster-datatable-selection-changed
151
152
  **/
152
153
  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 {boolean} features.copyAll Copy all feature
187
- * @property {boolean} features.help Help feature
188
- * @property {Object} templateMapping Template mapping
189
- * @property {string} templateMapping.row-key Row key
190
- * @property {string} templateMapping.filter-id Filter id
191
- **/
192
- get defaults() {
193
- return Object.assign(
194
- {},
195
- super.defaults,
196
- {
197
- templates: {
198
- main: getTemplate(),
199
- emptyState: getEmptyTemplate(),
200
- },
201
-
202
- datasource: {
203
- selector: null,
204
- },
205
-
206
- mapping: {
207
- data: "dataset",
208
- },
209
-
210
- data: [],
211
- headers: [],
212
-
213
- responsive: {
214
- breakpoint: 900,
215
- },
216
-
217
- labels: {
218
- theListContainsNoEntries: "The list contains no entries",
219
- copyAll: "Copy all",
220
- helpText:
221
- "<p>You can copy the values from individual rows<br>" +
222
- "to the clipboard by double-clicking on the relevant column.</p>" +
223
- "<p>To copy an entire row, hold down the Shift key while clicking.<br>" +
224
- "If you want to copy all rows, you can use the <strong>Copy All</strong> button.</p>",
225
- },
226
-
227
- classes: {
228
- control: "monster-theme-control-container-1",
229
- container: "",
230
- row: "monster-theme-control-row-1",
231
- },
232
-
233
- features: {
234
- settings: true,
235
- footer: true,
236
- autoInit: true,
237
- doubleClickCopyToClipboard: true,
238
- copyAll: true,
239
- help: true,
240
- },
241
-
242
- copy: {
243
- delimiter: ";",
244
- quoteOpen: '"',
245
- quoteClose: '"',
246
- rowBreak: "\n",
247
- },
248
-
249
- templateMapping: {
250
- "row-key": null,
251
- "filter-id": null,
252
- },
253
- },
254
- initOptionsFromArguments.call(this),
255
- );
256
- }
257
-
258
- /**
259
- *
260
- * @param {string} selector
261
- * @return {NodeListOf<*>}
262
- */
263
- getGridElements(selector) {
264
- return this[gridElementSymbol].querySelectorAll(selector);
265
- }
266
-
267
- /**
268
- *
269
- * @return {string}
270
- */
271
- static getTag() {
272
- return "monster-datatable";
273
- }
274
-
275
- /**
276
- * @return {void}
277
- */
278
- disconnectedCallback() {
279
- super.disconnectedCallback();
280
- if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
281
- this[resizeObserverSymbol].disconnect();
282
- }
283
- }
284
-
285
- /**
286
- * @return {void}
287
- */
288
- connectedCallback() {
289
- const self = this;
290
- super.connectedCallback();
291
-
292
- this[resizeObserverSymbol] = new ResizeObserver((entries) => {
293
- updateGrid.call(self);
294
- });
295
-
296
- this[resizeObserverSymbol].observe(this.parentNode);
297
- }
298
-
299
- /**
300
- * @return void
301
- */
302
- [assembleMethodSymbol]() {
303
- const rawKey = this.getOption("templateMapping.row-key");
304
-
305
- if (rawKey === null) {
306
- if (this.id !== null && this.id !== "") {
307
- const rawKey = this.getOption("templateMapping.row-key");
308
- if (rawKey === null) {
309
- this.setOption("templateMapping.row-key", this.id + "-row");
310
- }
311
- } else {
312
- this.setOption("templateMapping.row-key", "row");
313
- }
314
- }
315
-
316
- if (this.id !== null && this.id !== "") {
317
- this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
318
- } else {
319
- this.setOption("templateMapping.filter-id", "filter");
320
- }
321
-
322
- super[assembleMethodSymbol]();
323
-
324
- initControlReferences.call(this);
325
- initEventHandler.call(this);
326
-
327
- const selector = this.getOption("datasource.selector");
328
-
329
- if (isString(selector)) {
330
- const element = findElementWithSelectorUpwards(this, selector);
331
- if (element === null) {
332
- throw new Error("the selector must match exactly one element");
333
- }
334
-
335
- if (!isInstance(element, Datasource)) {
336
- throw new TypeError("the element must be a datasource");
337
- }
338
-
339
- this[datasourceLinkedElementSymbol] = element;
340
-
341
- queueMicrotask(() => {
342
- handleDataSourceChanges.call(this);
343
- element.datasource.attachObserver(
344
- new Observer(handleDataSourceChanges.bind(this)),
345
- );
346
- });
347
- }
348
-
349
- getHostConfig
350
- .call(this, getColumnVisibilityConfigKey)
351
- .then((config) => {
352
- const headerOrderMap = new Map();
353
-
354
- getHostConfig
355
- .call(this, getStoredOrderConfigKey)
356
- .then((orderConfig) => {
357
- if (isArray(orderConfig) || orderConfig.length > 0) {
358
- for (let i = 0; i < orderConfig.length; i++) {
359
- const item = orderConfig[i];
360
- const parts = item.split(" ");
361
- const field = parts[0];
362
- const direction = parts[1] || DIRECTION_ASC;
363
- headerOrderMap.set(field, direction);
364
- }
365
- }
366
- })
367
- .then(() => {
368
- try {
369
- initGridAndStructs.call(this, config, headerOrderMap);
370
- } catch (error) {
371
- addAttributeToken(
372
- this,
373
- ATTRIBUTE_ERRORMESSAGE,
374
- error?.message || error.toString(),
375
- );
376
- }
377
-
378
- updateColumnBar.call(this);
379
- })
380
- .catch((error) => {
381
- addAttributeToken(
382
- this,
383
- ATTRIBUTE_ERRORMESSAGE,
384
- error?.message || error.toString(),
385
- );
386
- });
387
- })
388
- .catch((error) => {
389
- addAttributeToken(
390
- this,
391
- ATTRIBUTE_ERRORMESSAGE,
392
- error?.message || error.toString(),
393
- );
394
- });
395
- }
396
-
397
- /**
398
- * @return {CSSStyleSheet[]}
399
- */
400
- static getCSSStyleSheet() {
401
- return [DatatableStyleSheet];
402
- }
403
-
404
- /**
405
- * Copy a row from the datatable
406
- *
407
- * @param {number|string} fromIndex
408
- * @param {number|string} toIndex
409
- * @return {DataTable}
410
- * @fires monster-datatable-row-copied
411
- */
412
- copyRow(fromIndex, toIndex) {
413
- const datasource = this[datasourceLinkedElementSymbol];
414
- if (!datasource) {
415
- return this;
416
- }
417
- let d = datasource.data;
418
- let c = clone(d);
419
-
420
- let rows = c;
421
- const mapping = this.getOption("mapping.data");
422
-
423
- if (mapping) {
424
- rows = c?.[mapping];
425
- }
426
-
427
- if (rows === undefined || rows === null) {
428
- rows = [];
429
- }
430
-
431
- if (toIndex === undefined) {
432
- toIndex = rows.length;
433
- }
434
-
435
- if (isString(fromIndex)) {
436
- fromIndex = parseInt(fromIndex);
437
- }
438
- if (isString(toIndex)) {
439
- toIndex = parseInt(toIndex);
440
- }
441
-
442
- if (toIndex < 0 || toIndex > rows.length) {
443
- throw new RangeError("index out of bounds");
444
- }
445
-
446
- validateArray(rows);
447
- validateInteger(fromIndex);
448
- validateInteger(toIndex);
449
-
450
- if (fromIndex < 0 || fromIndex >= rows.length) {
451
- throw new RangeError("index out of bounds");
452
- }
453
-
454
- rows.splice(toIndex, 0, clone(rows[fromIndex]));
455
- datasource.data = c;
456
-
457
- fireCustomEvent(this, "monster-datatable-row-copied", {
458
- index: toIndex,
459
- });
460
-
461
- return this;
462
- }
463
-
464
- /**
465
- * Remove a row from the datatable
466
- *
467
- * @param {number|string} index
468
- * @return {DataTable}
469
- * @fires monster-datatable-row-removed
470
- */
471
- removeRow(index) {
472
- const datasource = this[datasourceLinkedElementSymbol];
473
- if (!datasource) {
474
- return this;
475
- }
476
- let d = datasource.data;
477
- let c = clone(d);
478
-
479
- let rows = c;
480
- const mapping = this.getOption("mapping.data");
481
-
482
- if (mapping) {
483
- rows = c?.[mapping];
484
- }
485
-
486
- if (rows === undefined || rows === null) {
487
- rows = [];
488
- }
489
-
490
- if (isString(index)) {
491
- index = parseInt(index);
492
- }
493
-
494
- validateArray(rows);
495
- validateInteger(index);
496
-
497
- if (index < 0 || index >= rows.length) {
498
- throw new RangeError("index out of bounds");
499
- }
500
- if (mapping) {
501
- rows = c?.[mapping];
502
- }
503
-
504
- rows.splice(index, 1);
505
- datasource.data = c;
506
-
507
- fireCustomEvent(this, "monster-datatable-row-removed", {
508
- index: index,
509
- });
510
-
511
- return this;
512
- }
513
-
514
- /**
515
- * Add a row to the datatable
516
- *
517
- * @param {Object} data
518
- * @return {DataTable}
519
- *
520
- * @fires monster-datatable-row-added
521
- **/
522
- addRow(data) {
523
- const datasource = this[datasourceLinkedElementSymbol];
524
- if (!datasource) {
525
- return this;
526
- }
527
- let d = datasource.data;
528
- let c = clone(d);
529
-
530
- let rows = c;
531
-
532
- const mapping = this.getOption("mapping.data");
533
- if (mapping) {
534
- rows = c?.[mapping];
535
- }
536
-
537
- if (rows === undefined || rows === null) {
538
- rows = [];
539
- }
540
-
541
- validateArray(rows);
542
- validateObject(data);
543
-
544
- rows.push(data);
545
- datasource.data = c;
546
-
547
- fireCustomEvent(this, "monster-datatable-row-added", {
548
- index: rows.length - 1,
549
- });
550
-
551
- return this;
552
- }
154
+ /**
155
+ * This method is called by the `instanceof` operator.
156
+ * @return {symbol}
157
+ */
158
+ static get [instanceSymbol]() {
159
+ return Symbol.for("@schukai/monster/components/datatable@@instance");
160
+ }
161
+
162
+ /**
163
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
164
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
165
+ *
166
+ * The individual configuration values can be found in the table.
167
+ *
168
+ * @property {Object} templates Template definitions
169
+ * @property {string} templates.main Main template
170
+ * @property {Object} datasource Datasource configuration
171
+ * @property {string} datasource.selector Selector for the datasource
172
+ * @property {Object} mapping Mapping configuration
173
+ * @property {string} mapping.data Data mapping
174
+ * @property {Array} data Data
175
+ * @property {Array} headers Headers
176
+ * @property {Object} responsive Responsive configuration
177
+ * @property {number} responsive.breakpoint Breakpoint for responsive mode
178
+ * @property {Object} labels Labels
179
+ * @property {string} labels.theListContainsNoEntries Label for empty state
180
+ * @property {Object} classes Classes
181
+ * @property {string} classes.container Container class
182
+ * @property {Object} features Features
183
+ * @property {boolean} features.settings Settings feature
184
+ * @property {boolean} features.footer Footer feature
185
+ * @property {boolean} features.autoInit Auto init feature (init datasource automatically)
186
+ * @property {boolean} features.doubleClickCopyToClipboard Double click copy to clipboard feature
187
+ * @property {boolean} features.copyAll Copy all feature
188
+ * @property {boolean} features.help Help feature
189
+ * @property {Object} templateMapping Template mapping
190
+ * @property {string} templateMapping.row-key Row key
191
+ * @property {string} templateMapping.filter-id Filter id
192
+ **/
193
+ get defaults() {
194
+ return Object.assign(
195
+ {},
196
+ super.defaults,
197
+ {
198
+ templates: {
199
+ main: getTemplate(),
200
+ emptyState: getEmptyTemplate(),
201
+ },
202
+
203
+ datasource: {
204
+ selector: null,
205
+ },
206
+
207
+ mapping: {
208
+ data: "dataset",
209
+ },
210
+
211
+ data: [],
212
+ headers: [],
213
+
214
+ responsive: {
215
+ breakpoint: 900,
216
+ },
217
+
218
+ labels: getTranslations(),
219
+
220
+ classes: {
221
+ control: "monster-theme-control-container-1",
222
+ container: "",
223
+ row: "monster-theme-control-row-1",
224
+ },
225
+
226
+ features: {
227
+ settings: true,
228
+ footer: true,
229
+ autoInit: true,
230
+ doubleClickCopyToClipboard: true,
231
+ copyAll: true,
232
+ help: true,
233
+ },
234
+
235
+ copy: {
236
+ delimiter: ";",
237
+ quoteOpen: '"',
238
+ quoteClose: '"',
239
+ rowBreak: "\n",
240
+ },
241
+
242
+ templateMapping: {
243
+ "row-key": null,
244
+ "filter-id": null,
245
+ },
246
+ },
247
+ initOptionsFromArguments.call(this),
248
+ );
249
+ }
250
+
251
+ /**
252
+ *
253
+ * @param {string} selector
254
+ * @return {NodeListOf<*>}
255
+ */
256
+ getGridElements(selector) {
257
+ return this[gridElementSymbol].querySelectorAll(selector);
258
+ }
259
+
260
+ /**
261
+ *
262
+ * @return {string}
263
+ */
264
+ static getTag() {
265
+ return "monster-datatable";
266
+ }
267
+
268
+ /**
269
+ * @return {void}
270
+ */
271
+ disconnectedCallback() {
272
+ super.disconnectedCallback();
273
+ if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
274
+ this[resizeObserverSymbol].disconnect();
275
+ }
276
+ }
277
+
278
+ /**
279
+ * @return {void}
280
+ */
281
+ connectedCallback() {
282
+ const self = this;
283
+ super.connectedCallback();
284
+
285
+ this[resizeObserverSymbol] = new ResizeObserver((entries) => {
286
+ updateGrid.call(self);
287
+ });
288
+
289
+ this[resizeObserverSymbol].observe(this.parentNode);
290
+ }
291
+
292
+ /**
293
+ * Get the row number of the selected rows as an array
294
+ *
295
+ * @returns {number[]}
296
+ */
297
+ getSelectedRows() {
298
+ const rows = this.getGridElements(`[data-monster-role="select-row"]`);
299
+ const selectedRows = [];
300
+ rows.forEach((row) => {
301
+ if (row.checked) {
302
+ const key = row.parentNode.getAttribute("data-monster-insert-reference");
303
+ const index = key.split("-").pop();
304
+ selectedRows.push(parseInt(index, 10));
305
+ }
306
+ });
307
+
308
+ return selectedRows;
309
+ }
310
+
311
+ /**
312
+ * @return void
313
+ */
314
+ [assembleMethodSymbol]() {
315
+ const rawKey = this.getOption("templateMapping.row-key");
316
+
317
+ if (rawKey === null) {
318
+ if (this.id !== null && this.id !== "") {
319
+ const rawKey = this.getOption("templateMapping.row-key");
320
+ if (rawKey === null) {
321
+ this.setOption("templateMapping.row-key", this.id + "-row");
322
+ }
323
+ } else {
324
+ this.setOption("templateMapping.row-key", "row");
325
+ }
326
+ }
327
+
328
+ if (this.id !== null && this.id !== "") {
329
+ this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
330
+ } else {
331
+ this.setOption("templateMapping.filter-id", "filter");
332
+ }
333
+
334
+ super[assembleMethodSymbol]();
335
+
336
+ initControlReferences.call(this);
337
+ initEventHandler.call(this);
338
+
339
+ const selector = this.getOption("datasource.selector");
340
+
341
+ if (isString(selector)) {
342
+ const element = findElementWithSelectorUpwards(this, selector);
343
+ if (element === null) {
344
+ throw new Error("the selector must match exactly one element");
345
+ }
346
+
347
+ if (!isInstance(element, Datasource)) {
348
+ throw new TypeError("the element must be a datasource");
349
+ }
350
+
351
+ this[datasourceLinkedElementSymbol] = element;
352
+
353
+ queueMicrotask(() => {
354
+ handleDataSourceChanges.call(this);
355
+ element.datasource.attachObserver(
356
+ new Observer(handleDataSourceChanges.bind(this)),
357
+ );
358
+ });
359
+ }
360
+
361
+ getHostConfig
362
+ .call(this, getColumnVisibilityConfigKey)
363
+ .then((config) => {
364
+ const headerOrderMap = new Map();
365
+
366
+ getHostConfig
367
+ .call(this, getStoredOrderConfigKey)
368
+ .then((orderConfig) => {
369
+ if (isArray(orderConfig) || orderConfig.length > 0) {
370
+ for (let i = 0; i < orderConfig.length; i++) {
371
+ const item = orderConfig[i];
372
+ const parts = item.split(" ");
373
+ const field = parts[0];
374
+ const direction = parts[1] || DIRECTION_ASC;
375
+ headerOrderMap.set(field, direction);
376
+ }
377
+ }
378
+ })
379
+ .then(() => {
380
+ try {
381
+ initGridAndStructs.call(this, config, headerOrderMap);
382
+ } catch (error) {
383
+ addAttributeToken(
384
+ this,
385
+ ATTRIBUTE_ERRORMESSAGE,
386
+ error?.message || error.toString(),
387
+ );
388
+ }
389
+
390
+ updateColumnBar.call(this);
391
+ })
392
+ .catch((error) => {
393
+ addAttributeToken(
394
+ this,
395
+ ATTRIBUTE_ERRORMESSAGE,
396
+ error?.message || error.toString(),
397
+ );
398
+ });
399
+ })
400
+ .catch((error) => {
401
+ addAttributeToken(
402
+ this,
403
+ ATTRIBUTE_ERRORMESSAGE,
404
+ error?.message || error.toString(),
405
+ );
406
+ });
407
+ }
408
+
409
+ /**
410
+ * @return {CSSStyleSheet[]}
411
+ */
412
+ static getCSSStyleSheet() {
413
+ return [DatatableStyleSheet];
414
+ }
415
+
416
+ /**
417
+ * Copy a row from the datatable
418
+ *
419
+ * @param {number|string} fromIndex
420
+ * @param {number|string} toIndex
421
+ * @return {DataTable}
422
+ * @fires monster-datatable-row-copied
423
+ */
424
+ copyRow(fromIndex, toIndex) {
425
+ const datasource = this[datasourceLinkedElementSymbol];
426
+ if (!datasource) {
427
+ return this;
428
+ }
429
+ let d = datasource.data;
430
+ let c = clone(d);
431
+
432
+ let rows = c;
433
+ const mapping = this.getOption("mapping.data");
434
+
435
+ if (mapping) {
436
+ rows = c?.[mapping];
437
+ }
438
+
439
+ if (rows === undefined || rows === null) {
440
+ rows = [];
441
+ }
442
+
443
+ if (toIndex === undefined) {
444
+ toIndex = rows.length;
445
+ }
446
+
447
+ if (isString(fromIndex)) {
448
+ fromIndex = parseInt(fromIndex);
449
+ }
450
+ if (isString(toIndex)) {
451
+ toIndex = parseInt(toIndex);
452
+ }
453
+
454
+ if (toIndex < 0 || toIndex > rows.length) {
455
+ throw new RangeError("index out of bounds");
456
+ }
457
+
458
+ validateArray(rows);
459
+ validateInteger(fromIndex);
460
+ validateInteger(toIndex);
461
+
462
+ if (fromIndex < 0 || fromIndex >= rows.length) {
463
+ throw new RangeError("index out of bounds");
464
+ }
465
+
466
+ rows.splice(toIndex, 0, clone(rows[fromIndex]));
467
+ datasource.data = c;
468
+
469
+ fireCustomEvent(this, "monster-datatable-row-copied", {
470
+ index: toIndex,
471
+ });
472
+
473
+ return this;
474
+ }
475
+
476
+ /**
477
+ * Remove a row from the datatable
478
+ *
479
+ * @param {number|string} index
480
+ * @return {DataTable}
481
+ * @fires monster-datatable-row-removed
482
+ */
483
+ removeRow(index) {
484
+ const datasource = this[datasourceLinkedElementSymbol];
485
+ if (!datasource) {
486
+ return this;
487
+ }
488
+ let d = datasource.data;
489
+ let c = clone(d);
490
+
491
+ let rows = c;
492
+ const mapping = this.getOption("mapping.data");
493
+
494
+ if (mapping) {
495
+ rows = c?.[mapping];
496
+ }
497
+
498
+ if (rows === undefined || rows === null) {
499
+ rows = [];
500
+ }
501
+
502
+ if (isString(index)) {
503
+ index = parseInt(index);
504
+ }
505
+
506
+ validateArray(rows);
507
+ validateInteger(index);
508
+
509
+ if (index < 0 || index >= rows.length) {
510
+ throw new RangeError("index out of bounds");
511
+ }
512
+ if (mapping) {
513
+ rows = c?.[mapping];
514
+ }
515
+
516
+ rows.splice(index, 1);
517
+ datasource.data = c;
518
+
519
+ fireCustomEvent(this, "monster-datatable-row-removed", {
520
+ index: index,
521
+ });
522
+
523
+ return this;
524
+ }
525
+
526
+ /**
527
+ * Add a row to the datatable
528
+ *
529
+ * @param {Object} data
530
+ * @return {DataTable}
531
+ *
532
+ * @fires monster-datatable-row-added
533
+ **/
534
+ addRow(data) {
535
+ const datasource = this[datasourceLinkedElementSymbol];
536
+ if (!datasource) {
537
+ return this;
538
+ }
539
+ let d = datasource.data;
540
+ let c = clone(d);
541
+
542
+ let rows = c;
543
+
544
+ const mapping = this.getOption("mapping.data");
545
+ if (mapping) {
546
+ rows = c?.[mapping];
547
+ }
548
+
549
+ if (rows === undefined || rows === null) {
550
+ rows = [];
551
+ }
552
+
553
+ validateArray(rows);
554
+ validateObject(data);
555
+
556
+ rows.push(data);
557
+ datasource.data = c;
558
+
559
+ fireCustomEvent(this, "monster-datatable-row-added", {
560
+ index: rows.length - 1,
561
+ });
562
+
563
+ return this;
564
+ }
553
565
  }
554
566
 
555
567
  /**
@@ -557,7 +569,7 @@ class DataTable extends CustomElement {
557
569
  * @return {string}
558
570
  */
559
571
  function getColumnVisibilityConfigKey() {
560
- return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
572
+ return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
561
573
  }
562
574
 
563
575
  /**
@@ -565,7 +577,7 @@ function getColumnVisibilityConfigKey() {
565
577
  * @return {string}
566
578
  */
567
579
  function getFilterConfigKey() {
568
- return generateUniqueConfigKey("datatable", this?.id, "filter");
580
+ return generateUniqueConfigKey("datatable", this?.id, "filter");
569
581
  }
570
582
 
571
583
  /**
@@ -573,410 +585,621 @@ function getFilterConfigKey() {
573
585
  * @return {Promise}
574
586
  */
575
587
  function getHostConfig(callback) {
576
- const host = findElementWithSelectorUpwards(this, "monster-host");
577
-
578
- if (!(host && this.id)) {
579
- return Promise.resolve({});
580
- }
581
-
582
- if (!host || !isFunction(host?.getConfig)) {
583
- throw new TypeError("the host must be a monster-host");
584
- }
585
-
586
- const configKey = callback.call(this);
587
- return host.hasConfig(configKey).then((hasConfig) => {
588
- if (hasConfig) {
589
- return host.getConfig(configKey);
590
- } else {
591
- return {};
592
- }
593
- });
588
+ const host = findElementWithSelectorUpwards(this, "monster-host");
589
+
590
+ if (!host) {
591
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, "no host found");
592
+ return Promise.resolve({});
593
+ }
594
+
595
+ if (!this.id) {
596
+ addAttributeToken(
597
+ this,
598
+ ATTRIBUTE_ERRORMESSAGE,
599
+ "no id found; id is required for config",
600
+ );
601
+ return Promise.resolve({});
602
+ }
603
+
604
+ if (!host || !isFunction(host?.getConfig)) {
605
+ throw new TypeError("the host must be a monster-host");
606
+ }
607
+
608
+ const configKey = callback.call(this);
609
+ return host.hasConfig(configKey).then((hasConfig) => {
610
+ if (hasConfig) {
611
+ return host.getConfig(configKey);
612
+ } else {
613
+ return {};
614
+ }
615
+ });
594
616
  }
595
617
 
596
618
  /**
597
619
  * @private
598
620
  */
599
621
  function updateColumnBar() {
600
- if (!this[columnBarElementSymbol]) {
601
- return;
602
- }
603
-
604
- const columns = [];
605
- for (const header of this.getOption("headers")) {
606
- const mode = header.getInternal("mode");
607
-
608
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
609
- continue;
610
- }
611
-
612
- columns.push({
613
- visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
614
- name: header.label,
615
- index: header.index,
616
- });
617
- }
618
-
619
- this[columnBarElementSymbol].setOption("columns", columns);
622
+ if (!this[columnBarElementSymbol]) {
623
+ return;
624
+ }
625
+
626
+ const columns = [];
627
+ for (const header of this.getOption("headers")) {
628
+ const mode = header.getInternal("mode");
629
+
630
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
631
+ continue;
632
+ }
633
+
634
+ columns.push({
635
+ visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
636
+ name: header.label,
637
+ index: header.index,
638
+ });
639
+ }
640
+
641
+ this[columnBarElementSymbol].setOption("columns", columns);
620
642
  }
621
643
 
622
644
  /**
623
645
  * @private
624
646
  */
625
647
  function updateHeaderFromColumnBar() {
626
- if (!this[columnBarElementSymbol]) {
627
- return;
628
- }
648
+ if (!this[columnBarElementSymbol]) {
649
+ return;
650
+ }
629
651
 
630
- const options = this[columnBarElementSymbol].getOption("columns");
631
- if (!isArray(options)) return;
652
+ const options = this[columnBarElementSymbol].getOption("columns");
653
+ if (!isArray(options)) return;
632
654
 
633
- const invisibleMap = {};
655
+ const invisibleMap = {};
634
656
 
635
- for (let i = 0; i < options.length; i++) {
636
- const option = options[i];
637
- invisibleMap[option.index] = option.visible;
638
- }
657
+ for (let i = 0; i < options.length; i++) {
658
+ const option = options[i];
659
+ invisibleMap[option.index] = option.visible;
660
+ }
639
661
 
640
- for (const header of this.getOption("headers")) {
641
- const mode = header.getInternal("mode");
662
+ for (const header of this.getOption("headers")) {
663
+ const mode = header.getInternal("mode");
642
664
 
643
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
644
- continue;
645
- }
665
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
666
+ continue;
667
+ }
646
668
 
647
- if (invisibleMap[header.index] === false) {
648
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
649
- } else {
650
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
651
- }
652
- }
669
+ if (invisibleMap[header.index] === false) {
670
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
671
+ } else {
672
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
673
+ }
674
+ }
653
675
  }
654
676
 
655
677
  /**
656
678
  * @private
657
679
  */
658
680
  function updateConfigColumnBar() {
659
- if (!this[columnBarElementSymbol]) {
660
- return;
661
- }
662
-
663
- const options = this[columnBarElementSymbol].getOption("columns");
664
- if (!isArray(options)) return;
665
-
666
- const map = {};
667
- for (let i = 0; i < options.length; i++) {
668
- const option = options[i];
669
- map[option.name] = option.visible;
670
- }
671
-
672
- const host = findElementWithSelectorUpwards(this, "monster-host");
673
- if (!(host && this.id)) {
674
- return;
675
- }
676
- const configKey = getColumnVisibilityConfigKey.call(this);
677
-
678
- try {
679
- host.setConfig(configKey, map);
680
- } catch (error) {
681
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
682
- }
681
+ if (!this[columnBarElementSymbol]) {
682
+ return;
683
+ }
684
+
685
+ const options = this[columnBarElementSymbol].getOption("columns");
686
+ if (!isArray(options)) return;
687
+
688
+ const map = {};
689
+ for (let i = 0; i < options.length; i++) {
690
+ const option = options[i];
691
+ map[option.name] = option.visible;
692
+ }
693
+
694
+ const host = findElementWithSelectorUpwards(this, "monster-host");
695
+ if (!(host && this.id)) {
696
+ return;
697
+ }
698
+ const configKey = getColumnVisibilityConfigKey.call(this);
699
+
700
+ try {
701
+ host.setConfig(configKey, map);
702
+ } catch (error) {
703
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
704
+ }
683
705
  }
684
706
 
685
707
  /**
686
708
  * @private
687
709
  */
688
710
  function initEventHandler() {
689
- const self = this;
690
-
691
- const quoteOpenChar = this.getOption("copy.quoteOpen");
692
- const quoteCloseChar = this.getOption("copy.quoteClose");
693
- const delimiterChar = this.getOption("copy.delimiter");
694
- const rowBreak = this.getOption("copy.rowBreak");
695
-
696
- self[columnBarElementSymbol].attachObserver(
697
- new Observer((e) => {
698
- updateHeaderFromColumnBar.call(self);
699
- updateGrid.call(self);
700
- updateConfigColumnBar.call(self);
701
- }),
702
- );
703
-
704
- self[gridHeadersElementSymbol].addEventListener("click", function (event) {
705
- let element = null;
706
- const datasource = self[datasourceLinkedElementSymbol];
707
- if (!datasource) {
708
- return;
709
- }
710
-
711
- element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
712
- if (element) {
713
- const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
714
- const headers = self.getOption("headers");
715
-
716
- event.preventDefault();
717
-
718
- headers[index].changeDirection();
719
-
720
- queueMicrotask(function () {
721
- /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
722
- element.setAttribute(
723
- ATTRIBUTE_DATATABLE_SORTABLE,
724
- `${headers[index].field} ${headers[index].direction}`,
725
- );
726
-
727
- storeOrderStatement.call(self, true);
728
- });
729
- }
730
- });
731
-
732
- const eventHandlerDoubleClickCopyToClipboard = (event) => {
733
- const element = findTargetElementFromEvent(event, "data-monster-head");
734
- if (element) {
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(
741
- `[data-monster-insert-reference="${index}"]`,
742
- );
743
-
744
- const colTexts = [];
745
- for (let i = 0; i < cols.length; i++) {
746
- const col = cols[i];
747
-
748
- if (
749
- col.querySelector("monster-button-bar") ||
750
- col.querySelector("monster-button")
751
- ) {
752
- continue;
753
- }
754
-
755
- if (col.textContent) {
756
- colTexts.push(
757
- quoteOpenChar + col.textContent.trim() + quoteCloseChar,
758
- );
759
- }
760
- }
761
-
762
- text = colTexts.join(delimiterChar);
763
- }
764
- } else {
765
- if (
766
- element.querySelector("monster-button-bar") ||
767
- element.querySelector("monster-button")
768
- ) {
769
- return;
770
- }
771
-
772
- text = element.textContent.trim();
773
- }
774
-
775
- if (getWindow().navigator.clipboard && text) {
776
- getWindow()
777
- .navigator.clipboard.writeText(text)
778
- .then(
779
- () => {},
780
- (err) => {},
781
- );
782
- }
783
- }
784
- };
785
-
786
- if (self.getOption("features.doubleClickCopyToClipboard")) {
787
- self[gridElementSymbol].addEventListener(
788
- "dblclick",
789
- eventHandlerDoubleClickCopyToClipboard,
790
- );
791
- }
792
-
793
- if (self.getOption("features.copyAll") && this[copyAllElementSymbol]) {
794
- this[copyAllElementSymbol].addEventListener("click", (event) => {
795
- event.preventDefault();
796
-
797
- const table = [];
798
- let currentRow = [];
799
- let currentIndex = null;
800
-
801
- const cols = self.getGridElements(`[data-monster-insert-reference]`);
802
- const rowIndexes = new Map();
803
- cols.forEach((col) => {
804
- const index = col.getAttribute("data-monster-insert-reference");
805
- rowIndexes.set(index, true);
806
- });
807
-
808
- rowIndexes.forEach((value, key) => {
809
- const cols = self.getGridElements(
810
- `[data-monster-insert-reference="${key}"]`,
811
- );
812
-
813
- for (let i = 0; i < cols.length; i++) {
814
- const col = cols[i];
815
-
816
- if (
817
- col.querySelector("monster-button-bar") ||
818
- col.querySelector("monster-button")
819
- ) {
820
- continue;
821
- }
822
-
823
- if (col.textContent) {
824
- currentRow.push(
825
- quoteOpenChar + col.textContent.trim() + quoteCloseChar,
826
- );
827
- }
828
- }
829
-
830
- if (currentRow.length > 0) {
831
- table.push(currentRow);
832
- }
833
- currentRow = [];
834
- });
835
-
836
- if (table.length > 0) {
837
- const text = table.map((row) => row.join(delimiterChar)).join(rowBreak);
838
- if (getWindow().navigator.clipboard && text) {
839
- getWindow()
840
- .navigator.clipboard.writeText(text)
841
- .then(
842
- () => {},
843
- (err) => {},
844
- );
845
- }
846
- }
847
- });
848
- }
711
+ const self = this;
712
+
713
+ const quoteOpenChar = this.getOption("copy.quoteOpen");
714
+ const quoteCloseChar = this.getOption("copy.quoteClose");
715
+ const delimiterChar = this.getOption("copy.delimiter");
716
+ const rowBreak = this.getOption("copy.rowBreak");
717
+
718
+ self[columnBarElementSymbol].attachObserver(
719
+ new Observer((e) => {
720
+ updateHeaderFromColumnBar.call(self);
721
+ updateGrid.call(self);
722
+ updateConfigColumnBar.call(self);
723
+ }),
724
+ );
725
+
726
+ self[gridHeadersElementSymbol].addEventListener("click", function (event) {
727
+ let element = null;
728
+ const datasource = self[datasourceLinkedElementSymbol];
729
+ if (!datasource) {
730
+ return;
731
+ }
732
+
733
+ element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
734
+ if (element) {
735
+ const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
736
+ const headers = self.getOption("headers");
737
+
738
+ event.preventDefault();
739
+
740
+ headers[index].changeDirection();
741
+
742
+ queueMicrotask(function () {
743
+ /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
744
+ element.setAttribute(
745
+ ATTRIBUTE_DATATABLE_SORTABLE,
746
+ `${headers[index].field} ${headers[index].direction}`,
747
+ );
748
+
749
+ storeOrderStatement.call(self, true);
750
+ });
751
+ }
752
+ });
753
+
754
+ const eventHandlerDoubleClickCopyToClipboard = (event) => {
755
+ const element = findTargetElementFromEvent(event, "data-monster-head");
756
+ if (element) {
757
+ let text = "";
758
+
759
+ if (event.shiftKey) {
760
+ const index = element.getAttribute("data-monster-insert-reference");
761
+ if (index) {
762
+ const cols = self.getGridElements(
763
+ `[data-monster-insert-reference="${index}"]`,
764
+ );
765
+
766
+ const colTexts = [];
767
+ for (let i = 0; i < cols.length; i++) {
768
+ const col = cols[i];
769
+
770
+ if (
771
+ col.querySelector("monster-button-bar") ||
772
+ col.querySelector("monster-button")
773
+ ) {
774
+ continue;
775
+ }
776
+
777
+ if (col.textContent) {
778
+ colTexts.push(
779
+ quoteOpenChar + col.textContent.trim() + quoteCloseChar,
780
+ );
781
+ }
782
+ }
783
+
784
+ text = colTexts.join(delimiterChar);
785
+ }
786
+ } else {
787
+ if (
788
+ element.querySelector("monster-button-bar") ||
789
+ element.querySelector("monster-button")
790
+ ) {
791
+ return;
792
+ }
793
+
794
+ text = element.textContent.trim();
795
+ }
796
+
797
+ if (getWindow().navigator.clipboard && text) {
798
+ getWindow()
799
+ .navigator.clipboard.writeText(text)
800
+ .then(
801
+ () => {
802
+ },
803
+ (err) => {
804
+ },
805
+ );
806
+ }
807
+ }
808
+ };
809
+
810
+ if (self.getOption("features.doubleClickCopyToClipboard")) {
811
+ self[gridElementSymbol].addEventListener(
812
+ "dblclick",
813
+ eventHandlerDoubleClickCopyToClipboard,
814
+ );
815
+ }
816
+
817
+ if (self.getOption("features.copyAll") && this[copyAllElementSymbol]) {
818
+ this[copyAllElementSymbol].addEventListener("click", (event) => {
819
+ event.preventDefault();
820
+
821
+ const table = [];
822
+ let currentRow = [];
823
+ let currentIndex = null;
824
+
825
+ const cols = self.getGridElements(`[data-monster-insert-reference]`);
826
+ const rowIndexes = new Map();
827
+ cols.forEach((col) => {
828
+ const index = col.getAttribute("data-monster-insert-reference");
829
+ rowIndexes.set(index, true);
830
+ });
831
+
832
+ rowIndexes.forEach((value, key) => {
833
+ const cols = self.getGridElements(
834
+ `[data-monster-insert-reference="${key}"]`,
835
+ );
836
+
837
+ for (let i = 0; i < cols.length; i++) {
838
+ const col = cols[i];
839
+
840
+ if (
841
+ col.querySelector("monster-button-bar") ||
842
+ col.querySelector("monster-button")
843
+ ) {
844
+ continue;
845
+ }
846
+
847
+ if (col.textContent) {
848
+ currentRow.push(
849
+ quoteOpenChar + col.textContent.trim() + quoteCloseChar,
850
+ );
851
+ }
852
+ }
853
+
854
+ if (currentRow.length > 0) {
855
+ table.push(currentRow);
856
+ }
857
+ currentRow = [];
858
+ });
859
+
860
+ if (table.length > 0) {
861
+ const text = table.map((row) => row.join(delimiterChar)).join(rowBreak);
862
+ if (getWindow().navigator.clipboard && text) {
863
+ getWindow()
864
+ .navigator.clipboard.writeText(text)
865
+ .then(
866
+ () => {
867
+ },
868
+ (err) => {
869
+ },
870
+ );
871
+ }
872
+ }
873
+ });
874
+ }
875
+
876
+ const selectRowCallback = (event) => {
877
+ const element = findTargetElementFromEvent(event, "data-monster-role", "select-row");
878
+ if (element) {
879
+ const key = element.parentNode.getAttribute("data-monster-insert-reference");
880
+ const row = self.getGridElements(
881
+ `[data-monster-insert-reference="${key}"]`,
882
+ );
883
+
884
+ const index = key.split("-").pop();
885
+
886
+ if (element.checked) {
887
+ row.forEach((col) => {
888
+ col.classList.add("selected");
889
+ });
890
+
891
+ fireCustomEvent(self, "monster-datatable-row-selected", {
892
+ index: index
893
+ })
894
+
895
+ } else {
896
+ row.forEach((col) => {
897
+ col.classList.remove("selected");
898
+ });
899
+
900
+ fireCustomEvent(self, "monster-datatable-row-deselected", {
901
+ index: index
902
+ })
903
+ }
904
+
905
+ fireCustomEvent(this, "monster-datatable-selection-changed", {})
906
+ }
907
+
908
+ const rows = self.getGridElements(`[data-monster-role="select-row"]`);
909
+ const allSelected = Array.from(rows).every((row) => row.checked);
910
+ const selectAll = this[gridHeadersElementSymbol].querySelector(`[data-monster-role="select-all"]`);
911
+ selectAll.checked = allSelected;
912
+
913
+
914
+ }
915
+
916
+ this[gridElementSymbol].addEventListener("click", selectRowCallback);
917
+ this[gridElementSymbol].addEventListener("touch", selectRowCallback);
918
+
919
+ const selectAllCallback = (event) => {
920
+ const element = findTargetElementFromEvent(event, "data-monster-role", "select-all");
921
+ if (element) {
922
+ const mode = element.checked
923
+
924
+ const rows = this.getGridElements(`[data-monster-role="select-row"]`);
925
+ rows.forEach((row) => {
926
+ row.checked = mode;
927
+ });
928
+
929
+ if (mode) {
930
+ fireCustomEvent(this, "monster-datatable-all-rows-selected", {})
931
+ } else {
932
+ fireCustomEvent(this, "monster-datatable-all-rows-deselected", {})
933
+ }
934
+
935
+ fireCustomEvent(this, "monster-datatable-selection-changed", {})
936
+
937
+ }
938
+ }
939
+
940
+ this[gridHeadersElementSymbol].addEventListener("click", selectAllCallback)
941
+ this[gridHeadersElementSymbol].addEventListener("touch", selectAllCallback)
942
+
943
+
849
944
  }
850
945
 
851
946
  /**
852
947
  * @private
853
948
  */
854
949
  function initGridAndStructs(hostConfig, headerOrderMap) {
855
- const rowID = this.getOption("templateMapping.row-key");
856
-
857
- if (!this[gridElementSymbol]) {
858
- throw new Error("no grid element is defined");
859
- }
860
-
861
- let template;
862
- getSlottedElements.call(this).forEach((e) => {
863
- if (e instanceof HTMLTemplateElement && e.id === rowID) {
864
- template = e;
865
- }
866
- });
867
-
868
- if (!template) {
869
- throw new Error("no template is defined");
870
- }
871
-
872
- const rowCount = template.content.children.length;
873
-
874
- const headers = [];
875
-
876
- for (let i = 0; i < rowCount; i++) {
877
- let hClass = "";
878
- const row = template.content.children[i];
879
-
880
- let mode = "";
881
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
882
- mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
883
- }
884
-
885
- let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
886
- if (!grid || grid === "" || grid === "auto") {
887
- grid = "minmax(0, 1fr)";
888
- }
889
-
890
- let label = "";
891
- let labelKey = "";
892
-
893
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
894
- label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
895
- labelKey = label;
896
-
897
- try {
898
- if (label.startsWith("i18n:")) {
899
- label = label.substring(5, label.length);
900
- label = getDocumentTranslations().getText(label, label);
901
- }
902
- } catch (e) {
903
- label = "i18n error " + label;
904
- }
905
- }
906
-
907
- if (!label) {
908
- label = i + 1 + "";
909
- mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
910
- labelKey = label;
911
- }
912
-
913
- if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
914
- if (hostConfig[label] === false) {
915
- mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
916
- } else {
917
- mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
918
- }
919
- }
920
-
921
- let align = "";
922
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
923
- align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
924
- }
925
-
926
- switch (align) {
927
- case "center":
928
- hClass = "flex-center";
929
- break;
930
- case "end":
931
- hClass = "flex-end";
932
- break;
933
- case "start":
934
- hClass = "flex-start";
935
- break;
936
- default:
937
- hClass = "flex-start";
938
- }
939
-
940
- let field = "";
941
- let direction = DIRECTION_NONE;
942
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
943
- field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE).trim();
944
- const parts = field.split(" ").map((item) => item.trim());
945
- field = parts[0];
946
-
947
- if (headerOrderMap.has(field)) {
948
- direction = headerOrderMap.get(field);
949
- } else if (
950
- parts.length === 2 &&
951
- [DIRECTION_ASC, DIRECTION_DESC].indexOf(parts[1]) !== -1
952
- ) {
953
- direction = parts[1];
954
- }
955
- }
956
-
957
- if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
958
- hClass += " hidden";
959
- }
960
-
961
- const header = new Header();
962
- header.setInternals({
963
- field: field,
964
- label: label,
965
- classes: hClass,
966
- index: i,
967
- mode: mode,
968
- grid: grid,
969
- labelKey: labelKey,
970
- direction: direction,
971
- });
972
-
973
- headers.push(header);
974
- }
975
-
976
- this.setOption("headers", headers);
977
- queueMicrotask(() => {
978
- storeOrderStatement.call(this, this.getOption("features.autoInit"));
979
- });
950
+ const rowID = this.getOption("templateMapping.row-key");
951
+
952
+ if (!this[gridElementSymbol]) {
953
+ throw new Error("no grid element is defined");
954
+ }
955
+
956
+ let template;
957
+ getSlottedElements.call(this).forEach((e) => {
958
+
959
+ if (e instanceof HTMLTemplateElement && e.id === rowID) {
960
+ template = e;
961
+ }
962
+ });
963
+
964
+ if (!template) {
965
+ throw new Error("no template is defined");
966
+ }
967
+
968
+ const rowCount = template.content.children.length;
969
+
970
+ const headers = [];
971
+
972
+ for (let i = 0; i < rowCount; i++) {
973
+ let hClass = "";
974
+ const row = template.content.children[i];
975
+
976
+ let mode = "";
977
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
978
+ mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
979
+ }
980
+
981
+ let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
982
+ if (!grid || grid === "" || grid === "auto") {
983
+ grid = "minmax(0, 1fr)";
984
+ }
985
+
986
+ let label = "";
987
+ let labelKey = "";
988
+
989
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
990
+ label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
991
+ labelKey = label;
992
+
993
+ try {
994
+ if (label.startsWith("i18n:")) {
995
+ label = label.substring(5, label.length);
996
+ label = getDocumentTranslations().getText(label, label);
997
+ }
998
+ } catch (e) {
999
+ label = "i18n error " + label;
1000
+ }
1001
+ }
1002
+
1003
+ if (!label) {
1004
+ label = i + 1 + "";
1005
+ mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
1006
+ labelKey = label;
1007
+ }
1008
+
1009
+ if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
1010
+ if (hostConfig[label] === false) {
1011
+ mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
1012
+ } else {
1013
+ mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
1014
+ }
1015
+ }
1016
+
1017
+ let align = "";
1018
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
1019
+ align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
1020
+ }
1021
+
1022
+ switch (align) {
1023
+ case "center":
1024
+ hClass = "flex-center";
1025
+ break;
1026
+ case "end":
1027
+ hClass = "flex-end";
1028
+ break;
1029
+ case "start":
1030
+ hClass = "flex-start";
1031
+ break;
1032
+ default:
1033
+ hClass = "flex-start";
1034
+ }
1035
+
1036
+ let field = "";
1037
+ let direction = DIRECTION_NONE;
1038
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
1039
+ field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE).trim();
1040
+ const parts = field.split(" ").map((item) => item.trim());
1041
+ field = parts[0];
1042
+
1043
+ if (headerOrderMap.has(field)) {
1044
+ direction = headerOrderMap.get(field);
1045
+ } else if (
1046
+ parts.length === 2 &&
1047
+ [DIRECTION_ASC, DIRECTION_DESC].indexOf(parts[1]) !== -1
1048
+ ) {
1049
+ direction = parts[1];
1050
+ }
1051
+ }
1052
+
1053
+ if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
1054
+ hClass += " hidden";
1055
+ }
1056
+
1057
+ const features = [];
1058
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_FEATURES)) {
1059
+ const features = row.getAttribute(ATTRIBUTE_DATATABLE_FEATURES).split(" ");
1060
+ features.forEach((feature) => {
1061
+ features.push(feature.trim());
1062
+
1063
+ if (feature === "select") {
1064
+ label = "<input type='checkbox' data-monster-role='select-all' />";
1065
+
1066
+ while (row.firstChild) {
1067
+ row.removeChild(row.firstChild);
1068
+ }
1069
+
1070
+ const checkbox = document.createElement("input");
1071
+ checkbox.type = "checkbox";
1072
+ checkbox.setAttribute("data-monster-role", "select-row");
1073
+ row.appendChild(checkbox);
1074
+
1075
+ }
1076
+
1077
+ });
1078
+ }
1079
+
1080
+ const header = new Header();
1081
+ header.setInternals({
1082
+ field: field,
1083
+ label: label,
1084
+ classes: hClass,
1085
+ index: i,
1086
+ mode: mode,
1087
+ grid: grid,
1088
+ labelKey: labelKey,
1089
+ direction: direction,
1090
+ features: features,
1091
+ });
1092
+
1093
+ headers.push(header);
1094
+ }
1095
+
1096
+ this.setOption("headers", headers);
1097
+ queueMicrotask(() => {
1098
+ storeOrderStatement.call(this, this.getOption("features.autoInit"));
1099
+ });
1100
+ }
1101
+
1102
+ /**
1103
+ * @private
1104
+ * @returns {object}
1105
+ */
1106
+ function getTranslations() {
1107
+ const locale = getLocaleOfDocument();
1108
+ switch (locale.language) {
1109
+ case 'de':
1110
+ return {
1111
+ theListContainsNoEntries: "Die Liste enthält keine Einträge",
1112
+ copyAll: "Alles kopieren",
1113
+ helpText:
1114
+ "<p>Sie können die Werte aus einzelnen Zeilen<br>" +
1115
+ "in die Zwischenablage kopieren, indem Sie auf die entsprechende Spalte doppelklicken.</p>" +
1116
+ "<p>Um eine ganze Zeile zu kopieren, halten Sie die Umschalttaste gedrückt, während Sie klicken.<br>" +
1117
+ "Wenn Sie alle Zeilen kopieren möchten, können Sie die Schaltfläche <strong>Alles kopieren</strong> verwenden.</p>",
1118
+ };
1119
+ case 'fr':
1120
+ return {
1121
+ theListContainsNoEntries: "La liste ne contient aucune entrée",
1122
+ copyAll: "Copier tout",
1123
+ helpText:
1124
+ "<p>Vous pouvez copier les valeurs des rangées individuelles<br>" +
1125
+ "dans le presse-papiers en double-cliquant sur la colonne concernée.</p>" +
1126
+ "<p>Pour copier une rangée entière, maintenez la touche Maj enfoncée tout en cliquant.<br>" +
1127
+ "Si vous souhaitez copier toutes les rangées, vous pouvez utiliser le bouton <strong>Copier tout</strong>.</p>",
1128
+ };
1129
+ case 'sp':
1130
+ return {
1131
+ theListContainsNoEntries: "La lista no contiene entradas",
1132
+ copyAll: "Copiar todo",
1133
+ helpText:
1134
+ "<p>Puedes copiar los valores de filas individuales<br>" +
1135
+ "al portapapeles haciendo doble clic en la columna correspondiente.</p>" +
1136
+ "<p>Para copiar una fila entera, mantén presionada la tecla Shift mientras haces clic.<br>" +
1137
+ "Si quieres copiar todas las filas, puedes usar el botón <strong>Copiar todo</strong>.</p>",
1138
+ };
1139
+ case 'it':
1140
+ return {
1141
+ theListContainsNoEntries: "L'elenco non contiene voci",
1142
+ copyAll: "Copia tutto",
1143
+ helpText:
1144
+ "<p>Puoi copiare i valori dalle singole righe<br>" +
1145
+ "negli appunti facendo doppio clic sulla colonna relativa.</p>" +
1146
+ "<p>Per copiare un'intera riga, tieni premuto il tasto Shift mentre clicchi.<br>" +
1147
+ "Se vuoi copiare tutte le righe, puoi usare il pulsante <strong>Copia tutto</strong>.</p>",
1148
+ };
1149
+ case 'pl':
1150
+ return {
1151
+ theListContainsNoEntries: "Lista nie zawiera wpisów",
1152
+ copyAll: "Kopiuj wszystko",
1153
+ helpText:
1154
+ "<p>Możesz skopiować wartości z poszczególnych wierszy<br>" +
1155
+ "do schowka, klikając dwukrotnie na odpowiednią kolumnę.</p>" +
1156
+ "<p>Aby skopiować cały wiersz, przytrzymaj klawisz Shift podczas klikania.<br>" +
1157
+ "Jeśli chcesz skopiować wszystkie wiersze, możesz użyć przycisku <strong>Kopiuj wszystko</strong>.</p>",
1158
+ };
1159
+ case 'no':
1160
+ return {
1161
+ theListContainsNoEntries: "Listen inneholder ingen oppføringer",
1162
+ copyAll: "Kopier alt",
1163
+ helpText:
1164
+ "<p>Du kan kopiere verdier fra enkeltrader<br>" +
1165
+ "til utklippstavlen ved å dobbeltklikke på den relevante kolonnen.</p>" +
1166
+ "<p>For å kopiere en hel rad, hold nede Skift-tasten mens du klikker.<br>" +
1167
+ "Hvis du vil kopiere alle radene, kan du bruke knappen <strong>Kopier alt</strong>.</p>",
1168
+ };
1169
+ case 'dk':
1170
+ return {
1171
+ theListContainsNoEntries: "Listen indeholder ingen poster",
1172
+ copyAll: "Kopiér alt",
1173
+ helpText:
1174
+ "<p>Du kan kopiere værdier fra enkelte rækker<br>" +
1175
+ "til udklipsholderen ved at dobbeltklikke på den relevante kolonne.</p>" +
1176
+ "<p>For at kopiere en hel række, hold Shift-tasten nede, mens du klikker.<br>" +
1177
+ "Hvis du vil kopiere alle rækker, kan du bruge knappen <strong>Kopiér alt</strong>.</p>",
1178
+ };
1179
+ case 'sw':
1180
+ return {
1181
+ theListContainsNoEntries: "Listan innehåller inga poster",
1182
+ copyAll: "Kopiera allt",
1183
+ helpText:
1184
+ "<p>Du kan kopiera värden från enskilda rader<br>" +
1185
+ "till urklipp genom att dubbelklicka på den relevanta kolumnen.</p>" +
1186
+ "<p>För att kopiera en hel rad, håll ned Shift-tangenten medan du klickar.<br>" +
1187
+ "Om du vill kopiera alla rader kan du använda knappen <strong>Kopiera allt</strong>.</p>",
1188
+ };
1189
+
1190
+
1191
+ case 'en':
1192
+ default:
1193
+ return {
1194
+ theListContainsNoEntries: "The list contains no entries",
1195
+ copyAll: "Copy all",
1196
+ helpText:
1197
+ "<p>You can copy the values from individual rows<br>" +
1198
+ "to the clipboard by double-clicking on the relevant column.</p>" +
1199
+ "<p>To copy an entire row, hold down the Shift key while clicking.<br>" +
1200
+ "If you want to copy all rows, you can use the <strong>Copy All</strong> button.</p>",
1201
+ };
1202
+ }
980
1203
  }
981
1204
 
982
1205
  /**
@@ -984,79 +1207,79 @@ function initGridAndStructs(hostConfig, headerOrderMap) {
984
1207
  * @return {string}
985
1208
  */
986
1209
  export function getStoredOrderConfigKey() {
987
- return generateUniqueConfigKey("datatable", this?.id, "stored-order");
1210
+ return generateUniqueConfigKey("datatable", this?.id, "stored-order");
988
1211
  }
989
1212
 
990
1213
  /**
991
1214
  * @private
992
1215
  */
993
1216
  function storeOrderStatement(doFetch) {
994
- const headers = this.getOption("headers");
995
- const statement = createOrderStatement(headers);
996
- setDataSource.call(this, { orderBy: statement }, doFetch);
1217
+ const headers = this.getOption("headers");
1218
+ const statement = createOrderStatement(headers);
1219
+ setDataSource.call(this, {orderBy: statement}, doFetch);
997
1220
 
998
- const host = findElementWithSelectorUpwards(this, "monster-host");
999
- if (!(host && this.id)) {
1000
- return;
1001
- }
1221
+ const host = findElementWithSelectorUpwards(this, "monster-host");
1222
+ if (!(host && this.id)) {
1223
+ return;
1224
+ }
1002
1225
 
1003
- const configKey = getStoredOrderConfigKey.call(this);
1226
+ const configKey = getStoredOrderConfigKey.call(this);
1004
1227
 
1005
- // statement explode with , and remove all empty
1006
- const list = statement.split(",").filter((item) => item.trim() !== "");
1007
- if (list.length === 0) {
1008
- return;
1009
- }
1228
+ // statement explode with , and remove all empty
1229
+ const list = statement.split(",").filter((item) => item.trim() !== "");
1230
+ if (list.length === 0) {
1231
+ return;
1232
+ }
1010
1233
 
1011
- host.setConfig(configKey, list);
1234
+ host.setConfig(configKey, list);
1012
1235
  }
1013
1236
 
1014
1237
  /**
1015
1238
  * @private
1016
1239
  */
1017
1240
  function updateGrid() {
1018
- if (!this[gridElementSymbol]) {
1019
- throw new Error("no grid element is defined");
1020
- }
1021
-
1022
- let gridTemplateColumns = "";
1023
-
1024
- const headers = this.getOption("headers");
1025
-
1026
- let styles = "";
1027
-
1028
- for (let i = 0; i < headers.length; i++) {
1029
- const header = headers[i];
1030
-
1031
- if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
1032
- styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
1033
- styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
1034
- } else {
1035
- gridTemplateColumns += `${header.grid} `;
1036
- }
1037
- }
1038
-
1039
- const sheet = new CSSStyleSheet();
1040
- if (styles !== "") sheet.replaceSync(styles);
1041
- this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
1042
-
1043
- const bodyWidth = this.parentNode.clientWidth;
1044
-
1045
- const breakpoint = this.getOption("responsive.breakpoint");
1046
- this[dataControlElementSymbol].classList.toggle(
1047
- "small",
1048
- bodyWidth <= breakpoint,
1049
- );
1050
-
1051
- if (bodyWidth > breakpoint) {
1052
- this[gridElementSymbol].style.gridTemplateColumns =
1053
- `${gridTemplateColumns}`;
1054
- this[gridHeadersElementSymbol].style.gridTemplateColumns =
1055
- `${gridTemplateColumns}`;
1056
- } else {
1057
- this[gridElementSymbol].style.gridTemplateColumns = "auto";
1058
- this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
1059
- }
1241
+ if (!this[gridElementSymbol]) {
1242
+ throw new Error("no grid element is defined");
1243
+ }
1244
+
1245
+ let gridTemplateColumns = "";
1246
+
1247
+ const headers = this.getOption("headers");
1248
+
1249
+ let styles = "";
1250
+
1251
+ for (let i = 0; i < headers.length; i++) {
1252
+ const header = headers[i];
1253
+
1254
+ if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
1255
+ styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
1256
+ styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
1257
+ } else {
1258
+ gridTemplateColumns += `${header.grid} `;
1259
+ }
1260
+ }
1261
+
1262
+ const sheet = new CSSStyleSheet();
1263
+ if (styles !== "") sheet.replaceSync(styles);
1264
+ this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
1265
+
1266
+ const bodyWidth = this.parentNode.clientWidth;
1267
+
1268
+ const breakpoint = this.getOption("responsive.breakpoint");
1269
+ this[dataControlElementSymbol].classList.toggle(
1270
+ "small",
1271
+ bodyWidth <= breakpoint,
1272
+ );
1273
+
1274
+ if (bodyWidth > breakpoint) {
1275
+ this[gridElementSymbol].style.gridTemplateColumns =
1276
+ `${gridTemplateColumns}`;
1277
+ this[gridHeadersElementSymbol].style.gridTemplateColumns =
1278
+ `${gridTemplateColumns}`;
1279
+ } else {
1280
+ this[gridElementSymbol].style.gridTemplateColumns = "auto";
1281
+ this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
1282
+ }
1060
1283
  }
1061
1284
 
1062
1285
  /**
@@ -1064,20 +1287,20 @@ function updateGrid() {
1064
1287
  * @param {Header[]} headers
1065
1288
  * @param {bool} doFetch
1066
1289
  */
1067
- function setDataSource({ orderBy }, doFetch) {
1068
- const datasource = this[datasourceLinkedElementSymbol];
1290
+ function setDataSource({orderBy}, doFetch) {
1291
+ const datasource = this[datasourceLinkedElementSymbol];
1069
1292
 
1070
- if (!datasource) {
1071
- return;
1072
- }
1293
+ if (!datasource) {
1294
+ return;
1295
+ }
1073
1296
 
1074
- if (isFunction(datasource?.setParameters)) {
1075
- datasource.setParameters({ orderBy });
1076
- }
1297
+ if (isFunction(datasource?.setParameters)) {
1298
+ datasource.setParameters({orderBy});
1299
+ }
1077
1300
 
1078
- if (doFetch !== false && isFunction(datasource?.fetch)) {
1079
- datasource.fetch();
1080
- }
1301
+ if (doFetch !== false && isFunction(datasource?.fetch)) {
1302
+ datasource.fetch();
1303
+ }
1081
1304
  }
1082
1305
 
1083
1306
  /**
@@ -1085,28 +1308,30 @@ function setDataSource({ orderBy }, doFetch) {
1085
1308
  * @return {DataTable}
1086
1309
  */
1087
1310
  function initControlReferences() {
1088
- if (!this.shadowRoot) {
1089
- throw new Error("no shadow-root is defined");
1090
- }
1091
-
1092
- this[dataControlElementSymbol] = this.shadowRoot.querySelector(
1093
- "[data-monster-role=control]",
1094
- );
1095
-
1096
- this[gridElementSymbol] = this.shadowRoot.querySelector(
1097
- "[data-monster-role=datatable]",
1098
- );
1099
- this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
1100
- "[data-monster-role=datatable-headers]",
1101
- );
1102
- this[columnBarElementSymbol] =
1103
- this.shadowRoot.querySelector("monster-column-bar");
1104
-
1105
- this[copyAllElementSymbol] = this.shadowRoot.querySelector(
1106
- "[data-monster-role=copy-all]",
1107
- );
1108
-
1109
- return this;
1311
+ if (!this.shadowRoot) {
1312
+ throw new Error("no shadow-root is defined");
1313
+ }
1314
+
1315
+ this[dataControlElementSymbol] = this.shadowRoot.querySelector(
1316
+ "[data-monster-role=control]",
1317
+ );
1318
+
1319
+ this[gridElementSymbol] = this.shadowRoot.querySelector(
1320
+ "[data-monster-role=datatable]",
1321
+ );
1322
+
1323
+ this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
1324
+ "[data-monster-role=datatable-headers]",
1325
+ );
1326
+
1327
+ this[columnBarElementSymbol] =
1328
+ this.shadowRoot.querySelector("monster-column-bar");
1329
+
1330
+ this[copyAllElementSymbol] = this.shadowRoot.querySelector(
1331
+ "[data-monster-role=copy-all]",
1332
+ );
1333
+
1334
+ return this;
1110
1335
  }
1111
1336
 
1112
1337
  /**
@@ -1116,22 +1341,22 @@ function initControlReferences() {
1116
1341
  * @throws {Error} the datasource could not be initialized
1117
1342
  */
1118
1343
  function initOptionsFromArguments() {
1119
- const options = {};
1120
- const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
1121
-
1122
- if (selector) {
1123
- options.datasource = { selector: selector };
1124
- }
1125
-
1126
- const breakpoint = this.getAttribute(
1127
- ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
1128
- );
1129
- if (breakpoint) {
1130
- options.responsive = {};
1131
- options.responsive.breakpoint = parseInt(breakpoint);
1132
- }
1133
-
1134
- return options;
1344
+ const options = {};
1345
+ const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
1346
+
1347
+ if (selector) {
1348
+ options.datasource = {selector: selector};
1349
+ }
1350
+
1351
+ const breakpoint = this.getAttribute(
1352
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
1353
+ );
1354
+ if (breakpoint) {
1355
+ options.responsive = {};
1356
+ options.responsive.breakpoint = parseInt(breakpoint);
1357
+ }
1358
+
1359
+ return options;
1135
1360
  }
1136
1361
 
1137
1362
  /**
@@ -1139,7 +1364,7 @@ function initOptionsFromArguments() {
1139
1364
  * @return {string}
1140
1365
  */
1141
1366
  function getEmptyTemplate() {
1142
- return `<monster-state data-monster-role="empty-without-action">
1367
+ return `<monster-state data-monster-role="empty-without-action">
1143
1368
  <div part="visual">
1144
1369
  <svg width="4rem" height="4rem" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
1145
1370
  <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"/>
@@ -1157,8 +1382,8 @@ function getEmptyTemplate() {
1157
1382
  * @return {string}
1158
1383
  */
1159
1384
  function getTemplate() {
1160
- // language=HTML
1161
- return `
1385
+ // language=HTML
1386
+ return `
1162
1387
  <div data-monster-role="control" part="control" data-monster-attributes="class path:classes.control">
1163
1388
  <template id="headers-row">
1164
1389
  <div data-monster-attributes="class path:headers-row.classes,
@@ -1176,7 +1401,8 @@ function getTemplate() {
1176
1401
  data-monster-attributes="class path:features.help | ?::hidden"
1177
1402
  data-monster-replace="path:labels.helpText"
1178
1403
  ></monster-context-help>
1179
- <a href="#" data-monster-attributes="class path:features.copyAll | ?::hidden" data-monster-role="copy-all" data-monster-replace="path:locale.copyAll">Copy all</a>
1404
+ <a href="#" data-monster-attributes="class path:features.copyAll | ?::hidden"
1405
+ data-monster-role="copy-all" data-monster-replace="path:labels.copyAll">Copy all</a>
1180
1406
  <monster-column-bar
1181
1407
  data-monster-attributes="class path:features.settings | ?::hidden"></monster-column-bar>
1182
1408
  <slot name="bar"></slot>