@schukai/monster 3.55.4 → 3.55.5

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 (38) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/package.json +1 -1
  3. package/source/components/datatable/datatable.mjs +600 -586
  4. package/source/components/datatable/stylesheet/column-bar.mjs +1 -1
  5. package/source/components/datatable/stylesheet/dataset.mjs +1 -1
  6. package/source/components/datatable/stylesheet/datatable.mjs +1 -1
  7. package/source/components/datatable/stylesheet/embedded-pagination.mjs +1 -1
  8. package/source/components/datatable/stylesheet/filter.mjs +1 -1
  9. package/source/components/datatable/stylesheet/pagination.mjs +1 -1
  10. package/source/components/datatable/stylesheet/select-filter.mjs +1 -1
  11. package/source/components/datatable/stylesheet/status.mjs +1 -1
  12. package/source/components/form/stylesheet/action-button.mjs +1 -1
  13. package/source/components/form/stylesheet/button-bar.mjs +1 -1
  14. package/source/components/form/stylesheet/button.mjs +1 -1
  15. package/source/components/form/stylesheet/confirm-button.mjs +1 -1
  16. package/source/components/form/stylesheet/form.mjs +1 -1
  17. package/source/components/form/stylesheet/select.mjs +1 -1
  18. package/source/components/form/stylesheet/state-button.mjs +1 -1
  19. package/source/components/form/stylesheet/tabs.mjs +1 -1
  20. package/source/components/host/stylesheet/call-button.mjs +1 -1
  21. package/source/components/host/stylesheet/collapse.mjs +1 -1
  22. package/source/components/host/stylesheet/details.mjs +1 -1
  23. package/source/components/host/stylesheet/host.mjs +1 -1
  24. package/source/components/host/stylesheet/overlay.mjs +1 -1
  25. package/source/components/host/stylesheet/toggle-button.mjs +1 -1
  26. package/source/components/host/stylesheet/viewer.mjs +1 -1
  27. package/source/components/notify/stylesheet/message.mjs +1 -1
  28. package/source/components/notify/stylesheet/notify.mjs +1 -1
  29. package/source/components/state/stylesheet/log.mjs +1 -1
  30. package/source/components/state/stylesheet/state.mjs +1 -1
  31. package/source/components/stylesheet/border.mjs +1 -1
  32. package/source/components/stylesheet/card.mjs +1 -1
  33. package/source/components/stylesheet/form.mjs +1 -1
  34. package/source/components/stylesheet/icons.mjs +1 -1
  35. package/source/components/stylesheet/normalize.mjs +1 -1
  36. package/source/components/stylesheet/typography.mjs +1 -1
  37. package/source/types/version.mjs +1 -1
  38. package/test/cases/monster.mjs +1 -1
@@ -3,63 +3,63 @@
3
3
  * SPDX-License-Identifier: AGPL-3.0
4
4
  */
5
5
 
6
- import { Datasource } from "./datasource.mjs";
6
+ import {Datasource} from "./datasource.mjs";
7
7
  import {
8
- assembleMethodSymbol,
9
- CustomElement,
10
- registerCustomElement,
11
- getSlottedElements,
8
+ assembleMethodSymbol,
9
+ CustomElement,
10
+ registerCustomElement,
11
+ getSlottedElements,
12
12
  } from "../../dom/customelement.mjs";
13
- import { findTargetElementFromEvent } from "../../dom/events.mjs";
13
+ import {findTargetElementFromEvent} from "../../dom/events.mjs";
14
14
  import {
15
- isString,
16
- isFunction,
17
- isInstance,
18
- isObject,
19
- isArray,
15
+ isString,
16
+ isFunction,
17
+ isInstance,
18
+ isObject,
19
+ isArray,
20
20
  } from "../../types/is.mjs";
21
- import { Observer } from "../../types/observer.mjs";
21
+ import {Observer} from "../../types/observer.mjs";
22
22
  import {
23
- ATTRIBUTE_DATATABLE_HEAD,
24
- ATTRIBUTE_DATATABLE_GRID_TEMPLATE,
25
- ATTRIBUTE_DATASOURCE_SELECTOR,
26
- ATTRIBUTE_DATATABLE_ALIGN,
27
- ATTRIBUTE_DATATABLE_SORTABLE,
28
- ATTRIBUTE_DATATABLE_MODE,
29
- ATTRIBUTE_DATATABLE_INDEX,
30
- ATTRIBUTE_DATATABLE_MODE_HIDDEN,
31
- ATTRIBUTE_DATATABLE_MODE_VISIBLE,
32
- ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
33
- ATTRIBUTE_DATATABLE_MODE_FIXED,
23
+ ATTRIBUTE_DATATABLE_HEAD,
24
+ ATTRIBUTE_DATATABLE_GRID_TEMPLATE,
25
+ ATTRIBUTE_DATASOURCE_SELECTOR,
26
+ ATTRIBUTE_DATATABLE_ALIGN,
27
+ ATTRIBUTE_DATATABLE_SORTABLE,
28
+ ATTRIBUTE_DATATABLE_MODE,
29
+ ATTRIBUTE_DATATABLE_INDEX,
30
+ ATTRIBUTE_DATATABLE_MODE_HIDDEN,
31
+ ATTRIBUTE_DATATABLE_MODE_VISIBLE,
32
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
33
+ ATTRIBUTE_DATATABLE_MODE_FIXED,
34
34
  } from "./constants.mjs";
35
- import { instanceSymbol } from "../../constants.mjs";
35
+ import {instanceSymbol} from "../../constants.mjs";
36
36
  import {
37
- Header,
38
- createOrderStatement,
39
- DIRECTION_ASC,
40
- DIRECTION_DESC,
41
- DIRECTION_NONE,
37
+ Header,
38
+ createOrderStatement,
39
+ DIRECTION_ASC,
40
+ DIRECTION_DESC,
41
+ DIRECTION_NONE,
42
42
  } from "./datatable/header.mjs";
