@schukai/monster 4.40.0 → 4.41.0

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