@schukai/monster 3.87.1 → 3.88.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +70 -43
  2. package/package.json +1 -1
  3. package/source/components/content/stylesheet/copy.mjs +1 -1
  4. package/source/components/datatable/datatable.mjs +928 -909
  5. package/source/components/datatable/filter.mjs +914 -920
  6. package/source/components/datatable/pagination.mjs +1 -3
  7. package/source/components/datatable/stylesheet/change-button.mjs +1 -1
  8. package/source/components/datatable/stylesheet/column-bar.mjs +13 -6
  9. package/source/components/datatable/stylesheet/dataset.mjs +1 -1
  10. package/source/components/datatable/stylesheet/datatable.mjs +13 -6
  11. package/source/components/datatable/stylesheet/embedded-pagination.mjs +13 -6
  12. package/source/components/datatable/stylesheet/filter-select.mjs +1 -1
  13. package/source/components/datatable/stylesheet/filter.mjs +13 -6
  14. package/source/components/datatable/stylesheet/save-button.mjs +1 -1
  15. package/source/components/datatable/stylesheet/status.mjs +1 -1
  16. package/source/components/form/api-button.mjs +420 -415
  17. package/source/components/form/button-bar.mjs +24 -6
  18. package/source/components/form/popper.mjs +1 -1
  19. package/source/components/form/stylesheet/action-button.mjs +13 -6
  20. package/source/components/form/stylesheet/button.mjs +13 -6
  21. package/source/components/form/stylesheet/confirm-button.mjs +13 -6
  22. package/source/components/form/stylesheet/form.mjs +1 -1
  23. package/source/components/form/stylesheet/popper-button.mjs +13 -6
  24. package/source/components/form/stylesheet/select.mjs +13 -6
  25. package/source/components/form/stylesheet/state-button.mjs +13 -6
  26. package/source/components/form/tree-select.mjs +1 -1
  27. package/source/components/host/stylesheet/call-button.mjs +1 -1
  28. package/source/components/host/stylesheet/host.mjs +1 -1
  29. package/source/components/host/stylesheet/overlay.mjs +1 -1
  30. package/source/components/host/stylesheet/toggle-button.mjs +1 -1
  31. package/source/components/host/stylesheet/viewer.mjs +1 -1
  32. package/source/components/layout/stylesheet/collapse.mjs +1 -1
  33. package/source/components/layout/stylesheet/details.mjs +1 -1
  34. package/source/components/layout/stylesheet/slider.mjs +1 -1
  35. package/source/components/layout/stylesheet/tabs.mjs +1 -1
  36. package/source/components/navigation/stylesheet/table-of-content.mjs +1 -1
  37. package/source/components/navigation/table-of-content.mjs +20 -4
  38. package/source/components/style/common.css +1 -1
  39. package/source/components/style/common.pcss +7 -1
  40. package/source/components/style/mixin/property.pcss +1 -0
  41. package/source/components/style/normalize.css +1 -1
  42. package/source/components/style/normalize.pcss +8 -0
  43. package/source/components/style/property.css +1 -1
  44. package/source/components/stylesheet/common.mjs +7 -14
  45. package/source/components/stylesheet/mixin/property.mjs +6 -13
  46. package/source/components/stylesheet/normalize.mjs +7 -14
  47. package/source/components/stylesheet/property.mjs +1 -1
  48. package/source/components/tree-menu/style/tree-menu.pcss +0 -2
  49. package/source/components/tree-menu/stylesheet/tree-menu.mjs +7 -14
  50. package/source/types/is.mjs +9 -1
  51. package/source/types/version.mjs +1 -1
  52. package/test/cases/monster.mjs +1 -1
  53. package/test/cases/types/is.mjs +59 -1
  54. package/test/util/jsdom.mjs +2 -0
  55. package/test/web/test.html +2 -2
  56. package/test/web/tests.js +83 -21
@@ -12,75 +12,77 @@
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_MODE_VISIBLE,
50
- ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
51
- 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_MODE_VISIBLE,
50
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
51
+ ATTRIBUTE_DATATABLE_MODE_FIXED,
52
52
  } from "./constants.mjs";
53
- import {instanceSymbol} from "../../constants.mjs";
53
+ import { instanceSymbol } from "../../constants.mjs";
54
54
  import {
55
- Header,
56
- createOrderStatement,
57
- DIRECTION_ASC,
58
- DIRECTION_DESC,
59
- DIRECTION_NONE,
55
+ Header,
56
+ createOrderStatement,
57
+ DIRECTION_ASC,
58
+ DIRECTION_DESC,
59
+ DIRECTION_NONE,
60
60
  } from "./datatable/header.mjs";
61
- import {DatatableStyleSheet} from "./stylesheet/datatable.mjs";
61
+ import { DatatableStyleSheet } from "./stylesheet/datatable.mjs";
62
62
  import {
63
- handleDataSourceChanges,
64
- datasourceLinkedElementSymbol,
63
+ handleDataSourceChanges,
64
+ datasourceLinkedElementSymbol,
65
65
  } from "./util.mjs";
66
66
  import "./columnbar.mjs";
67
67
  import "./filter-button.mjs";
68
68
  import {
69
- findElementWithSelectorUpwards,
70
- getDocument,
71
- getWindow,
69
+ findElementWithSelectorUpwards,
70
+ getDocument,
71
+ getWindow,
72
72
  } from "../../dom/util.mjs";
73
- import {addAttributeToken} from "../../dom/attributes.mjs";
74
- import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
75
- import {getDocumentTranslations} from "../../i18n/translations.mjs";
73
+ import { addAttributeToken } from "../../dom/attributes.mjs";
74
+ import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.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
- export {DataTable};
83
+ import "../form/context-help.mjs";
84
+
85
+ export { DataTable };
84
86
 
85
87
  /**
86
88
  * @private
@@ -148,398 +150,404 @@ const resizeObserverSymbol = Symbol("resizeObserver");
148
150
  * @fires monster-datatable-row-added
149
151
  **/