43
- import { getStoredFilterConfigKey } from "./filter/util.mjs";
44
- import { DatatableStyleSheet } from "./stylesheet/datatable.mjs";
43
+ import {getStoredFilterConfigKey} from "./filter/util.mjs";
44
+ import {DatatableStyleSheet} from "./stylesheet/datatable.mjs";
45
45
  import {
46
- handleDataSourceChanges,
47
- datasourceLinkedElementSymbol,
46
+ handleDataSourceChanges,
47
+ datasourceLinkedElementSymbol,
48
48
  } from "./util.mjs";
49
49
  import "./columnbar.mjs";
50
50
  import "./filter-button.mjs";
51
- import { getDocument, getWindow } from "../../dom/util.mjs";
52
- import { addAttributeToken } from "../../dom/attributes.mjs";
53
- import { ATTRIBUTE_ERRORMESSAGE } from "../../dom/constants.mjs";
54
- import { getDocumentTranslations } from "../../i18n/translations.mjs";
51
+ import {getDocument, getWindow} from "../../dom/util.mjs";
52
+ import {addAttributeToken} from "../../dom/attributes.mjs";
53
+ import {ATTRIBUTE_ERRORMESSAGE} from "../../dom/constants.mjs";
54
+ import {getDocumentTranslations} from "../../i18n/translations.mjs";
55
55
  import "../state/state.mjs";
56
56
  import "../host/collapse.mjs";
57
- import { generateUniqueConfigKey } from "../host/util.mjs";
57
+ import {generateUniqueConfigKey} from "../host/util.mjs";
58
58
 
59
59
  import "./datasource/dom.mjs";
60
60
  import "./datasource/rest.mjs";
61
61
 
62
- export { DataTable };
62
+ export {DataTable};
63
63
 
64
64
  /**
65
65
  * @private
@@ -120,209 +120,209 @@ const columnBarElementSymbol = Symbol("columnBarElement");
120
120
  * @summary A data table
121
121
  */
