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