150
152
  class DataTable extends CustomElement {
151
- /**
152
- * This method is called by the `instanceof` operator.
153
- * @return {symbol}
154
- */
155
- static get [instanceSymbol]() {
156
- return Symbol.for("@schukai/monster/components/datatable@@instance");
157
- }
158
-
159
- /**
160
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
161
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
162
- *
163
- * The individual configuration values can be found in the table.
164
- *
165
- * @property {Object} templates Template definitions
166
- * @property {string} templates.main Main template
167
- * @property {Object} datasource Datasource configuration
168
- * @property {string} datasource.selector Selector for the datasource
169
- * @property {Object} mapping Mapping configuration
170
- * @property {string} mapping.data Data mapping
171
- * @property {Array} data Data
172
- * @property {Array} headers Headers
173
- * @property {Object} responsive Responsive configuration
174
- * @property {number} responsive.breakpoint Breakpoint for responsive mode
175
- * @property {Object} labels Labels
176
- * @property {string} labels.theListContainsNoEntries Label for empty state
177
- * @property {Object} classes Classes
178
- * @property {string} classes.container Container class
179
- * @property {Object} features Features
180
- * @property {boolean} features.settings Settings feature
181
- * @property {boolean} features.footer Footer feature
182
- * @property {boolean} features.autoInit Auto init feature (init datasource automatically)
183
- * @property {boolean} features.doubleClickCopyToClipboard Double click copy to clipboard feature
184
- * @property {Object} templateMapping Template mapping
185
- * @property {string} templateMapping.row-key Row key
186
- * @property {string} templateMapping.filter-id Filter id
187
- **/
188
- get defaults() {
189
- return Object.assign(
190
- {},
191
- super.defaults,
192
- {
193
- templates: {
194
- main: getTemplate(),
195
- emptyState: getEmptyTemplate(),
196
- },
197
-
198
- datasource: {
199
- selector: null,
200
- },
201
-
202
- mapping: {
203
- data: "dataset",
204
- },
205
-
206
- data: [],
207
- headers: [],
208
-
209
- responsive: {
210
- breakpoint: 900,
211
- },
212
-
213
- labels: {
214
- theListContainsNoEntries: "The list contains no entries",
215
- copyAll: "Copy all",
216
- },
217
-
218
- classes: {
219
- control: "monster-theme-control-container-1",
220
- container: "",
221
- row: "monster-theme-control-row-1",
222
- },
223
-
224
- features: {
225
- settings: true,
226
- footer: true,
227
- autoInit: true,
228
- doubleClickCopyToClipboard: true,
229
- copyAll: true,
230
- },
231
-
232
- copy: {
233
- delimiter: ";",
234
- quoteOpen: '"',
235
- quoteClose: '"',
236
- rowBreak: "\n",
237
- },
238
-
239
- templateMapping: {
240
- "row-key": null,
241
- "filter-id": null,
242
- },
243
- },
244
- initOptionsFromArguments.call(this),
245
- );
246
- }
247
-
248
- /**
249
- *
250
- * @param {string} selector
251
- * @return {NodeListOf<*>}
252
- */
253
- getGridElements(selector) {
254
- return this[gridElementSymbol].querySelectorAll(selector);
255
- }
256
-
257
- /**
258
- *
259
- * @return {string}
260
- */
261
- static getTag() {
262
- return "monster-datatable";
263
- }
264
-
265
- /**
266
- * @return {void}
267
- */
268
- disconnectedCallback() {
269
- super.disconnectedCallback();
270
- if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
271
- this[resizeObserverSymbol].disconnect();
272
- }
273
- }
274
-
275
- /**
276
- * @return {void}
277
- */
278
- connectedCallback() {
279
- const self = this;
280
- super.connectedCallback();
281
-
282
- this[resizeObserverSymbol] = new ResizeObserver((entries) => {
283
- updateGrid.call(self);
284
- });
285
-
286
- this[resizeObserverSymbol].observe(this.parentNode);
287
- }
288
-
289
- /**
290
- * @return void
291
- */
292
- [assembleMethodSymbol]() {
293
- const rawKey = this.getOption("templateMapping.row-key");
294
-
295
- if (rawKey === null) {
296
- if (this.id !== null && this.id !== "") {
297
- const rawKey = this.getOption("templateMapping.row-key");
298
- if (rawKey === null) {
299
- this.setOption("templateMapping.row-key", this.id + "-row");
300
- }
301
- } else {
302
- this.setOption("templateMapping.row-key", "row");
303
- }
304
- }
305
-
306
- if (this.id !== null && this.id !== "") {
307
- this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
308
- } else {
309
- this.setOption("templateMapping.filter-id", "filter");
310
- }
311
-
312
- super[assembleMethodSymbol]();
313
-
314
- initControlReferences.call(this);
315
- initEventHandler.call(this);
316
-
317
- const selector = this.getOption("datasource.selector");
318
-
319
- if (isString(selector)) {
320
- const element = findElementWithSelectorUpwards(this, selector);
321
- if (element === null) {
322
- throw new Error("the selector must match exactly one element");
323
- }
324
-
325
- if (!isInstance(element, Datasource)) {
326
- throw new TypeError("the element must be a datasource");
327
- }
328
-
329
- this[datasourceLinkedElementSymbol] = element;
330
-
331
- queueMicrotask(() => {
332
- handleDataSourceChanges.call(this);
333
- element.datasource.attachObserver(
334
- new Observer(handleDataSourceChanges.bind(this)),
335
- );
336
- });
337
- }
338
-
339
- getHostConfig
340
- .call(this, getColumnVisibilityConfigKey)
341
- .then((config) => {
342
- const headerOrderMap = new Map();
343
-
344
- getHostConfig
345
- .call(this, getStoredOrderConfigKey)
346
- .then((orderConfig) => {
347
- if (isArray(orderConfig) || orderConfig.length > 0) {
348
- for (let i = 0; i < orderConfig.length; i++) {
349
- const item = orderConfig[i];
350
- const parts = item.split(" ");
351
- const field = parts[0];
352
- const direction = parts[1] || DIRECTION_ASC;
353
- headerOrderMap.set(field, direction);
354
- }
355
- }
356
- })
357
- .then(() => {
358
- try {
359
- initGridAndStructs.call(this, config, headerOrderMap);
360
- } catch (error) {
361
- addAttributeToken(
362
- this,
363
- ATTRIBUTE_ERRORMESSAGE,
364
- error?.message || error.toString(),
365
- );
366
- }
367
-
368
- updateColumnBar.call(this);
369
- })
370
- .catch((error) => {
371
- addAttributeToken(
372
- this,
373
- ATTRIBUTE_ERRORMESSAGE,
374
- error?.message || error.toString(),
375
- );
376
- });
377
- })
378
- .catch((error) => {
379
- addAttributeToken(
380
- this,
381
- ATTRIBUTE_ERRORMESSAGE,
382
- error?.message || error.toString(),
383
- );
384
- });
385
- }
386
-
387
- /**
388
- * @return {CSSStyleSheet[]}
389
- */
390
- static getCSSStyleSheet() {
391
- return [DatatableStyleSheet];
392
- }
393
-
394
- /**
395
- * Copy a row from the datatable
396
- *
397
- * @param {number|string} fromIndex
398
- * @param {number|string} toIndex
399
- * @return {DataTable}
400
- * @fires monster-datatable-row-copied
401
- */
402
- copyRow(fromIndex, toIndex) {
403
- const datasource = this[datasourceLinkedElementSymbol];
404
- if (!datasource) {
405
- return this;
406
- }
407
- let d = datasource.data;
408
- let c = clone(d);
409
-
410
- let rows = c;
411
- const mapping = this.getOption("mapping.data");
412
-
413
- if (mapping) {
414
- rows = c?.[mapping];
415
- }
416
-
417
- if (rows === undefined || rows === null) {
418
- rows = [];
419
- }
420
-
421
- if (toIndex === undefined) {
422
- toIndex = rows.length;
423
- }
424
-
425
- if (isString(fromIndex)) {
426
- fromIndex = parseInt(fromIndex);
427
- }
428
- if (isString(toIndex)) {
429
- toIndex = parseInt(toIndex);
430
- }
431
-
432
- if (toIndex < 0 || toIndex > rows.length) {
433
- throw new RangeError("index out of bounds");
434
- }
435
-
436
- validateArray(rows);
437
- validateInteger(fromIndex);
438
- validateInteger(toIndex);
439
-
440
- if (fromIndex < 0 || fromIndex >= rows.length) {
441
- throw new RangeError("index out of bounds");
442
- }
443
-
444
- rows.splice(toIndex, 0, clone(rows[fromIndex]));
445
- datasource.data = c;
446
-
447
- fireCustomEvent(this, "monster-datatable-row-copied", {
448
- index: toIndex,
449
- });
450
-
451
- return this;
452
- }
453
-
454
- /**
455
- * Remove a row from the datatable
456
- *
457
- * @param {number|string} index
458
- * @return {DataTable}
459
- * @fires monster-datatable-row-removed
460
- */
461
- removeRow(index) {
462
- const datasource = this[datasourceLinkedElementSymbol];
463
- if (!datasource) {
464
- return this;
465
- }
466
- let d = datasource.data;
467
- let c = clone(d);
468
-
469
- let rows = c;
470
- const mapping = this.getOption("mapping.data");
471
-
472
- if (mapping) {
473
- rows = c?.[mapping];
474
- }
475
-
476
- if (rows === undefined || rows === null) {
477
- rows = [];
478
- }
479
-
480
- if (isString(index)) {
481
- index = parseInt(index);
482
- }
483
-
484
- validateArray(rows);
485
- validateInteger(index);
486
-
487
- if (index < 0 || index >= rows.length) {
488
- throw new RangeError("index out of bounds");
489
- }
490
- if (mapping) {
491
- rows = c?.[mapping];
492
- }
493
-
494
- rows.splice(index, 1);
495
- datasource.data = c;
496
-
497
- fireCustomEvent(this, "monster-datatable-row-removed", {
498
- index: index,
499
- });
500
-
501
- return this;
502
- }
503
-
504
- /**
505
- * Add a row to the datatable
506
- *
507
- * @param {Object} data
508
- * @return {DataTable}
509
- *
510
- * @fires monster-datatable-row-added
511
- **/
512
- addRow(data) {
513
- const datasource = this[datasourceLinkedElementSymbol];
514
- if (!datasource) {
515
- return this;
516
- }
517
- let d = datasource.data;
518
- let c = clone(d);
519
-
520
- let rows = c;
521
-
522
- const mapping = this.getOption("mapping.data");
523
- if (mapping) {
524
- rows = c?.[mapping];
525
- }
526
-
527
- if (rows === undefined || rows === null) {
528
- rows = [];
529
- }
530
-
531
- validateArray(rows);
532
- validateObject(data);
533
-
534
- rows.push(data);
535
- datasource.data = c;
536
-
537
- fireCustomEvent(this, "monster-datatable-row-added", {
538
- index: rows.length - 1,
539
- });
540
-
541
- return this;
542
- }
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 {Object} templateMapping Template mapping
187
+ * @property {string} templateMapping.row-key Row key
188
+ * @property {string} templateMapping.filter-id Filter id
189
+ **/
190
+ get defaults() {
191
+ return Object.assign(
192
+ {},
193
+ super.defaults,
194
+ {
195
+ templates: {
196
+ main: getTemplate(),
197
+ emptyState: getEmptyTemplate(),
198
+ },
199
+
200
+ datasource: {
201
+ selector: null,
202
+ },
203
+
204
+ mapping: {
205
+ data: "dataset",
206
+ },
207
+
208
+ data: [],
209
+ headers: [],
210
+
211
+ responsive: {
212
+ breakpoint: 900,
213
+ },
214
+
215
+ labels: {
216
+ theListContainsNoEntries: "The list contains no entries",
217
+ copyAll: "Copy all",
218
+ helpText:
219
+ "<p>You can copy the values from individual rows<br>" +
220
+ "to the clipboard by double-clicking on the relevant column.</p>" +
221
+ "<p>To copy an entire row, hold down the Shift key while clicking.<br>" +
222
+ "If you want to copy all rows, you can use the <strong>Copy All</strong> button.</p>",
223
+ },
224
+
225
+ classes: {
226
+ control: "monster-theme-control-container-1",
227
+ container: "",
228
+ row: "monster-theme-control-row-1",
229
+ },
230
+
231
+ features: {
232
+ settings: true,
233
+ footer: true,
234
+ autoInit: true,
235
+ doubleClickCopyToClipboard: true,
236
+ copyAll: true,
237
+ help: true,
238
+ },
239
+
240
+ copy: {
241
+ delimiter: ";",
242
+ quoteOpen: '"',
243
+ quoteClose: '"',
244
+ rowBreak: "\n",
245
+ },
246
+
247
+ templateMapping: {
248
+ "row-key": null,
249
+ "filter-id": null,
250
+ },
251
+ },
252
+ initOptionsFromArguments.call(this),
253
+ );
254
+ }
255
+
256
+ /**
257
+ *
258
+ * @param {string} selector
259
+ * @return {NodeListOf<*>}
260
+ */
261
+ getGridElements(selector) {
262
+ return this[gridElementSymbol].querySelectorAll(selector);
263
+ }
264
+
265
+ /**
266
+ *
267
+ * @return {string}
268
+ */
269
+ static getTag() {
270
+ return "monster-datatable";
271
+ }
272
+
273
+ /**
274
+ * @return {void}
275
+ */
276
+ disconnectedCallback() {
277
+ super.disconnectedCallback();
278
+ if (this?.[resizeObserverSymbol] instanceof ResizeObserver) {
279
+ this[resizeObserverSymbol].disconnect();
280
+ }
281
+ }
282
+
283
+ /**
284
+ * @return {void}
285
+ */
286
+ connectedCallback() {
287
+ const self = this;
288
+ super.connectedCallback();
289
+
290
+ this[resizeObserverSymbol] = new ResizeObserver((entries) => {
291
+ updateGrid.call(self);
292
+ });
293
+
294
+ this[resizeObserverSymbol].observe(this.parentNode);
295
+ }
296
+
297
+ /**
298
+ * @return void
299
+ */
300
+ [assembleMethodSymbol]() {
301
+ const rawKey = this.getOption("templateMapping.row-key");
302
+
303
+ if (rawKey === null) {
304
+ if (this.id !== null && this.id !== "") {
305
+ const rawKey = this.getOption("templateMapping.row-key");
306
+ if (rawKey === null) {
307
+ this.setOption("templateMapping.row-key", this.id + "-row");
308
+ }
309
+ } else {
310
+ this.setOption("templateMapping.row-key", "row");
311
+ }
312
+ }
313
+
314
+ if (this.id !== null && this.id !== "") {
315
+ this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
316
+ } else {
317
+ this.setOption("templateMapping.filter-id", "filter");
318
+ }
319
+
320
+ super[assembleMethodSymbol]();
321
+
322
+ initControlReferences.call(this);
323
+ initEventHandler.call(this);
324
+
325
+ const selector = this.getOption("datasource.selector");
326
+
327
+ if (isString(selector)) {
328
+ const element = findElementWithSelectorUpwards(this, selector);
329
+ if (element === null) {
330
+ throw new Error("the selector must match exactly one element");
331
+ }
332
+
333
+ if (!isInstance(element, Datasource)) {
334
+ throw new TypeError("the element must be a datasource");
335
+ }
336
+
337
+ this[datasourceLinkedElementSymbol] = element;
338
+
339
+ queueMicrotask(() => {
340
+ handleDataSourceChanges.call(this);
341
+ element.datasource.attachObserver(
342
+ new Observer(handleDataSourceChanges.bind(this)),
343
+ );
344
+ });
345
+ }
346
+
347
+ getHostConfig
348
+ .call(this, getColumnVisibilityConfigKey)
349
+ .then((config) => {
350
+ const headerOrderMap = new Map();
351
+
352
+ getHostConfig
353
+ .call(this, getStoredOrderConfigKey)
354
+ .then((orderConfig) => {
355
+ if (isArray(orderConfig) || orderConfig.length > 0) {
356
+ for (let i = 0; i < orderConfig.length; i++) {
357
+ const item = orderConfig[i];
358
+ const parts = item.split(" ");
359
+ const field = parts[0];
360
+ const direction = parts[1] || DIRECTION_ASC;
361
+ headerOrderMap.set(field, direction);
362
+ }
363
+ }
364
+ })
365
+ .then(() => {
366
+ try {
367
+ initGridAndStructs.call(this, config, headerOrderMap);
368
+ } catch (error) {
369
+ addAttributeToken(
370
+ this,
371
+ ATTRIBUTE_ERRORMESSAGE,
372
+ error?.message || error.toString(),
373
+ );
374
+ }
375
+
376
+ updateColumnBar.call(this);
377
+ })
378
+ .catch((error) => {
379
+ addAttributeToken(
380
+ this,
381
+ ATTRIBUTE_ERRORMESSAGE,
382
+ error?.message || error.toString(),
383
+ );
384
+ });
385
+ })
386
+ .catch((error) => {
387
+ addAttributeToken(
388
+ this,
389
+ ATTRIBUTE_ERRORMESSAGE,
390
+ error?.message || error.toString(),
391
+ );
392
+ });
393
+ }
394
+
395
+ /**
396
+ * @return {CSSStyleSheet[]}
397
+ */
398
+ static getCSSStyleSheet() {
399
+ return [DatatableStyleSheet];
400
+ }
401
+
402
+ /**
403
+ * Copy a row from the datatable
404
+ *
405
+ * @param {number|string} fromIndex
406
+ * @param {number|string} toIndex
407
+ * @return {DataTable}
408
+ * @fires monster-datatable-row-copied
409
+ */
410
+ copyRow(fromIndex, toIndex) {
411
+ const datasource = this[datasourceLinkedElementSymbol];
412
+ if (!datasource) {
413
+ return this;
414
+ }
415
+ let d = datasource.data;
416
+ let c = clone(d);
417
+
418
+ let rows = c;
419
+ const mapping = this.getOption("mapping.data");
420
+
421
+ if (mapping) {
422
+ rows = c?.[mapping];
423
+ }
424
+
425
+ if (rows === undefined || rows === null) {
426
+ rows = [];
427
+ }
428
+
429
+ if (toIndex === undefined) {
430
+ toIndex = rows.length;
431
+ }
432
+
433
+ if (isString(fromIndex)) {
434
+ fromIndex = parseInt(fromIndex);
435
+ }
436
+ if (isString(toIndex)) {
437
+ toIndex = parseInt(toIndex);
438
+ }
439
+
440
+ if (toIndex < 0 || toIndex > rows.length) {
441
+ throw new RangeError("index out of bounds");
442
+ }
443
+
444
+ validateArray(rows);
445
+ validateInteger(fromIndex);
446
+ validateInteger(toIndex);
447
+
448
+ if (fromIndex < 0 || fromIndex >= rows.length) {
449
+ throw new RangeError("index out of bounds");
450
+ }
451
+
452
+ rows.splice(toIndex, 0, clone(rows[fromIndex]));
453
+ datasource.data = c;
454
+
455
+ fireCustomEvent(this, "monster-datatable-row-copied", {
456
+ index: toIndex,
457
+ });
458
+
459
+ return this;
460
+ }
461
+
462
+ /**
463
+ * Remove a row from the datatable
464
+ *
465
+ * @param {number|string} index
466
+ * @return {DataTable}
467
+ * @fires monster-datatable-row-removed
468
+ */
469
+ removeRow(index) {
470
+ const datasource = this[datasourceLinkedElementSymbol];
471
+ if (!datasource) {
472
+ return this;
473
+ }
474
+ let d = datasource.data;
475
+ let c = clone(d);
476
+
477
+ let rows = c;
478
+ const mapping = this.getOption("mapping.data");
479
+
480
+ if (mapping) {
481
+ rows = c?.[mapping];
482
+ }
483
+
484
+ if (rows === undefined || rows === null) {
485
+ rows = [];
486
+ }
487
+
488
+ if (isString(index)) {
489
+ index = parseInt(index);
490
+ }
491
+
492
+ validateArray(rows);
493
+ validateInteger(index);
494
+
495
+ if (index < 0 || index >= rows.length) {
496
+ throw new RangeError("index out of bounds");
497
+ }
498
+ if (mapping) {
499
+ rows = c?.[mapping];
500
+ }
501
+
502
+ rows.splice(index, 1);
503
+ datasource.data = c;
504
+
505
+ fireCustomEvent(this, "monster-datatable-row-removed", {
506
+ index: index,
507
+ });
508
+
509
+ return this;
510
+ }
511
+
512
+ /**
513
+ * Add a row to the datatable
514
+ *
515
+ * @param {Object} data
516
+ * @return {DataTable}
517
+ *
518
+ * @fires monster-datatable-row-added
519
+ **/
520
+ addRow(data) {
521
+ const datasource = this[datasourceLinkedElementSymbol];
522
+ if (!datasource) {
523
+ return this;
524
+ }
525
+ let d = datasource.data;
526
+ let c = clone(d);
527
+
528
+ let rows = c;
529
+
530
+ const mapping = this.getOption("mapping.data");
531
+ if (mapping) {
532
+ rows = c?.[mapping];
533
+ }
534
+
535
+ if (rows === undefined || rows === null) {
536
+ rows = [];
537
+ }
538
+
539
+ validateArray(rows);
540
+ validateObject(data);
541
+
542
+ rows.push(data);
543
+ datasource.data = c;
544
+
545
+ fireCustomEvent(this, "monster-datatable-row-added", {
546
+ index: rows.length - 1,
547
+ });
548
+
549
+ return this;
550
+ }
543
551
  }