122
122
  class DataTable extends CustomElement {
123
- /**
124
- * This method is called by the `instanceof` operator.
125
- * @returns {symbol}
126
- */
127
- static get [instanceSymbol]() {
128
- return Symbol.for("@schukai/monster/components/datatable@@instance");
129
- }
130
-
131
- /**
132
- * To set the options via the html tag the attribute `data-monster-options` must be used.
133
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
134
- *
135
- * The individual configuration values can be found in the table.
136
- *
137
- * @property {Object} templates Template definitions
138
- * @property {string} templates.main Main template
139
- * @property {Object} datasource Datasource configuration
140
- * @property {string} datasource.selector Selector for the datasource
141
- * @property {Object} mapping Mapping configuration
142
- * @property {string} mapping.data Data mapping
143
- * @property {Array} data Data
144
- * @property {Array} headers Headers
145
- * @property {Object} responsive Responsive configuration
146
- * @property {number} responsive.breakpoint Breakpoint for responsive mode
147
- * @property {Object} labels Labels
148
- * @property {string} labels.theListContainsNoEntries Label for empty state
149
- * @property {Object} features Features
150
- * @property {boolean} features.settings Settings feature
151
- * @property {boolean} features.footer Footer feature
152
- * @property {boolean} features.autoInit Auto init feature (init datasource automatically)
153
- * @property {Object} templateMapping Template mapping
154
- * @property {string} templateMapping.row-key Row key
155
- * @property {string} templateMapping.filter-id Filter id
156
- **/
157
- get defaults() {
158
- return Object.assign(
159
- {},
160
- super.defaults,
161
- {
162
- templates: {
163
- main: getTemplate(),
164
- emptyState: getEmptyTemplate(),
165
- },
166
-
167
- datasource: {
168
- selector: null,
169
- },
170
-
171
- mapping: {
172
- data: "dataset",
173
- },
174
-
175
- data: [],
176
- headers: [],
177
-
178
- responsive: {
179
- breakpoint: 800,
180
- },
181
-
182
- labels: {
183
- theListContainsNoEntries: "The list contains no entries",
184
- },
185
-
186
- features: {
187
- settings: true,
188
- footer: true,
189
- autoInit: true,
190
- },
191
-
192
- templateMapping: {
193
- "row-key": null,
194
- "filter-id": null,
195
- },
196
- },
197
- initOptionsFromArguments.call(this),
198
- );
199
- }
200
-
201
- /**
202
- *
203
- * @param {string} selector
204
- * @returns {NodeListOf<*>}
205
- */
206
- getGridElements(selector) {
207
- return this[gridElementSymbol].querySelectorAll(selector);
208
- }
209
-
210
- /**
211
- *
212
- * @return {string}
213
- */
214
- static getTag() {
215
- return "monster-datatable";
216
- }
217
-
218
- /**
219
- *
220
- * @return {Monster.Components.Form.Form}
221
- */
222
- [assembleMethodSymbol]() {
223
- const rawKey = this.getOption("templateMapping.row-key");
224
-
225
- if (rawKey === null) {
226
- if (this.id !== null && this.id !== "") {
227
- const rawKey = this.getOption("templateMapping.row-key");
228
- if (rawKey === null) {
229
- this.setOption("templateMapping.row-key", this.id + "-row");
230
- }
231
- } else {
232
- this.setOption("templateMapping.row-key", "row");
233
- }
234
- }
235
-
236
- if (this.id !== null && this.id !== "") {
237
- this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
238
- } else {
239
- this.setOption("templateMapping.filter-id", "filter");
240
- }
241
-
242
- super[assembleMethodSymbol]();
243
-
244
- initControlReferences.call(this);
245
- initEventHandler.call(this);
246
-
247
- const selector = this.getOption("datasource.selector");
248
-
249
- if (isString(selector)) {
250
- const elements = document.querySelectorAll(selector);
251
- if (elements.length !== 1) {
252
- throw new Error("the selector must match exactly one element");
253
- }
254
-
255
- const element = elements[0];
256
-
257
- if (!isInstance(element, Datasource)) {
258
- throw new TypeError("the element must be a datasource");
259
- }
260
-
261
- this[datasourceLinkedElementSymbol] = element;
262
-
263
- setTimeout(() => {
264
- handleDataSourceChanges.call(this);
265
- element.datasource.attachObserver(
266
- new Observer(handleDataSourceChanges.bind(this)),
267
- );
268
- }, 0);
269
- }
270
-
271
- getHostConfig
272
- .call(this, getColumnVisibilityConfigKey)
273
- .then((config) => {
274
- const headerOrderMap = new Map();
275
-
276
- getHostConfig
277
- .call(this, getStoredOrderConfigKey)
278
- .then((orderConfig) => {
279
- if (isArray(orderConfig) || orderConfig.length > 0) {
280
- for (let i = 0; i < orderConfig.length; i++) {
281
- const item = orderConfig[i];
282
- const parts = item.split(" ");
283
- const field = parts[0];
284
- const direction = parts[1] || DIRECTION_ASC;
285
- headerOrderMap.set(field, direction);
286
- }
287
- }
288
- })
289
- .then(() => {
290
- try {
291
- initGridAndStructs.call(this, config, headerOrderMap);
292
- } catch (error) {
293
- addAttributeToken(
294
- this,
295
- ATTRIBUTE_ERRORMESSAGE,
296
- error?.message || error.toString(),
297
- );
298
- }
299
-
300
- updateColumnBar.call(this);
301
- })
302
- .catch((error) => {
303
- addAttributeToken(
304
- this,
305
- ATTRIBUTE_ERRORMESSAGE,
306
- error?.message || error.toString(),
307
- );
308
- });
309
- })
310
- .catch((error) => {
311
- addAttributeToken(
312
- this,
313
- ATTRIBUTE_ERRORMESSAGE,
314
- error?.message || error.toString(),
315
- );
316
- });
317
- }
318
-
319
- /**
320
- *
321
- * @return {CSSStyleSheet[]}
322
- */
323
- static getCSSStyleSheet() {
324
- return [DatatableStyleSheet];
325
- }
123
+ /**
124
+ * This method is called by the `instanceof` operator.
125
+ * @returns {symbol}
126
+ */
127
+ static get [instanceSymbol]() {
128
+ return Symbol.for("@schukai/monster/components/datatable@@instance");
129
+ }
130
+
131
+ /**
132
+ * To set the options via the html tag the attribute `data-monster-options` must be used.
133
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
134
+ *
135
+ * The individual configuration values can be found in the table.
136
+ *
137
+ * @property {Object} templates Template definitions
138
+ * @property {string} templates.main Main template
139
+ * @property {Object} datasource Datasource configuration
140
+ * @property {string} datasource.selector Selector for the datasource
141
+ * @property {Object} mapping Mapping configuration
142
+ * @property {string} mapping.data Data mapping
143
+ * @property {Array} data Data
144
+ * @property {Array} headers Headers
145
+ * @property {Object} responsive Responsive configuration
146
+ * @property {number} responsive.breakpoint Breakpoint for responsive mode
147
+ * @property {Object} labels Labels
148
+ * @property {string} labels.theListContainsNoEntries Label for empty state
149
+ * @property {Object} features Features
150
+ * @property {boolean} features.settings Settings feature
151
+ * @property {boolean} features.footer Footer feature
152
+ * @property {boolean} features.autoInit Auto init feature (init datasource automatically)
153
+ * @property {Object} templateMapping Template mapping
154
+ * @property {string} templateMapping.row-key Row key
155
+ * @property {string} templateMapping.filter-id Filter id
156
+ **/
157
+ get defaults() {
158
+ return Object.assign(
159
+ {},
160
+ super.defaults,
161
+ {
162
+ templates: {
163
+ main: getTemplate(),
164
+ emptyState: getEmptyTemplate(),
165
+ },
166
+
167
+ datasource: {
168
+ selector: null,
169
+ },
170
+
171
+ mapping: {
172
+ data: "dataset",
173
+ },
174
+
175
+ data: [],
176
+ headers: [],
177
+
178
+ responsive: {
179
+ breakpoint: 800,
180
+ },
181
+
182
+ labels: {
183
+ theListContainsNoEntries: "The list contains no entries",
184
+ },
185
+
186
+ features: {
187
+ settings: true,
188
+ footer: true,
189
+ autoInit: true,
190
+ },
191
+
192
+ templateMapping: {
193
+ "row-key": null,
194
+ "filter-id": null,
195
+ },
196
+ },
197
+ initOptionsFromArguments.call(this),
198
+ );
199
+ }
200
+
201
+ /**
202
+ *
203
+ * @param {string} selector
204
+ * @returns {NodeListOf<*>}
205
+ */
206
+ getGridElements(selector) {
207
+ return this[gridElementSymbol].querySelectorAll(selector);
208
+ }
209
+
210
+ /**
211
+ *
212
+ * @return {string}
213
+ */
214
+ static getTag() {
215
+ return "monster-datatable";
216
+ }
217
+
218
+ /**
219
+ *
220
+ * @return {Monster.Components.Form.Form}
221
+ */
222
+ [assembleMethodSymbol]() {
223
+ const rawKey = this.getOption("templateMapping.row-key");
224
+
225
+ if (rawKey === null) {
226
+ if (this.id !== null && this.id !== "") {
227
+ const rawKey = this.getOption("templateMapping.row-key");
228
+ if (rawKey === null) {
229
+ this.setOption("templateMapping.row-key", this.id + "-row");
230
+ }
231
+ } else {
232
+ this.setOption("templateMapping.row-key", "row");
233
+ }
234
+ }
235
+
236
+ if (this.id !== null && this.id !== "") {
237
+ this.setOption("templateMapping.filter-id", "" + this.id + "-filter");
238
+ } else {
239
+ this.setOption("templateMapping.filter-id", "filter");
240
+ }
241
+
242
+ super[assembleMethodSymbol]();
243
+
244
+ initControlReferences.call(this);
245
+ initEventHandler.call(this);
246
+
247
+ const selector = this.getOption("datasource.selector");
248
+
249
+ if (isString(selector)) {
250
+ const elements = document.querySelectorAll(selector);
251
+ if (elements.length !== 1) {
252
+ throw new Error("the selector must match exactly one element");
253
+ }
254
+
255
+ const element = elements[0];
256
+
257
+ if (!isInstance(element, Datasource)) {
258
+ throw new TypeError("the element must be a datasource");
259
+ }
260
+
261
+ this[datasourceLinkedElementSymbol] = element;
262
+
263
+ setTimeout(() => {
264
+ handleDataSourceChanges.call(this);
265
+ element.datasource.attachObserver(
266
+ new Observer(handleDataSourceChanges.bind(this)),
267
+ );
268
+ }, 0);
269
+ }
270
+
271
+ getHostConfig
272
+ .call(this, getColumnVisibilityConfigKey)
273
+ .then((config) => {
274
+ const headerOrderMap = new Map();
275
+
276
+ getHostConfig
277
+ .call(this, getStoredOrderConfigKey)
278
+ .then((orderConfig) => {
279
+ if (isArray(orderConfig) || orderConfig.length > 0) {
280
+ for (let i = 0; i < orderConfig.length; i++) {
281
+ const item = orderConfig[i];
282
+ const parts = item.split(" ");
283
+ const field = parts[0];
284
+ const direction = parts[1] || DIRECTION_ASC;
285
+ headerOrderMap.set(field, direction);
286
+ }
287
+ }
288
+ })
289
+ .then(() => {
290
+ try {
291
+ initGridAndStructs.call(this, config, headerOrderMap);
292
+ } catch (error) {
293
+ addAttributeToken(
294
+ this,
295
+ ATTRIBUTE_ERRORMESSAGE,
296
+ error?.message || error.toString(),
297
+ );
298
+ }
299
+
300
+ updateColumnBar.call(this);
301
+ })
302
+ .catch((error) => {
303
+ addAttributeToken(
304
+ this,
305
+ ATTRIBUTE_ERRORMESSAGE,
306
+ error?.message || error.toString(),
307
+ );
308
+ });
309
+ })
310
+ .catch((error) => {
311
+ addAttributeToken(
312
+ this,
313
+ ATTRIBUTE_ERRORMESSAGE,
314
+ error?.message || error.toString(),
315
+ );
316
+ });
317
+ }
318
+
319
+ /**
320
+ *
321
+ * @return {CSSStyleSheet[]}
322
+ */
323
+ static getCSSStyleSheet() {
324
+ return [DatatableStyleSheet];
325
+ }
326
326
  }
