@schukai/monster 4.43.2 → 4.43.3

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