544
552
 
545
553
  /**
@@ -547,7 +555,7 @@ class DataTable extends CustomElement {
547
555
  * @return {string}
548
556
  */
549
557
  function getColumnVisibilityConfigKey() {
550
- return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
558
+ return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
551
559
  }
552
560
 
553
561
  /**
@@ -555,7 +563,7 @@ function getColumnVisibilityConfigKey() {
555
563
  * @return {string}
556
564
  */
557
565
  function getFilterConfigKey() {
558
- return generateUniqueConfigKey("datatable", this?.id, "filter");
566
+ return generateUniqueConfigKey("datatable", this?.id, "filter");
559
567
  }
560
568
 
561
569
  /**
@@ -563,403 +571,410 @@ function getFilterConfigKey() {
563
571
  * @return {Promise}
564
572
  */
565
573
  function getHostConfig(callback) {
566
- const host = findElementWithSelectorUpwards(this, "monster-host");
567
-
568
- if (!(host && this.id)) {
569
- return Promise.resolve({});
570
- }
571
-
572
- if (!host || !isFunction(host?.getConfig)) {
573
- throw new TypeError("the host must be a monster-host");
574
- }
575
-
576
- const configKey = callback.call(this);
577
- return host.hasConfig(configKey).then((hasConfig) => {
578
- if (hasConfig) {
579
- return host.getConfig(configKey);
580
- } else {
581
- return {};
582
- }
583
- });
574
+ const host = findElementWithSelectorUpwards(this, "monster-host");
575
+
576
+ if (!(host && this.id)) {
577
+ return Promise.resolve({});
578
+ }
579
+
580
+ if (!host || !isFunction(host?.getConfig)) {
581
+ throw new TypeError("the host must be a monster-host");
582
+ }
583
+
584
+ const configKey = callback.call(this);
585
+ return host.hasConfig(configKey).then((hasConfig) => {
586
+ if (hasConfig) {
587
+ return host.getConfig(configKey);
588
+ } else {
589
+ return {};
590
+ }
591
+ });
584
592
  }