327
327
 
328
328
  /**
@@ -330,7 +330,7 @@ class DataTable extends CustomElement {
330
330
  * @returns {string}
331
331
  */
332
332
  function getColumnVisibilityConfigKey() {
333
- return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
333
+ return generateUniqueConfigKey("datatable", this?.id, "columns-visibility");
334
334
  }
335
335
 
336
336
  /**
@@ -338,7 +338,7 @@ function getColumnVisibilityConfigKey() {
338
338
  * @returns {string}
339
339
  */
340
340
  function getFilterConfigKey() {
341
- return generateUniqueConfigKey("datatable", this?.id, "filter");
341
+ return generateUniqueConfigKey("datatable", this?.id, "filter");
342
342
  }
343
343
 
344
344
  /**
@@ -346,279 +346,292 @@ function getFilterConfigKey() {
346
346
  * @returns {Promise}
347
347
  */
348
348
  function getHostConfig(callback) {
349
- const document = getDocument();
350
- const host = document.querySelector("monster-host");
351
-
352
- if (!(host && this.id)) {
353
- return Promise.resolve({});
354
- }
355
-
356
- if (!host || !isFunction(host?.getConfig)) {
357
- throw new TypeError("the host must be a monster-host");
358
- }
359
-
360
- const configKey = callback.call(this);
361
- return host.hasConfig(configKey).then((hasConfig) => {
362
- if (hasConfig) {
363
- return host.getConfig(configKey);
364
- } else {
365
- return {};
366
- }
367
- });
349
+ const document = getDocument();
350
+ const host = document.querySelector("monster-host");
351
+
352
+ if (!(host && this.id)) {
353
+ return Promise.resolve({});
354
+ }
355
+
356
+ if (!host || !isFunction(host?.getConfig)) {
357
+ throw new TypeError("the host must be a monster-host");
358
+ }
359
+
360
+ const configKey = callback.call(this);
361
+ return host.hasConfig(configKey).then((hasConfig) => {
362
+ if (hasConfig) {
363
+ return host.getConfig(configKey);
364
+ } else {
365
+ return {};
366
+ }
367
+ });
368
368
  }
369
369
 
370
370
  /**
371
371
  * @private
372
372
  */
