@schukai/monster 3.98.1 → 3.98.3

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