585
593
 
586
594
  /**
587
595
  * @private
588
596
  */
589
597
  function updateColumnBar() {
590
- if (!this[columnBarElementSymbol]) {
591
- return;
592
- }
593
-
594
- const columns = [];
595
- for (const header of this.getOption("headers")) {
596
- const mode = header.getInternal("mode");
597
-
598
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
599
- continue;
600
- }
601
-
602
- columns.push({
603
- visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
604
- name: header.label,
605
- index: header.index,
606
- });
607
- }
608
-
609
- this[columnBarElementSymbol].setOption("columns", columns);
598
+ if (!this[columnBarElementSymbol]) {
599
+ return;
600
+ }
601
+
602
+ const columns = [];
603
+ for (const header of this.getOption("headers")) {
604
+ const mode = header.getInternal("mode");
605
+
606
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
607
+ continue;
608
+ }
609
+
610
+ columns.push({
611
+ visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
612
+ name: header.label,
613
+ index: header.index,
614
+ });
615
+ }
616
+
617
+ this[columnBarElementSymbol].setOption("columns", columns);
610
618
  }
611
619
 
612
620
  /**
613
621
  * @private
614
622
  */
615
623
  function updateHeaderFromColumnBar() {
616
- if (!this[columnBarElementSymbol]) {
617
- return;
618
- }
624
+ if (!this[columnBarElementSymbol]) {
625
+ return;
626
+ }
619
627
 
620
- const options = this[columnBarElementSymbol].getOption("columns");
621
- if (!isArray(options)) return;
628
+ const options = this[columnBarElementSymbol].getOption("columns");
629
+ if (!isArray(options)) return;
622
630
 
623
- const invisibleMap = {};
631
+ const invisibleMap = {};
624
632
 
625
- for (let i = 0; i < options.length; i++) {
626
- const option = options[i];
627
- invisibleMap[option.index] = option.visible;
628
- }
633
+ for (let i = 0; i < options.length; i++) {
634
+ const option = options[i];
635
+ invisibleMap[option.index] = option.visible;
636
+ }
629
637
 
630
- for (const header of this.getOption("headers")) {
631
- const mode = header.getInternal("mode");
638
+ for (const header of this.getOption("headers")) {
639
+ const mode = header.getInternal("mode");
632
640
 
633
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
634
- continue;
635
- }
641
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
642
+ continue;
643
+ }
636
644
 
637
- if (invisibleMap[header.index] === false) {
638
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
639
- } else {
640
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
641
- }
642
- }
645
+ if (invisibleMap[header.index] === false) {
646
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
647
+ } else {
648
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
649
+ }
650
+ }
643
651
  }