373
373
  function updateColumnBar() {
374
- if (!this[columnBarElementSymbol]) {
375
- return;
376
- }
377
-
378
- const columns = [];
379
- for (const header of this.getOption("headers")) {
380
- const mode = header.getInternal("mode");
381
-
382
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
383
- continue;
384
- }
385
-
386
- columns.push({
387
- visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
388
- name: header.label,
389
- index: header.index,
390
- });
391
- }
392
-
393
- this[columnBarElementSymbol].setOption("columns", columns);
374
+ if (!this[columnBarElementSymbol]) {
375
+ return;
376
+ }
377
+
378
+ const columns = [];
379
+ for (const header of this.getOption("headers")) {
380
+ const mode = header.getInternal("mode");
381
+
382
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
383
+ continue;
384
+ }
385
+
386
+ columns.push({
387
+ visible: mode !== ATTRIBUTE_DATATABLE_MODE_HIDDEN,
388
+ name: header.label,
389
+ index: header.index,
390
+ });
391
+ }
392
+
393
+ this[columnBarElementSymbol].setOption("columns", columns);
394
394
  }
395
395
 
396
396
  /**
397
397
  * @private
398
398
  */
399
399
  function updateHeaderFromColumnBar() {
400
- if (!this[columnBarElementSymbol]) {
401
- return;
402
- }
400
+ if (!this[columnBarElementSymbol]) {
401
+ return;
402
+ }
403
403
 
404
- const options = this[columnBarElementSymbol].getOption("columns");
405
- if (!isArray(options)) return;
404
+ const options = this[columnBarElementSymbol].getOption("columns");
405
+ if (!isArray(options)) return;
406
406
 
407
- const invisibleMap = {};
407
+ const invisibleMap = {};
408
408
 
409
- for (let i = 0; i < options.length; i++) {
410
- const option = options[i];
411
- invisibleMap[option.index] = option.visible;
412
- }
409
+ for (let i = 0; i < options.length; i++) {
410
+ const option = options[i];
411
+ invisibleMap[option.index] = option.visible;
412
+ }
413
413
 
414
- for (const header of this.getOption("headers")) {
415
- const mode = header.getInternal("mode");
414
+ for (const header of this.getOption("headers")) {
415
+ const mode = header.getInternal("mode");
416
416
 
417
- if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
418
- continue;
419
- }
417
+ if (mode === ATTRIBUTE_DATATABLE_MODE_FIXED) {
418
+ continue;
419
+ }
420
420
 
421
- if (invisibleMap[header.index] === false) {
422
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
423
- } else {
424
- header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
425
- }
426
- }
421
+ if (invisibleMap[header.index] === false) {
422
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_HIDDEN);
423
+ } else {
424
+ header.setInternal("mode", ATTRIBUTE_DATATABLE_MODE_VISIBLE);
425
+ }
426
+ }
427
427
  }
428
428
 
429
429
  /**
430
430
  * @private
431
431
  */
432
432
  function updateConfigColumnBar() {
433
- if (!this[columnBarElementSymbol]) {
434
- return;
435
- }
436
-
437
- const options = this[columnBarElementSymbol].getOption("columns");
438
- if (!isArray(options)) return;
439
-
440
- const map = {};
441
- for (let i = 0; i < options.length; i++) {
442
- const option = options[i];
443
- map[option.name] = option.visible;
444
- }
445
-
446
- const document = getDocument();
447
- const host = document.querySelector("monster-host");
448
- if (!(host && this.id)) {
449
- return;
450
- }
451
- const configKey = getColumnVisibilityConfigKey.call(this);
452
-
453
- try {
454
- host.setConfig(configKey, map);
455
- } catch (error) {
456
- addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
457
- }
433
+ if (!this[columnBarElementSymbol]) {
434
+ return;
435
+ }
436
+
437
+ const options = this[columnBarElementSymbol].getOption("columns");
438
+ if (!isArray(options)) return;
439
+
440
+ const map = {};
441
+ for (let i = 0; i < options.length; i++) {
442
+ const option = options[i];
443
+ map[option.name] = option.visible;
444
+ }
445
+
446
+ const document = getDocument();
447
+ const host = document.querySelector("monster-host");
448
+ if (!(host && this.id)) {
449
+ return;
450
+ }
451
+ const configKey = getColumnVisibilityConfigKey.call(this);
452
+
453
+ try {
454
+ host.setConfig(configKey, map);
455
+ } catch (error) {
456
+ addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, String(error));
457
+ }
458
458
  }
459
459
 
460
460
  /**
461
461
  * @private
462
462
  */
463
463
  function initEventHandler() {
464
- const self = this;
465
-
466
- getWindow().addEventListener("resize", (event) => {
467
- updateGrid.call(self);
468
- });
469
-
470
- self[columnBarElementSymbol].attachObserver(
471
- new Observer((e) => {
472
- updateHeaderFromColumnBar.call(self);
473
- updateGrid.call(self);
474
- updateConfigColumnBar.call(self);
475
- }),
476
- );
477
-
478
- self[gridHeadersElementSymbol].addEventListener("click", function (event) {
479
- let element = null;
480
- const datasource = self[datasourceLinkedElementSymbol];
481
- if (!datasource) {
482
- return;
483
- }
484
-
485
- element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
486
- if (element) {
487
- const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
488
- const headers = self.getOption("headers");
489
-
490
- event.preventDefault();
491
-
492
- headers[index].changeDirection();
493
-
494
- setTimeout(function () {
495
- /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
496
- element.setAttribute(
497
- ATTRIBUTE_DATATABLE_SORTABLE,
498
- `${headers[index].field} ${headers[index].direction}`,
499
- );
500
-
501
- storeOrderStatement.call(self, true);
502
- }, 0);
503
- }
504
- });
464
+ const self = this;
465
+
466
+ getWindow().addEventListener("resize", (event) => {
467
+ updateGrid.call(self);
468
+ });
469
+
470
+ self[columnBarElementSymbol].attachObserver(
471
+ new Observer((e) => {
472
+ updateHeaderFromColumnBar.call(self);
473
+ updateGrid.call(self);
474
+ updateConfigColumnBar.call(self);
475
+ }),
476
+ );
477
+
478
+ self[gridHeadersElementSymbol].addEventListener("click", function (event) {
479
+ let element = null;
480
+ const datasource = self[datasourceLinkedElementSymbol];
481
+ if (!datasource) {
482
+ return;
483
+ }
484
+
485
+ element = findTargetElementFromEvent(event, ATTRIBUTE_DATATABLE_SORTABLE);
486
+ if (element) {
487
+ const index = element.parentNode.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
488
+ const headers = self.getOption("headers");
489
+
490
+ event.preventDefault();
491
+
492
+ headers[index].changeDirection();
493
+
494
+ setTimeout(function () {
495
+ /** hotfix, normally this should be done via the updater, no idea why this is not possible. */
496
+ element.setAttribute(
497
+ ATTRIBUTE_DATATABLE_SORTABLE,
498
+ `${headers[index].field} ${headers[index].direction}`,
499
+ );
500
+
501
+ storeOrderStatement.call(self, true);
502
+ }, 0);
503
+ }
504
+ });
505
505
  }
