@schukai/monster 3.99.0 → 3.99.2

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