644
652
 
645
653
  /**
646
654
  * @private
647
655
  */
648
656
  function updateConfigColumnBar() {
649
- if (!this[columnBarElementSymbol]) {
650
- return;
651
- }
652
-
653
- const options = this[columnBarElementSymbol].getOption("columns");
654
- if (!isArray(options)) return;
655
-
656
- const map = {};
657
- for (let i = 0; i < options.length; i++) {
658
- const option = options[i];
659
- map[option.name] = option.visible;
660
- }
661
-
662
- const host = findElementWithSelectorUpwards(this, "monster-host");
663
- if (!(host && this.id)) {
664
- return;
665
- }
666
- const configKey = getColumnVisibilityConfigKey.call(this);
667
-
668
- try {
669
- host.setConfig(configKey, map);
670
- } catch (error) {
671
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
672
- }
657
+ if (!this[columnBarElementSymbol]) {
658
+ return;
659
+ }
660
+
661
+ const options = this[columnBarElementSymbol].getOption("columns");
662
+ if (!isArray(options)) return;
663
+
664
+ const map = {};
665
+ for (let i = 0; i < options.length; i++) {
666
+ const option = options[i];
667
+ map[option.name] = option.visible;
668
+ }
669
+
670
+ const host = findElementWithSelectorUpwards(this, "monster-host");
671
+ if (!(host && this.id)) {
672
+ return;
673
+ }
674
+ const configKey = getColumnVisibilityConfigKey.call(this);
675
+
676
+ try {
677
+ host.setConfig(configKey, map);
678
+ } catch (error) {
679
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
680
+ }
673
681
  }
674
682
 
675
683
  /**
676
684
  * @private
677
685
  */