506
506
 
507
507
  /**
508
508
  * @private
509
509
  */
510
510
  function initGridAndStructs(hostConfig, headerOrderMap) {
511
- const rowID = this.getOption("templateMapping.row-key");
512
-
513
- if (!this[gridElementSymbol]) {
514
- throw new Error("no grid element is defined");
515
- }
516
-
517
- let template;
518
- getSlottedElements.call(this).forEach((e) => {
519
- if (e instanceof HTMLTemplateElement && e.id === rowID) {
520
- template = e;
521
- }
522
- });
523
-
524
- if (!template) {
525
- throw new Error("no template is defined");
526
- }
527
-
528
- const rowCount = template.content.children.length;
529
-
530
- const headers = [];
531
-
532
- for (let i = 0; i < rowCount; i++) {
533
- let hClass = "";
534
- const row = template.content.children[i];
535
-
536
- let mode = "";
537
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
538
- mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
539
- }
540
-
541
- let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
542
- if (!grid || grid === "" || grid === "auto") {
543
- grid = "minmax(0, 1fr)";
544
- }
545
-
546
- let label = "";
547
- let labelKey = "";
548
-
549
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
550
- label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
551
- labelKey = label;
552
-
553
- try {
554
- if (label.startsWith("i18n:")) {
555
- label = label.substring(5, label.length);
556
- label = getDocumentTranslations().getText(label, label);
557
- }
558
- } catch (e) {
559
- label = "i18n error " + label;
560
- }
561
- }
562
-
563
- if (!label) {
564
- label = i + 1 + "";
565
- mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
566
- labelKey = label;
567
- }
568
-
569
- if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
570
- if (hostConfig[label] === false) {
571
- mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
572
- } else {
573
- mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
574
- }
575
- }
576
-
577
- let align = "";
578
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
579
- align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
580
- }
581
-
582
- switch (align) {
583
- case "center":
584
- hClass = "flex-center";
585
- break;
586
- case "end":
587
- hClass = "flex-end";
588
- break;
589
- case "start":
590
- hClass = "flex-start";
591
- break;
592
- default:
593
- hClass = "flex-start";
594
- }
595
-
596
- let field = "";
597
- if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
598
- field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE);
599
- }
600
-
601
- if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
602
- hClass += " hidden";
603
- }
604
-
605
- const header = new Header();
606
- header.setInternals({
607
- field: field,
608
- label: label,
609
- classes: hClass,
610
- index: i,
611
- mode: mode,
612
- grid: grid,
613
- labelKey: labelKey,
614
- direction: headerOrderMap.get(field) || DIRECTION_NONE,
615
- });
616
-
617
- headers.push(header);
618
- }
619
-
620
- this.setOption("headers", headers);
621
- storeOrderStatement.call(this, this.getOption("features.autoInit"));
511
+ const rowID = this.getOption("templateMapping.row-key");
512
+
513
+ if (!this[gridElementSymbol]) {
514
+ throw new Error("no grid element is defined");
515
+ }
516
+
517
+ let template;
518
+ getSlottedElements.call(this).forEach((e) => {
519
+ if (e instanceof HTMLTemplateElement && e.id === rowID) {
520
+ template = e;
521
+ }
522
+ });
523
+
524
+ if (!template) {
525
+ throw new Error("no template is defined");
526
+ }
527
+
528
+ const rowCount = template.content.children.length;
529
+
530
+ const headers = [];
531
+
532
+ for (let i = 0; i < rowCount; i++) {
533
+ let hClass = "";
534
+ const row = template.content.children[i];
535
+
536
+ let mode = "";
537
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_MODE)) {
538
+ mode = row.getAttribute(ATTRIBUTE_DATATABLE_MODE);
539
+ }
540
+
541
+ let grid = row.getAttribute(ATTRIBUTE_DATATABLE_GRID_TEMPLATE);
542
+ if (!grid || grid === "" || grid === "auto") {
543
+ grid = "minmax(0, 1fr)";
544
+ }
545
+
546
+ let label = "";
547
+ let labelKey = "";
548
+
549
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_HEAD)) {
550
+ label = row.getAttribute(ATTRIBUTE_DATATABLE_HEAD);
551
+ labelKey = label;
552
+
553
+ try {
554
+ if (label.startsWith("i18n:")) {
555
+ label = label.substring(5, label.length);
556
+ label = getDocumentTranslations().getText(label, label);
557
+ }
558
+ } catch (e) {
559
+ label = "i18n error " + label;
560
+ }
561
+ }
562
+
563
+ if (!label) {
564
+ label = i + 1 + "";
565
+ mode = ATTRIBUTE_DATATABLE_MODE_FIXED;
566
+ labelKey = label;
567
+ }
568
+
569
+ if (isObject(hostConfig) && hostConfig.hasOwnProperty(label)) {
570
+ if (hostConfig[label] === false) {
571
+ mode = ATTRIBUTE_DATATABLE_MODE_HIDDEN;
572
+ } else {
573
+ mode = ATTRIBUTE_DATATABLE_MODE_VISIBLE;
574
+ }
575
+ }
576
+
577
+ let align = "";
578
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_ALIGN)) {
579
+ align = row.getAttribute(ATTRIBUTE_DATATABLE_ALIGN);
580
+ }
581
+
582
+ switch (align) {
583
+ case "center":
584
+ hClass = "flex-center";
585
+ break;
586
+ case "end":
587
+ hClass = "flex-end";
588
+ break;
589
+ case "start":
590
+ hClass = "flex-start";
591
+ break;
592
+ default:
593
+ hClass = "flex-start";
594
+ }
595
+
596
+ let field = "";
597
+ let direction = DIRECTION_NONE;
598
+ if (row.hasAttribute(ATTRIBUTE_DATATABLE_SORTABLE)) {
599
+ field = row.getAttribute(ATTRIBUTE_DATATABLE_SORTABLE).trim();
600
+ const parts = field.split(" ").map((item) => item.trim());
601
+ field = parts[0];
602
+
603
+ if (headerOrderMap.has(field)) {
604
+ direction = headerOrderMap.get(field);
605
+ } else if (parts.length === 2 && [DIRECTION_ASC, DIRECTION_DESC].indexOf(parts[1]) !== -1) {
606
+ direction = parts[1];
607
+ }
608
+ }
609
+
610
+ if (mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
611
+ hClass += " hidden";
612
+ }
613
+
614
+ const header = new Header();
615
+ header.setInternals({
616
+ field: field,
617
+ label: label,
618
+ classes: hClass,
619
+ index: i,
620
+ mode: mode,
621
+ grid: grid,
622
+ labelKey: labelKey,
623
+ direction: direction,
624
+ });
625
+
626
+ headers.push(header);
627
+ }
628
+
629
+ this.setOption("headers", headers);
630
+ setTimeout(() => {
631
+ storeOrderStatement.call(this, this.getOption("features.autoInit"));
632
+
633
+ },
634
+ 0);
622
635
  }
623
636
 
624
637
  /**
@@ -626,79 +639,80 @@ function initGridAndStructs(hostConfig, headerOrderMap) {
626
639
  * @returns {string}
627
640
  */
628
641
  export function getStoredOrderConfigKey() {
629
- return generateUniqueConfigKey("datatable", this?.id, "stored-order");
642
+ return generateUniqueConfigKey("datatable", this?.id, "stored-order");
630
643
  }
631
644
 
632
645
  /**
633
646
  * @private
634
647
  */
635
648
  function storeOrderStatement(doFetch) {
636
- const headers = this.getOption("headers");
637
- const statement = createOrderStatement(headers);
638
- setDataSource.call(this, { orderBy: statement }, doFetch);
639
-
640
- const document = getDocument();
641
- const host = document.querySelector("monster-host");
642
- if (!(host && this.id)) {
643
- return;
644
- }
645
-
646
- const configKey = getStoredOrderConfigKey.call(this);
647
-
648
- // statement explode with , and remove all empty
649
- const list = statement.split(",").filter((item) => item.trim() !== "");
650
- if (list.length === 0) {
651
- // host.deleteConfig(configKey);
652
- return;
653
- }
654
-
655
- host.setConfig(configKey, list);
649
+
650
+ const headers = this.getOption("headers");
651
+ const statement = createOrderStatement(headers);
652
+ setDataSource.call(this, {orderBy: statement}, doFetch);
653
+
654
+ const document = getDocument();
655
+ const host = document.querySelector("monster-host");
656
+ if (!(host && this.id)) {
657
+ return;
658
+ }
659
+
660
+ const configKey = getStoredOrderConfigKey.call(this);
661
+
662
+ // statement explode with , and remove all empty
663
+ const list = statement.split(",").filter((item) => item.trim() !== "");
664
+ if (list.length === 0) {
665
+ // host.deleteConfig(configKey);
666
+ return;
667
+ }
668
+
669
+ host.setConfig(configKey, list);
656
670
  }
657
671
 
658
672
  /**
659
673
  * @private
660
674
  */
661
675
  function updateGrid() {
662
- if (!this[gridElementSymbol]) {
663
- throw new Error("no grid element is defined");
664
- }
665
-
666
- let gridTemplateColumns = "";
667
-
668
- const headers = this.getOption("headers");
669
-
670
- let styles = "";
671
-
672
- for (let i = 0; i < headers.length; i++) {
673
- const header = headers[i];
674
-
675
- if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
676
- styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
677
- styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
678
- } else {
679
- gridTemplateColumns += `${header.grid} `;
680
- }
681
- }
682
-
683
- const sheet = new CSSStyleSheet();
684
- if (styles !== "") sheet.replaceSync(styles);
685
- this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
686
-
687
- const bodyWidth = getDocument().body.getBoundingClientRect().width;
688
-
689
- const breakpoint = this.getOption("responsive.breakpoint");
690
-
691
- if (bodyWidth > breakpoint) {
692
- this[
693
- gridElementSymbol
694
- ].style.gridTemplateColumns = `${gridTemplateColumns}`;
695
- this[
696
- gridHeadersElementSymbol
697
- ].style.gridTemplateColumns = `${gridTemplateColumns}`;
698
- } else {
699
- this[gridElementSymbol].style.gridTemplateColumns = "auto";
700
- this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
701
- }
676
+ if (!this[gridElementSymbol]) {
677
+ throw new Error("no grid element is defined");
678
+ }
679
+
680
+ let gridTemplateColumns = "";
681
+
682
+ const headers = this.getOption("headers");
683
+
684
+ let styles = "";
685
+
686
+ for (let i = 0; i < headers.length; i++) {
687
+ const header = headers[i];
688
+
689
+ if (header.mode === ATTRIBUTE_DATATABLE_MODE_HIDDEN) {
690
+ styles += `[data-monster-role=datatable]>[data-monster-head="${header.labelKey}"] { display: none; }\n`;
691
+ styles += `[data-monster-role=datatable-headers]>[data-monster-index="${header.index}"] { display: none; }\n`;
692
+ } else {
693
+ gridTemplateColumns += `${header.grid} `;
694
+ }
695
+ }
696
+
697
+ const sheet = new CSSStyleSheet();
698
+ if (styles !== "") sheet.replaceSync(styles);
699
+ this.shadowRoot.adoptedStyleSheets = [...DataTable.getCSSStyleSheet(), sheet];
700
+
701
+ const bodyWidth = getDocument().body.getBoundingClientRect().width;
702
+
703
+ const breakpoint = this.getOption("responsive.breakpoint");
704
+
705
+ if (bodyWidth > breakpoint) {
706
+ this[
707
+ gridElementSymbol
708
+ ].style.gridTemplateColumns = `${gridTemplateColumns}`;
709
+ this[
710
+ gridHeadersElementSymbol
711
+ ].style.gridTemplateColumns = `${gridTemplateColumns}`;
712
+ } else {
713
+ this[gridElementSymbol].style.gridTemplateColumns = "auto";
714
+ this[gridHeadersElementSymbol].style.gridTemplateColumns = "auto";
715
+ }
702
716
  }