678
686
  function initEventHandler() {
679
- const self = this;
680
-
681
- const quoteOpenChar = this.getOption("copy.quoteOpen");
682
- const quoteCloseChar = this.getOption("copy.quoteClose");
683
- const delimiterChar = this.getOption("copy.delimiter");
684
- const rowBreak = this.getOption("copy.rowBreak");
685
-
686
- self[columnBarElementSymbol].attachObserver(
687
- new Observer((e) => {
688
- updateHeaderFromColumnBar.call(self);
689
- updateGrid.call(self);
690
- updateConfigColumnBar.call(self);
691
- }),
692
- );
693
-
694
- self[gridHeadersElementSymbol].addEventListener("click", function (event) {
695
- let element = null;
696
- const datasource = self[datasourceLinkedElementSymbol];
697
- if (!datasource) {
698
- return;
699
- }
700
-
701
- element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
702
- if (element) {
703
- const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
704
- const headers = self.getOption("headers");
705
-
706
- event.preventDefault();
707
-
708
- headers[index].changeDirection();
709
-
710
- queueMicrotask(function () {
711
- /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
712
- element.setAttribute(
713
- ATTRIBUTE_DATATABLE_SORTABLE,
714
- `${headers[index].field} ${headers[index].direction}`,
715
- );
716
-
717
- storeOrderStatement.call(self, true);
718
- });
719
- }
720
- });
721
-
722
-
723
- const eventHandlerDoubleClickCopyToClipboard = (event) => {
724
- const element = findTargetElementFromEvent(event, "data-monster-head");
725
- if (element) {
726
-
727
-
728
- let text = "";
729
-
730
- if (event.shiftKey) {
731
- const index = element.getAttribute("data-monster-insert-reference");
732
- if (index) {
733
- const cols = self.getGridElements(`[data-monster-insert-reference="${index}"]`);
734
-
735
- const colTexts = []
736
- for (let i = 0; i < cols.length; i++) {
737
- const col = cols[i];
738
-
739
- if (col.querySelector("monster-button-bar") || col.querySelector("monster-button")) {
740
- continue;
741
- }
742
-
743
- if (col.textContent) {
744
- colTexts.push(quoteOpenChar+col.textContent.trim()+quoteCloseChar);
745
- }
746
- }
747
-
748
- text = colTexts.join(delimiterChar);
749
-
750
- }
751
-
752
-
753
- } else {
754
- if (element.querySelector("monster-button-bar") || element.querySelector("monster-button")) {
755
- return;
756
- }
757
-
758
- text = element.textContent.trim();
759
-
760
- }
761
-
762
- if (getWindow().navigator.clipboard && text) {
763
- getWindow().navigator.clipboard.writeText(text).then(
764
- () => {
765
- },
766
- (err) => {
767
- }
768
- );
769
- }
770
- }
771
- }
772
-
773
- if (self.getOption("features.doubleClickCopyToClipboard")) {
774
- self[gridElementSymbol].addEventListener("dblclick", eventHandlerDoubleClickCopyToClipboard);
775
- }
776
-
777
- if (self.getOption("features.copyAll") && this[copyAllElementSymbol]) {
778
- this[copyAllElementSymbol].addEventListener("click", (event) => {
779
- event.preventDefault();
780
-
781
- const table = [];
782
- let currentRow = [];
783
- let currentIndex= null;
784
-
785
- const cols = self.getGridElements(`[data-monster-insert-reference]`);
786
- const rowIndexes = new Map();
787
- cols.forEach((col) => {
788
- const index = col.getAttribute("data-monster-insert-reference");
789
- rowIndexes.set(index, true);
790
- });
791
-
792
- rowIndexes.forEach((value, key) => {
793
- const cols = self.getGridElements(`[data-monster-insert-reference="${key}"]`);
794
-
795
- for (let i = 0; i < cols.length; i++) {
796
- const col = cols[i];
797
-
798
- if (col.querySelector("monster-button-bar") || col.querySelector("monster-button")) {
799
- continue;
800
- }
801
-
802
- if (col.textContent) {
803
- currentRow.push(quoteOpenChar+col.textContent.trim()+quoteCloseChar);
804
- }
805
- }
806
-
807
- if(currentRow.length > 0) {
808
- table.push(currentRow);
809
- }
810
- currentRow = [];
811
- });
812
-
813
- if(table.length > 0) {
814
- const text = table.map(row => row.join(delimiterChar)).join(rowBreak);
815
- if (getWindow().navigator.clipboard && text) {
816
- getWindow().navigator.clipboard.writeText(text).then(
817
- () => {
818
- },
819
- (err) => {
820
- }
821
- );
822
- }
823
- }
824
-
825
- });
826
-
827
-
828
- }
829
-
830
-
831
-
687
+ const self = this;
688
+
689
+ const quoteOpenChar = this.getOption("copy.quoteOpen");
690
+ const quoteCloseChar = this.getOption("copy.quoteClose");
691
+ const delimiterChar = this.getOption("copy.delimiter");
692
+ const rowBreak = this.getOption("copy.rowBreak");
693
+
694
+ self[columnBarElementSymbol].attachObserver(
695
+ new Observer((e) => {
696
+ updateHeaderFromColumnBar.call(self);
697
+ updateGrid.call(self);
698
+ updateConfigColumnBar.call(self);
699
+ }),
700
+ );
701
+
702
+ self[gridHeadersElementSymbol].addEventListener("click", function (event) {
703
+ let element = null;
704
+ const datasource = self[datasourceLinkedElementSymbol];
705
+ if (!datasource) {
706
+ return;
707
+ }
708
+
709
+ element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
710
+ if (element) {
711
+ const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
712
+ const headers = self.getOption("headers");
713
+
714
+ event.preventDefault();
715
+
716
+ headers[index].changeDirection();
717
+
718
+ queueMicrotask(function () {
719
+ /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
720
+ element.setAttribute(
721
+ ATTRIBUTE_DATATABLE_SORTABLE,
722
+ `${headers[index].field} ${headers[index].direction}`,
723
+ );
724
+
725
+ storeOrderStatement.call(self, true);
726
+ });
727
+ }
728
+ });
729
+
730
+ const eventHandlerDoubleClickCopyToClipboard = (event) => {
731
+ const element = findTargetElementFromEvent(event, "data-monster-head");
732
+ if (element) {
733
+ let text = "";
734
+
735
+ if (event.shiftKey) {
736
+ const index = element.getAttribute("data-monster-insert-reference");
737
+ if (index) {
738
+ const cols = self.getGridElements(
739
+ `[data-monster-insert-reference="${index}"]`,
740
+ );
741
+
742
+ const colTexts = [];
743
+ for (let i = 0; i < cols.length; i++) {
744
+ const col = cols[i];
745
+
746
+ if (
747
+ col.querySelector("monster-button-bar") ||
748
+ col.querySelector("monster-button")
749
+ ) {
750
+ continue;
751
+ }
752
+
753
+ if (col.textContent) {
754
+ colTexts.push(
755
+ quoteOpenChar + col.textContent.trim() + quoteCloseChar,
756
+ );
757
+ }
758
+ }
759
+
760
+ text = colTexts.join(delimiterChar);
761
+ }
762
+ } else {
763
+ if (
764
+ element.querySelector("monster-button-bar") ||
765
+ element.querySelector("monster-button")
766
+ ) {
767
+ return;
768
+ }
769
+
770
+ text = element.textContent.trim();
771
+ }
772
+
773
+ if (getWindow().navigator.clipboard && text) {
774
+ getWindow()
775
+ .navigator.clipboard.writeText(text)
776
+ .then(
777
+ () => {},
778
+ (err) => {},
779
+ );
780
+ }
781
+ }
782
+ };
783
+
784
+ if (self.getOption("features.doubleClickCopyToClipboard")) {
785
+ self[gridElementSymbol].addEventListener(
786
+ "dblclick",
787
+ eventHandlerDoubleClickCopyToClipboard,
788
+ );
789
+ }
790
+
791
+ if (self.getOption("features.copyAll") && this[copyAllElementSymbol]) {
792
+ this[copyAllElementSymbol].addEventListener("click", (event) => {
793
+ event.preventDefault();
794
+
795
+ const table = [];
796
+ let currentRow = [];
797
+ let currentIndex = null;
798
+
799
+ const cols = self.getGridElements(`[data-monster-insert-reference]`);
800
+ const rowIndexes = new Map();
801
+ cols.forEach((col) => {
802
+ const index = col.getAttribute("data-monster-insert-reference");
803
+ rowIndexes.set(index, true);
804
+ });
805
+
806
+ rowIndexes.forEach((value, key) => {
807
+ const cols = self.getGridElements(
808
+ `[data-monster-insert-reference="${key}"]`,
809
+ );
810
+
811
+ for (let i = 0; i < cols.length; i++) {
812
+ const col = cols[i];
813
+
814
+ if (
815
+ col.querySelector("monster-button-bar") ||
816
+ col.querySelector("monster-button")
817
+ ) {
818
+ continue;
819
+ }
820
+
821
+ if (col.textContent) {
822
+ currentRow.push(
823
+ quoteOpenChar + col.textContent.trim() + quoteCloseChar,
824
+ );
825
+ }
826
+ }
827
+
828
+ if (currentRow.length > 0) {
829
+ table.push(currentRow);
830
+ }
831
+ currentRow = [];
832
+ });
833
+
834
+ if (table.length > 0) {
835
+ const text = table.map((row) => row.join(delimiterChar)).join(rowBreak);
836
+ if (getWindow().navigator.clipboard && text) {
837
+ getWindow()
838
+ .navigator.clipboard.writeText(text)
839
+ .then(
840
+ () => {},
841
+ (err) => {},
842
+ );
843
+ }
844
+ }
845
+ });
846
+ }
832
847
  }
833
848
 
834
849
  /**
835
850
  * @private
836
851
  */
837
852
  function initGridAndStructs(hostConfig, headerOrderMap) {
838
- const rowID = this.getOption("templateMapping.row-key");
839
-
840
- if (!this[gridElementSymbol]) {
841
- throw new Error("no grid element is defined");
842
- }
843
-
844
- let template;
845
- getSlottedElements.call(this).forEach((e) => {
846
- if (e instanceof HTMLTemplateElement && e.id === rowID) {
847
- template = e;
848
- }
849
- });
850
-
851
- if (!template) {
852
- throw new Error("no template is defined");
853
- }
854
-
855
- const rowCount = template.content.children.length;
856
-
857
- const headers = [];
858
-
859
- for (let i = 0; i < rowCount; i++) {
860
- let hClass = "";
861
- const row = template.content.children[i];
862
-
863
- let mode = "";
864
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
865
- mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
866
- }
867
-
868
- let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
869
- if (!grid || grid === "" || grid === "auto") {
870
- grid = "minmax(0, 1fr)";
871
- }
872
-
873
- let label = "";
874
- let labelKey = "";
875
-
876
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
877
- label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
878
- labelKey = label;
879
-
880
- try {
881
- if (label.startsWith("i18n:")) {
882
- label = label.substring(5, label.length);
883
- label = getDocumentTranslations().getText(label, label);
884
- }
885
- } catch (e) {
886
- label = "i18n error " + label;
887
- }
888
- }
889
-
890
- if (!label) {
891
- label = i + 1 + "";
892
- mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
893
- labelKey = label;
894
- }
895
-
896
- if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
897
- if (hostConfig[label] === false) {
898
- mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
899
- } else {
900
- mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
901
- }
902
- }
903
-
904
- let align = "";
905
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
906
- align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
907
- }
908
-
909
- switch (align) {
910
- case "center":
911
- hClass = "flex-center";
912
- break;
913
- case "end":
914
- hClass = "flex-end";
915
- break;
916
- case "start":
917
- hClass = "flex-start";
918
- break;
919
- default:
920
- hClass = "flex-start";
921
- }
922
-
923
- let field = "";
924
- let direction = DIRECTION_NONE;
925
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
926
- field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE).trim();
927
- const parts = field.split(" ").map((item) => item.trim());
928
- field = parts[0];
929
-
930
- if (headerOrderMap.has(field)) {
931
- direction = headerOrderMap.get(field);
932
- } else if (
933
- parts.length === 2 &&
934
- [DIRECTION_ASC, DIRECTION_DESC].indexOf(parts[1]) !== -1
935
- ) {
936
- direction = parts[1];
937
- }
938
- }
939
-
940
- if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
941
- hClass += " hidden";
942
- }
943
-
944
- const header = new Header();
945
- header.setInternals({
946
- field: field,
947
- label: label,
948
- classes: hClass,
949
- index: i,
950
- mode: mode,
951
- grid: grid,
952
- labelKey: labelKey,
953
- direction: direction,
954
- });
955
-
956
- headers.push(header);
957
- }
958
-
959
- this.setOption("headers", headers);
960
- queueMicrotask(() => {
961
- storeOrderStatement.call(this, this.getOption("features.autoInit"));
962
- });
853
+ const rowID = this.getOption("templateMapping.row-key");
854
+
855
+ if (!this[gridElementSymbol]) {
856
+ throw new Error("no grid element is defined");
857
+ }
858
+
859
+ let template;
860
+ getSlottedElements.call(this).forEach((e) => {
861
+ if (e instanceof HTMLTemplateElement && e.id === rowID) {
862
+ template = e;
863
+ }
864
+ });
865
+
866
+ if (!template) {
867
+ throw new Error("no template is defined");
868
+ }
869
+
870
+ const rowCount = template.content.children.length;
871
+
872
+ const headers = [];
873
+
874
+ for (let i = 0; i < rowCount; i++) {
875
+ let hClass = "";
876
+ const row = template.content.children[i];
877
+
878
+ let mode = "";
879
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
880
+ mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
881
+ }
882
+
883
+ let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
884
+ if (!grid || grid === "" || grid === "auto") {
885
+ grid = "minmax(0, 1fr)";
886
+ }
887
+
888
+ let label = "";
889
+ let labelKey = "";
890
+
891
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
892
+ label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
893
+ labelKey = label;
894
+
895
+ try {
896
+ if (label.startsWith("i18n:")) {
897
+ label = label.substring(5, label.length);
898
+ label = getDocumentTranslations().getText(label, label);
899
+ }
900
+ } catch (e) {
901
+ label = "i18n error " + label;
902
+ }
903
+ }
904
+
905
+ if (!label) {
906
+ label = i + 1 + "";
907
+ mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
908
+ labelKey = label;
909
+ }
910
+
911
+ if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
912
+ if (hostConfig[label] === false) {
913
+ mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
914
+ } else {
915
+ mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
916
+ }
917
+ }
918
+
919
+ let align = "";
920
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
921
+ align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
922
+ }
923
+
924
+ switch (align) {
925
+ case "center":
926
+ hClass = "flex-center";
927
+ break;
928
+ case "end":
929
+ hClass = "flex-end";
930
+ break;
931
+ case "start":
932
+ hClass = "flex-start";
933
+ break;
934
+ default:
935
+ hClass = "flex-start";
936
+ }
937
+
938
+ let field = "";
939
+ let direction = DIRECTION_NONE;
940
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
941
+ field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE).trim();
942
+ const parts = field.split(" ").map((item) => item.trim());
943
+ field = parts[0];
944
+
945
+ if (headerOrderMap.has(field)) {
946
+ direction = headerOrderMap.get(field);
947
+ } else if (
948
+ parts.length === 2 &&
949
+ [DIRECTION_ASC, DIRECTION_DESC].indexOf(parts[1]) !== -1
950
+ ) {
951
+ direction = parts[1];
952
+ }
953
+ }
954
+
955
+ if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
956
+ hClass += " hidden";
957
+ }
958
+
959
+ const header = new Header();
960
+ header.setInternals({
961
+ field: field,
962
+ label: label,
963
+ classes: hClass,
964
+ index: i,
965
+ mode: mode,
966
+ grid: grid,
967
+ labelKey: labelKey,
968
+ direction: direction,
969
+ });
970
+
971
+ headers.push(header);
972
+ }
973
+
974
+ this.setOption("headers", headers);
975
+ queueMicrotask(() => {
976
+ storeOrderStatement.call(this, this.getOption("features.autoInit"));
977
+ });
963
978
  }
964
979
 
965
980
  /**
@@ -967,79 +982,79 @@ function initGridAndStructs(hostConfig, headerOrderMap) {
967
982
  * @return {string}
968
983
  */
969
984
  export function getStoredOrderConfigKey() {
970
- return generateUniqueConfigKey("datatable", this?.id, "stored-order");
985
+ return generateUniqueConfigKey("datatable", this?.id, "stored-order");
971
986
  }
972
987
 
973
988
  /**
974
989
  * @private
975
990
  */
976
991
  function storeOrderStatement(doFetch) {
977
- const headers = this.getOption("headers");
978
- const statement = createOrderStatement(headers);
979
- setDataSource.call(this, {orderBy: statement}, doFetch);
992
+ const headers = this.getOption("headers");
993
+ const statement = createOrderStatement(headers);
994
+ setDataSource.call(this, { orderBy: statement }, doFetch);
980
995
 
981
- const host = findElementWithSelectorUpwards(this, "monster-host");
982
- if (!(host && this.id)) {
983
- return;
984
- }
996
+ const host = findElementWithSelectorUpwards(this, "monster-host");
997
+ if (!(host && this.id)) {
998
+ return;
999
+ }
985
1000
 
986
- const configKey = getStoredOrderConfigKey.call(this);
1001
+ const configKey = getStoredOrderConfigKey.call(this);
987
1002
 
988
- // statement explode with , and remove all empty
989
- const list = statement.split(",").filter((item) => item.trim() !== "");
990
- if (list.length === 0) {
991
- return;
992
- }
1003
+ // statement explode with , and remove all empty
1004
+ const list = statement.split(",").filter((item) => item.trim() !== "");
1005
+ if (list.length === 0) {
1006
+ return;
1007
+ }
993
1008
 
994
- host.setConfig(configKey, list);
1009
+ host.setConfig(configKey, list);
995
1010
  }
996
1011
 
997
1012
  /**
998
1013
  * @private
999
1014
  */