703
717
 
704
718
  /**
@@ -706,20 +720,20 @@ function updateGrid() {
706
720
  * @param {Monster.Components.Datatable.Header[]} headers
707
721
  * @param {bool} doFetch
708
722
  */
709
- function setDataSource({ orderBy }, doFetch) {
710
- const datasource = this[datasourceLinkedElementSymbol];
723
+ function setDataSource({orderBy}, doFetch) {
724
+ const datasource = this[datasourceLinkedElementSymbol];
711
725
 
712
- if (!datasource) {
713
- return;
714
- }
726
+ if (!datasource) {
727
+ return;
728
+ }
715
729
 
716
- if (isFunction(datasource?.setParameters)) {
717
- datasource.setParameters({ orderBy });
718
- }
730
+ if (isFunction(datasource?.setParameters)) {
731
+ datasource.setParameters({orderBy});
732
+ }
719
733
 
720
- if (doFetch !== false && isFunction(datasource?.fetch)) {
721
- datasource.fetch();
722
- }
734
+ if (doFetch !== false && isFunction(datasource?.fetch)) {
735
+ datasource.fetch();
736
+ }
723
737
  }
724
738
 
725
739
  /**
@@ -727,20 +741,20 @@ function setDataSource({ orderBy }, doFetch) {
727
741
  * @return {Monster.Components.Datatable.Form}
728
742
  */
729
743
  function initControlReferences() {
730
- if (!this.shadowRoot) {
731
- throw new Error("no shadow-root is defined");
732
- }
733
-
734
- this[gridElementSymbol] = this.shadowRoot.querySelector(
735
- "[data-monster-role=datatable]",
736
- );
737
- this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
738
- "[data-monster-role=datatable-headers]",
739
- );
740
- this[columnBarElementSymbol] =
741
- this.shadowRoot.querySelector("monster-column-bar");
742
-
743
- return this;
744
+ if (!this.shadowRoot) {
745
+ throw new Error("no shadow-root is defined");
746
+ }
747
+
748
+ this[gridElementSymbol] = this.shadowRoot.querySelector(
749
+ "[data-monster-role=datatable]",
750
+ );
751
+ this[gridHeadersElementSymbol] = this.shadowRoot.querySelector(
752
+ "[data-monster-role=datatable-headers]",
753
+ );
754
+ this[columnBarElementSymbol] =
755
+ this.shadowRoot.querySelector("monster-column-bar");
756
+
757
+ return this;
744
758
  }
745
759
 
746
760
  /**
@@ -750,22 +764,22 @@ function initControlReferences() {
750
764
  * @throws {Error} the datasource could not be initialized
751
765
  */
752
766
  function initOptionsFromArguments() {
753
- const options = {};
754
- const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
755
-
756
- if (selector) {
757
- options.datasource = { selector: selector };
758
- }
759
-
760
- const breakpoint = this.getAttribute(
761
- ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
762
- );
763
- if (breakpoint) {
764
- options.responsive = {};
765
- options.responsive.breakpoint = parseInt(breakpoint);
766
- }
767
-
768
- return options;
767
+ const options = {};
768
+ const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
769
+
770
+ if (selector) {
771
+ options.datasource = {selector: selector};
772
+ }
773
+
774
+ const breakpoint = this.getAttribute(
775
+ ATTRIBUTE_DATATABLE_RESPONSIVE_BREAKPOINT,
776
+ );
777
+ if (breakpoint) {
778
+ options.responsive = {};
779
+ options.responsive.breakpoint = parseInt(breakpoint);
780
+ }
781
+
782
+ return options;
769
783
  }
770
784
 
771
785
  /**
@@ -773,7 +787,7 @@ function initOptionsFromArguments() {
773
787
  * @return {string}
774
788
  */
775
789
  function getEmptyTemplate() {
776
- return `<monster-state data-monster-role="empty-without-action">
790
+ return `<monster-state data-monster-role="empty-without-action">
777
791
  <div part="visual">
778
792
  <svg width="4rem" height="4rem" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
779
793
  <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"/>
@@ -791,8 +805,8 @@ function getEmptyTemplate() {
791
805
  * @return {string}
792
806
  */
793
807
  function getTemplate() {
794
- // language=HTML
795
- return `
808
+ // language=HTML
809
+ return `
796
810
  <div data-monster-role="control" part="control">
797
811
  <template id="headers-row">
798
812
  <div data-monster-attributes="class path:headers-row.classname,