1000
1015
  function updateGrid() {
1001
- if (!this[gridElementSymbol]) {
1002
- throw new Error("no grid element is defined");
1003
- }
1004
-
1005
- let gridTemplateColumns = "";
1006
-
1007
- const headers = this.getOption("headers");
1008
-
1009
- let styles = "";
1010
-
1011
- for (let i = 0; i < headers.length; i++) {
1012
- const header = headers[i];
1013
-
1014
- if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
1015
- styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
1016
- styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
1017
- } else {
1018
- gridTemplateColumns += `${header.grid} `;
1019
- }
1020
- }
1021
-
1022
- const sheet = new CSSStyleSheet();
1023
- if (styles !== "") sheet.replaceSync(styles);
1024
- this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
1025
-
1026
- const bodyWidth = this.parentNode.clientWidth;
1027
-
1028
- const breakpoint = this.getOption("responsive.breakpoint");
1029
- this[dataControlElementSymbol].classList.toggle(
1030
- "small",
1031
- bodyWidth <= breakpoint,
1032
- );
1033
-
1034
- if (bodyWidth > breakpoint) {
1035
- this[gridElementSymbol].style.gridTemplateColumns =
1036
- `${gridTemplateColumns}`;
1037
- this[gridHeadersElementSymbol].style.gridTemplateColumns =
1038
- `${gridTemplateColumns}`;
1039
- } else {
1040
- this[gridElementSymbol].style.gridTemplateColumns = "auto";
1041
- this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
1042
- }
1016
+ if (!this[gridElementSymbol]) {
1017
+ throw new Error("no grid element is defined");
1018
+ }
1019
+
1020
+ let gridTemplateColumns = "";
1021
+
1022
+ const headers = this.getOption("headers");
1023
+
1024
+ let styles = "";
1025
+
1026
+ for (let i = 0; i < headers.length; i++) {
1027
+ const header = headers[i];
1028
+
1029
+ if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
1030
+ styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
1031
+ styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
1032
+ } else {
1033
+ gridTemplateColumns += `${header.grid} `;
1034
+ }
1035
+ }
1036
+
1037
+ const sheet = new CSSStyleSheet();
1038
+ if (styles !== "") sheet.replaceSync(styles);
1039
+ this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
1040
+
1041
+ const bodyWidth = this.parentNode.clientWidth;
1042
+
1043
+ const breakpoint = this.getOption("responsive.breakpoint");
1044
+ this[dataControlElementSymbol].classList.toggle(
1045
+ "small",
1046
+ bodyWidth <= breakpoint,
1047
+ );
1048
+
1049
+ if (bodyWidth > breakpoint) {
1050
+ this[gridElementSymbol].style.gridTemplateColumns =
1051
+ `${gridTemplateColumns}`;
1052
+ this[gridHeadersElementSymbol].style.gridTemplateColumns =
1053
+ `${gridTemplateColumns}`;
1054
+ } else {
1055
+ this[gridElementSymbol].style.gridTemplateColumns = "auto";
1056
+ this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
1057
+ }
1043
1058
  }
1044
1059
 
1045
1060
  /**
@@ -1047,20 +1062,20 @@ function updateGrid() {
1047
1062
  * @param {Header[]} headers
1048
1063
  * @param {bool} doFetch
1049
1064
  */
1050
- function setDataSource({orderBy}, doFetch) {
1051
- const datasource = this[datasourceLinkedElementSymbol];
1065
+ function setDataSource({ orderBy }, doFetch) {
1066
+ const datasource = this[datasourceLinkedElementSymbol];
1052
1067
 
1053
- if (!datasource) {
1054
- return;
1055
- }
1068
+ if (!datasource) {
1069
+ return;
1070
+ }
1056
1071
 
1057
- if (isFunction(datasource?.setParameters)) {
1058
- datasource.setParameters({orderBy});
1059
- }
1072
+ if (isFunction(datasource?.setParameters)) {
1073
+ datasource.setParameters({ orderBy });
1074
+ }
1060
1075
 
1061
- if (doFetch !== false && isFunction(datasource?.fetch)) {
1062
- datasource.fetch();
1063
- }
1076
+ if (doFetch !== false && isFunction(datasource?.fetch)) {
1077
+ datasource.fetch();
1078
+ }
1064
1079
  }
1065
1080
 
1066
1081
  /**
@@ -1068,28 +1083,28 @@ function setDataSource({orderBy}, doFetch) {
1068
1083
  * @return {DataTable}
1069
1084
  */
1070
1085
  function initControlReferences() {
1071
- if (!this.shadowRoot) {
1072
- throw new Error("no shadow-root is defined");
1073
- }
1074
-
1075
- this[dataControlElementSymbol] = this.shadowRoot.querySelector(
1076
- "[data-monster-role=control]",
1077
- );
1078
-
1079
- this[gridElementSymbol] = this.shadowRoot.querySelector(
1080
- "[data-monster-role=datatable]",
1081
- );
1082
- this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
1083
- "[data-monster-role=datatable-headers]",
1084
- );
1085
- this[columnBarElementSymbol] =
1086
- this.shadowRoot.querySelector("monster-column-bar");
1087
-
1088
- this[copyAllElementSymbol] = this.shadowRoot.querySelector(
1089
- "[data-monster-role=copy-all]",
1090
- );
1091
-
1092
- return this;
1086
+ if (!this.shadowRoot) {
1087
+ throw new Error("no shadow-root is defined");
1088
+ }
1089
+
1090
+ this[dataControlElementSymbol] = this.shadowRoot.querySelector(
1091
+ "[data-monster-role=control]",
1092
+ );
1093
+
1094
+ this[gridElementSymbol] = this.shadowRoot.querySelector(
1095
+ "[data-monster-role=datatable]",
1096
+ );
1097
+ this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
1098
+ "[data-monster-role=datatable-headers]",
1099
+ );
1100
+ this[columnBarElementSymbol] =
1101
+ this.shadowRoot.querySelector("monster-column-bar");
1102
+
1103
+ this[copyAllElementSymbol] = this.shadowRoot.querySelector(
1104
+ "[data-monster-role=copy-all]",
1105
+ );
1106
+
1107
+ return this;
1093
1108
  }
1094
1109
 
1095
1110
  /**
@@ -1099,22 +1114,22 @@ function initControlReferences() {
1099
1114
  * @throws {Error} the datasource could not be initialized
1100
1115
  */
1101
1116
  function initOptionsFromArguments() {
1102
- const options = {};
1103
- const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
1104
-
1105
- if (selector) {
1106
- options.datasource = {selector: selector};
1107
- }
1108
-
1109
- const breakpoint = this.getAttribute(
1110
- ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
1111
- );
1112
- if (breakpoint) {
1113
- options.responsive = {};
1114
- options.responsive.breakpoint = parseInt(breakpoint);
1115
- }
1116
-
1117
- return options;
1117
+ const options = {};
1118
+ const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
1119
+
1120
+ if (selector) {
1121
+ options.datasource = { selector: selector };
1122
+ }
1123
+
1124
+ const breakpoint = this.getAttribute(
1125
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
1126
+ );
1127
+ if (breakpoint) {
1128
+ options.responsive = {};
1129
+ options.responsive.breakpoint = parseInt(breakpoint);
1130
+ }
1131
+
1132
+ return options;
1118
1133
  }
1119
1134
 
1120
1135
  /**
@@ -1122,7 +1137,7 @@ function initOptionsFromArguments() {
1122
1137
  * @return {string}
1123
1138
  */
1124
1139
  function getEmptyTemplate() {
1125
- return `<monster-state data-monster-role="empty-without-action">
1140
+ return `<monster-state data-monster-role="empty-without-action">
1126
1141
  <div part="visual">
1127
1142
  <svg width="4rem" height="4rem" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
1128
1143
  <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"/>
@@ -1140,8 +1155,8 @@ function getEmptyTemplate() {
1140
1155
  * @return {string}
1141
1156
  */
1142
1157
  function getTemplate() {
1143
- // language=HTML
1144
- return `
1158
+ // language=HTML
1159
+ return `
1145
1160
  <div data-monster-role="control" part="control" data-monster-attributes="class path:classes.control">
1146
1161
  <template id="headers-row">
1147
1162
  <div data-monster-attributes="class path:headers-row.classes,
@@ -1155,6 +1170,10 @@ function getTemplate() {
1155
1170
  <slot name="filter"></slot>
1156
1171
  </div>
1157
1172
  <div class="bar">
1173
+ <monster-context-help
1174
+ data-monster-attributes="class path:features.help | ?::hidden"
1175
+ data-monster-replace="path:labels.helpText"
1176
+ ></monster-context-help>
1158
1177
  <a href="#" data-monster-attributes="class path:features.copyAll | ?::hidden" data-monster-role="copy-all" data-monster-replace="path:locale.copyAll">Copy all</a>
1159
1178
  <monster-column-bar
1160
1179
  data-monster-attributes="class path:features.settings | ?::hidden"></monster-column-bar>