@schukai/monster 3.95.1 → 3.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +1 -1
  3. package/source/components/datatable/dataset.mjs +331 -305
  4. package/source/components/datatable/datasource/dom.mjs +35 -1
  5. package/source/components/datatable/datasource/rest.mjs +99 -69
  6. package/source/components/datatable/datasource.mjs +15 -15
  7. package/source/components/datatable/embedded-pagination.mjs +11 -0
  8. package/source/components/datatable/pagination.mjs +41 -25
  9. package/source/components/datatable/status.mjs +1 -3
  10. package/source/components/datatable/style/pagination.pcss +2 -2
  11. package/source/components/datatable/stylesheet/pagination.mjs +1 -1
  12. package/source/components/datatable/util.mjs +2 -1
  13. package/source/components/form/select.mjs +1 -1
  14. package/source/components/form/toggle-switch.mjs +2 -6
  15. package/source/components/host/config-manager.mjs +1 -3
  16. package/source/components/layout/tabs.mjs +897 -895
  17. package/source/components/notify/message.mjs +10 -14
  18. package/source/components/notify/notify.mjs +9 -13
  19. package/source/components/notify/stylesheet/notify.mjs +13 -6
  20. package/source/components/state/log.mjs +184 -184
  21. package/source/components/state/stylesheet/log.mjs +13 -6
  22. package/source/data/datasource/server/restapi.mjs +22 -16
  23. package/source/data/datasource/server.mjs +1 -0
  24. package/source/data/transformer.mjs +803 -806
  25. package/source/dom/updater.mjs +767 -767
  26. package/source/i18n/time-ago.mjs +1352 -636
  27. package/source/monster.mjs +2 -0
  28. package/source/types/has.mjs +26 -0
  29. package/source/types/version.mjs +1 -1
  30. package/test/cases/components/form/form.mjs +166 -125
  31. package/test/cases/monster.mjs +1 -1
  32. package/test/web/import.js +1 -0
  33. package/test/web/test.html +2 -2
  34. package/test/web/tests.js +2080 -1433
@@ -12,290 +12,316 @@
12
12
  * SPDX-License-Identifier: AGPL-3.0
13
13
  */
14
14
 
15
- import { instanceSymbol, internalSymbol } from "../../constants.mjs";
16
- import { Pathfinder } from "../../data/pathfinder.mjs";
17
- import { getLinkedObjects, hasObjectLink } from "../../dom/attributes.mjs";
18
- import { customElementUpdaterLinkSymbol } from "../../dom/constants.mjs";
15
+ import {instanceSymbol} from "../../constants.mjs";
16
+ import {Pathfinder} from "../../data/pathfinder.mjs";
19
17
  import {
20
- assembleMethodSymbol,
21
- CustomElement,
22
- attributeObserverSymbol,
23
- registerCustomElement,
18
+ assembleMethodSymbol,
19
+ CustomElement,
20
+ attributeObserverSymbol,
21
+ registerCustomElement,
24
22
  } from "../../dom/customelement.mjs";
25
- import { findElementWithSelectorUpwards } from "../../dom/util.mjs";
26
- import { isString } from "../../types/is.mjs";
27
- import { Observer } from "../../types/observer.mjs";
28
- import { clone } from "../../util/clone.mjs";
23
+ import {findElementWithSelectorUpwards} from "../../dom/util.mjs";
24
+ import {isString} from "../../types/is.mjs";
25
+ import {Observer} from "../../types/observer.mjs";
29
26
  import {
30
- ATTRIBUTE_DATASOURCE_SELECTOR,
31
- ATTRIBUTE_DATATABLE_INDEX,
27
+ ATTRIBUTE_DATASOURCE_SELECTOR,
28
+ ATTRIBUTE_DATATABLE_INDEX,
32
29
  } from "./constants.mjs";
33
- import { Datasource } from "./datasource.mjs";
34
- import { DatasetStyleSheet } from "./stylesheet/dataset.mjs";
30
+ import {Datasource} from "./datasource.mjs";
31
+ import {DatasetStyleSheet} from "./stylesheet/dataset.mjs";
35
32
  import {
36
- handleDataSourceChanges,
37
- datasourceLinkedElementSymbol,
33
+ handleDataSourceChanges,
34
+ datasourceLinkedElementSymbol,
38
35
  } from "./util.mjs";
39
- import { FormStyleSheet } from "../stylesheet/form.mjs";
36
+ import {FormStyleSheet} from "../stylesheet/form.mjs";
40
37
 
41
- export { DataSet };
38
+ export {DataSet};
42
39
 
43
40
  /**
44
- * The data set component is used to show the data of a data source.
41
+ * A data set component
45
42
  *
46
- * <img src="./images/dataset.png">
43
+ * @fragments /fragments/components/datatable/dataset
47
44
  *
48
- * You can create this control either by specifying the HTML tag <monster-dataset />` directly in the HTML or using
49
- * Javascript via the `document.createElement('monster-dataset');` method.
45
+ * @example /examples/components/datatable/dataset-dom
46
+ * @example /examples/components/datatable/dataset-rest
50
47
  *
51
- * ```html
52
- * <monster-dataset></monster-dataset>
53
- * ```
54
- *
55
- * Or you can create this CustomControl directly in Javascript:
56
- *
57
- * ```js
58
- * import '@schukai/component-datatable/source/dataset.mjs';
59
- * document.createElement('monster-dataset');
60
- * ```
61
- *
62
- * The Body should have a class "hidden" to ensure that the styles are applied correctly.
63
- *
64
- * ```css
65
- * body.hidden {
66
- * visibility: hidden;
67
- * }
68
- * ```
69
- *
70
- * @startuml dataset.png
71
- * skinparam monochrome true
72
- * skinparam shadowing false
73
- * HTMLElement <|-- CustomElement
74
- * CustomElement <|-- DataSet
75
- * @enduml
48
+ * @issue https://localhost.alvine.dev:8443/development/issues/closed/272.html
76
49
  *
77
50
  * @copyright schukai GmbH
78
- * @summary A data set
51
+ * @summary A dataset component that can be used to show the data of a data source
79
52
  */
80
53
  class DataSet extends CustomElement {
81
- /**
82
- * This method is called by the `instanceof` operator.
83
- * @return {symbol}
84
- */
85
- static get [instanceSymbol]() {
86
- return Symbol.for("@schukai/monster/components/dataset@@instance");
87
- }
88
-
89
- /**
90
- * This method determines which attributes are to be monitored by `attributeChangedCallback()`.
91
- *
92
- * @return {string[]}
93
- * @since 1.15.0
94
- */
95
- static get observedAttributes() {
96
- const attributes = super.observedAttributes;
97
- attributes.push(ATTRIBUTE_DATATABLE_INDEX);
98
- return attributes;
99
- }
100
-
101
- /**
102
- * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
103
- * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
104
- *
105
- * The individual configuration values can be found in the table.
106
- *
107
- * @property {Object} templates Template definitions
108
- * @property {string} templates.main Main template
109
- * @property {object} datasource The datasource
110
- * @property {string} datasource.selector The selector of the datasource
111
- * @property {object} mapping The mapping
112
- * @property {string} mapping.data The data
113
- * @property {number} mapping.index The index
114
- * @property {Array} data The data
115
- */
116
- get defaults() {
117
- const obj = Object.assign({}, super.defaults, {
118
- templates: {
119
- main: getTemplate(),
120
- },
121
-
122
- datasource: {
123
- selector: null,
124
- },
125
-
126
- mapping: {
127
- data: "dataset",
128
- index: 0,
129
- },
130
-
131
- features: {
132
- /**
133
- * @since 3.70.0
134
- * @type {boolean}
135
- */
136
- refreshOnMutation: true,
137
- },
138
-
139
- /**
140
- * @since 3.70.0
141
- * @type {boolean}
142
- */
143
- refreshOnMutation: {
144
- selector: "input, select, textarea",
145
- },
146
-
147
- data: {},
148
- });
149
-
150
- updateOptionsFromArguments.call(this, obj);
151
- return obj;
152
- }
153
-
154
- /**
155
- *
156
- * @return {string}
157
- */
158
- static getTag() {
159
- return "monster-dataset";
160
- }
161
-
162
- /**
163
- * This method is called when the component is created.
164
- * @since 3.70.0
165
- * @return {DataSet}
166
- */
167
- refresh() {
168
- // makes sure that handleDataSourceChanges is called
169
- this.setOption("data", {});
170
- return this;
171
- }
172
-
173
- /**
174
- *
175
- * @return {Promise<unknown>}
176
- */
177
- write() {
178
- return new Promise((resolve, reject) => {
179
- if (!this[datasourceLinkedElementSymbol]) {
180
- reject(new Error("No datasource"));
181
- return;
182
- }
183
-
184
- const internalUpdateCloneData = this.getInternalUpdateCloneData();
185
- if (!internalUpdateCloneData) {
186
- reject(new Error("No update data"));
187
- return;
188
- }
189
-
190
- const internalData = internalUpdateCloneData?.["data"];
191
- if (
192
- internalData === undefined ||
193
- internalData === null ||
194
- internalData === ""
195
- ) {
196
- reject(new Error("No data"));
197
- return;
198
- }
199
-
200
- queueMicrotask(() => {
201
- const path = this.getOption("mapping.data");
202
- const index = this.getOption("mapping.index");
203
-
204
- let pathWithIndex;
205
-
206
- if (isString(path) && path !== "") {
207
- pathWithIndex = path + "." + index;
208
- } else {
209
- pathWithIndex = String(index);
210
- }
211
-
212
- const data = this[datasourceLinkedElementSymbol]?.data;
213
- if (!data) {
214
- reject(new Error("No data"));
215
- return;
216
- }
217
-
218
- const unref = JSON.stringify(data);
219
- const ref = JSON.parse(unref);
220
-
221
- new Pathfinder(ref).setVia(pathWithIndex, internalData);
222
-
223
- this[datasourceLinkedElementSymbol].data = ref;
224
-
225
- resolve();
226
- });
227
- });
228
- }
229
-
230
- /**
231
- * This method is responsible for assembling the component.
232
- *
233
- * It calls the parent's assemble method first, then initializes control references and event handlers.
234
- * If the `datasource.selector` option is provided and is a string, it searches for the corresponding
235
- * element in the DOM using that selector.
236
- *
237
- * If the selector matches exactly one element, it checks if the element is an instance of the `Datasource` class.
238
- *
239
- * If it is, the component's `datasourceLinkedElementSymbol` property is set to the element, and the component
240
- * attaches an observer to the datasource's changes.
241
- *
242
- * The observer is a function that calls the `handleDataSourceChanges` method in the context of the component.
243
- * Additionally, the component attaches an observer to itself, which also calls the `handleDataSourceChanges`
244
- * method in the component's context.
245
- */
246
- [assembleMethodSymbol]() {
247
- super[assembleMethodSymbol]();
248
-
249
- initEventHandler.call(this);
250
-
251
- if (!this[datasourceLinkedElementSymbol]) {
252
- const selector = this.getOption("datasource.selector");
253
-
254
- if (isString(selector)) {
255
- const element = findElementWithSelectorUpwards(this, selector);
256
- if (element === null) {
257
- throw new Error("the selector must match exactly one element");
258
- }
259
-
260
- if (!(element instanceof Datasource)) {
261
- throw new TypeError("the element must be a datasource");
262
- }
263
-
264
- this[datasourceLinkedElementSymbol] = element;
265
- element.datasource.attachObserver(
266
- new Observer(handleDataSourceChanges.bind(this)),
267
- );
268
- } else {
269
- throw new Error("the selector must be a string");
270
- }
271
- }
272
-
273
- if (
274
- this.getOption("features.refreshOnMutation") &&
275
- this.getOption("refreshOnMutation.selector")
276
- ) {
277
- initMutationObserver.call(this);
278
- }
279
- }
280
-
281
- /**
282
- * @return [CSSStyleSheet]
283
- */
284
- static getCSSStyleSheet() {
285
- return [FormStyleSheet, DatasetStyleSheet];
286
- }
54
+ /**
55
+ * This method is called by the `instanceof` operator.
56
+ * @return {symbol}
57
+ */
58
+ static get [instanceSymbol]() {
59
+ return Symbol.for("@schukai/monster/components/dataset@@instance");
60
+ }
61
+
62
+ /**
63
+ * This method determines which attributes are to be monitored by `attributeChangedCallback()`.
64
+ *
65
+ * @return {string[]}
66
+ * @since 1.15.0
67
+ */
68
+ static get observedAttributes() {
69
+ const attributes = super.observedAttributes;
70
+ attributes.push(ATTRIBUTE_DATATABLE_INDEX);
71
+ attributes.push("data-monster-option-mapping-index");
72
+ return attributes;
73
+ }
74
+
75
+ /**
76
+ * To set the options via the HTML tag, the attribute `data-monster-options` must be used.
77
+ * @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
78
+ *
79
+ * The individual configuration values can be found in the table.
80
+ *
81
+ * @property {Object} templates Template definitions
82
+ * @property {string} templates.main Main template
83
+ * @property {object} datasource The datasource
84
+ * @property {string} datasource.selector The selector of the datasource
85
+ * @property {object} mapping The mapping
86
+ * @property {string} mapping.data The data
87
+ * @property {number} mapping.index The index
88
+ * @property {object} features The features
89
+ * @property {boolean} features.refreshOnMutation Refresh on mutation
90
+ * @property {object} refreshOnMutation The refresh on mutation
91
+ * @property {string} refreshOnMutation.selector The selector
92
+ */
93
+ get defaults() {
94
+ const obj = Object.assign({}, super.defaults, {
95
+ templates: {
96
+ main: getTemplate(),
97
+ },
98
+
99
+ datasource: {
100
+ selector: null,
101
+ },
102
+
103
+ mapping: {
104
+ data: "dataset",
105
+ index: 0,
106
+ },
107
+
108
+ features: {
109
+ /**
110
+ * @since 3.70.0
111
+ * @type {boolean}
112
+ */
113
+ refreshOnMutation: true,
114
+ },
115
+
116
+ /**
117
+ * @since 3.70.0
118
+ * @type {boolean}
119
+ */
120
+ refreshOnMutation: {
121
+ selector: "input, select, textarea",
122
+ },
123
+
124
+ data: {},
125
+ });
126
+
127
+ updateOptionsFromArguments.call(this, obj);
128
+ return obj;
129
+ }
130
+
131
+ /**
132
+ *
133
+ * @return {string}
134
+ */
135
+ static getTag() {
136
+ return "monster-dataset";
137
+ }
138
+
139
+ /**
140
+ * This method is called when the component is created.
141
+ * @since 3.70.0
142
+ * @return {DataSet}
143
+ */
144
+ refresh() {
145
+ // makes sure that handleDataSourceChanges is called
146
+ this.setOption("data", {});
147
+ return this;
148
+ }
149
+
150
+ /**
151
+ *
152
+ * @return {Promise<unknown>}
153
+ */
154
+ write() {
155
+ return new Promise((resolve, reject) => {
156
+ if (!this[datasourceLinkedElementSymbol]) {
157
+ reject(new Error("No datasource"));
158
+ return;
159
+ }
160
+
161
+ const internalUpdateCloneData = this.getInternalUpdateCloneData();
162
+ if (!internalUpdateCloneData) {
163
+ reject(new Error("No update data"));
164
+ return;
165
+ }
166
+
167
+ const internalData = internalUpdateCloneData?.["data"];
168
+ if (
169
+ internalData === undefined ||
170
+ internalData === null ||
171
+ internalData === ""
172
+ ) {
173
+ reject(new Error("No data"));
174
+ return;
175
+ }
176
+
177
+ queueMicrotask(() => {
178
+ const path = this.getOption("mapping.data");
179
+ const index = this.getOption("mapping.index");
180
+
181
+ let pathWithIndex;
182
+
183
+ if (isString(path) && path !== "") {
184
+ pathWithIndex = path + "." + index;
185
+ } else {
186
+ pathWithIndex = String(index);
187
+ }
188
+
189
+ const data = this[datasourceLinkedElementSymbol]?.data;
190
+ if (!data) {
191
+ reject(new Error("No data"));
192
+ return;
193
+ }
194
+
195
+ const unref = JSON.stringify(data);
196
+ const ref = JSON.parse(unref);
197
+
198
+ new Pathfinder(ref).setVia(pathWithIndex, internalData);
199
+
200
+ this[datasourceLinkedElementSymbol].data = ref;
201
+
202
+ resolve();
203
+ });
204
+ });
205
+ }
206
+
207
+ /**
208
+ * This method is responsible for assembling the component.
209
+ *
210
+ * It calls the parent's assemble method first, then initializes control references and event handlers.
211
+ * If the `datasource.selector` option is provided and is a string, it searches for the corresponding
212
+ * element in the DOM using that selector.
213
+ *
214
+ * If the selector matches exactly one element, it checks if the element is an instance of the `Datasource` class.
215
+ *
216
+ * If it is, the component's `datasourceLinkedElementSymbol` property is set to the element, and the component
217
+ * attaches an observer to the datasource's changes.
218
+ *
219
+ * The observer is a function that calls the `handleDataSourceChanges` method in the context of the component.
220
+ * Additionally, the component attaches an observer to itself, which also calls the `handleDataSourceChanges`
221
+ * method in the component's context.
222
+ */
223
+ [assembleMethodSymbol]() {
224
+ super[assembleMethodSymbol]();
225
+
226
+ requestAnimationFrame(() => {
227
+ if (!this[datasourceLinkedElementSymbol]) {
228
+ const selector = this.getOption("datasource.selector");
229
+
230
+ if (isString(selector)) {
231
+ const element = findElementWithSelectorUpwards(this, selector);
232
+ if (element === null) {
233
+ throw new Error("the selector must match exactly one element");
234
+ }
235
+
236
+ if (!(element instanceof Datasource)) {
237
+ throw new TypeError("the element must be a datasource");
238
+ }
239
+
240
+ this[datasourceLinkedElementSymbol] = element;
241
+ element.datasource.attachObserver(
242
+ new Observer(handleDataSourceChanges.bind(this)),
243
+ );
244
+
245
+ handleDataSourceChanges.call(this);
246
+ } else {
247
+ throw new Error("the selector must be a string");
248
+ }
249
+ }
250
+
251
+ if (
252
+ this.getOption("features.refreshOnMutation") &&
253
+ this.getOption("refreshOnMutation.selector")
254
+ ) {
255
+ initMutationObserver.call(this);
256
+ }
257
+
258
+ initEventHandler.call(this);
259
+ });
260
+ }
261
+
262
+ /**
263
+ * @return [CSSStyleSheet]
264
+ */
265
+ static getCSSStyleSheet() {
266
+ return [FormStyleSheet, DatasetStyleSheet];
267
+ }
287
268
  }
288
269
 
289
270
  /**
290
271
  * @private
291
272
  */
292
273
  function initEventHandler() {
293
- this[attributeObserverSymbol][ATTRIBUTE_DATATABLE_INDEX] = () => {
294
- const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
295
- if (index) {
296
- this.setOption("mapping.index", parseInt(index, 10));
297
- }
298
- };
274
+ this[attributeObserverSymbol][ATTRIBUTE_DATATABLE_INDEX] = () => {
275
+ // @deprecated use data-monster-option-mapping-index
276
+ const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
277
+ if (index) {
278
+ this.setOption("mapping.index", parseInt(index, 10));
279
+ handleDataSourceChanges.call(this);
280
+ }
281
+ };
282
+
283
+ this[attributeObserverSymbol]["data-monster-option-mapping-index"] = () => {
284
+ const index = this.getAttribute("data-monster-option-mapping-index");
285
+ if (index !== null && index !== undefined && index !== "") {
286
+ this.setOption("mapping.index", parseInt(index, 10));
287
+ handleDataSourceChanges.call(this);
288
+ }
289
+ };
290
+
291
+ if (this[datasourceLinkedElementSymbol]) {
292
+ this[datasourceLinkedElementSymbol].datasource.attachObserver(
293
+ new Observer(() => {
294
+
295
+ let index = 0;
296
+ if (typeof this[datasourceLinkedElementSymbol]?.currentPage === "function") {
297
+ const page = this[datasourceLinkedElementSymbol].currentPage();
298
+ if (page !== null && page !== undefined && page !== "") {
299
+ index = parseInt(page, 10) - 1;
300
+ }
301
+ }
302
+
303
+ this.setOption("mapping.index", index);
304
+ handleDataSourceChanges.call(this);
305
+ }),
306
+ );
307
+
308
+ this[datasourceLinkedElementSymbol].attachObserver(
309
+ new Observer(() => {
310
+ let index = 0;
311
+ if (typeof this[datasourceLinkedElementSymbol]?.currentPage === "function") {
312
+ const page = this[datasourceLinkedElementSymbol].currentPage();
313
+ if (page !== null && page !== undefined && page !== "") {
314
+ index = parseInt(page, 10) - 1;
315
+ }
316
+ }
317
+
318
+ this.setOption("mapping.index", index);
319
+ handleDataSourceChanges.call(this);
320
+ }),
321
+ );
322
+
323
+ handleDataSourceChanges.call(this);
324
+ }
299
325
  }
300
326
 
301
327
  /**
@@ -303,56 +329,56 @@ function initEventHandler() {
303
329
  * @param {Object} options
304
330
  */
305
331
  function updateOptionsFromArguments(options) {
306
- const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX);
332
+ const index = this.getAttribute(ATTRIBUTE_DATATABLE_INDEX); // @deprecated use data-monster-option-mapping-index
307
333
 
308
- if (index !== null && index !== undefined) {
309
- options.mapping.index = parseInt(index, 10);
310
- }
334
+ if (index !== null && index !== undefined) {
335
+ options.mapping.index = parseInt(index, 10);
336
+ }
311
337
 
312
- const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
338
+ const selector = this.getAttribute(ATTRIBUTE_DATASOURCE_SELECTOR);
313
339
 
314
- if (selector) {
315
- options.datasource.selector = selector;
316
- }
340
+ if (selector) {
341
+ options.datasource.selector = selector;
342
+ }
317
343
  }
318
344
 
319
345
  /**
320
346
  * @private
321
347
  */
322
348
  function initMutationObserver() {
323
- const config = { attributes: false, childList: true, subtree: true };
324
-
325
- const callback = (mutationList, observer) => {
326
- if (mutationList.length === 0) {
327
- return;
328
- }
329
-
330
- let doneFlag = false;
331
- for (const mutation of mutationList) {
332
- if (mutation.type === "childList") {
333
- for (const node of mutation.addedNodes) {
334
- if (
335
- node instanceof HTMLElement &&
336
- node.matches(this.getOption("refreshOnMutation.selector"))
337
- ) {
338
- doneFlag = true;
339
- break;
340
- }
341
- }
342
-
343
- if (doneFlag) {
344
- break;
345
- }
346
- }
347
- }
348
-
349
- if (doneFlag) {
350
- this.refresh();
351
- }
352
- };
353
-
354
- const observer = new MutationObserver(callback);
355
- observer.observe(this, config);
349
+ const config = {attributes: false, childList: true, subtree: true};
350
+
351
+ const callback = (mutationList, observer) => {
352
+ if (mutationList.length === 0) {
353
+ return;
354
+ }
355
+
356
+ let doneFlag = false;
357
+ for (const mutation of mutationList) {
358
+ if (mutation.type === "childList") {
359
+ for (const node of mutation.addedNodes) {
360
+ if (
361
+ node instanceof HTMLElement &&
362
+ node.matches(this.getOption("refreshOnMutation.selector"))
363
+ ) {
364
+ doneFlag = true;
365
+ break;
366
+ }
367
+ }
368
+
369
+ if (doneFlag) {
370
+ break;
371
+ }
372
+ }
373
+ }
374
+
375
+ if (doneFlag) {
376
+ this.refresh();
377
+ }
378
+ };
379
+
380
+ const observer = new MutationObserver(callback);
381
+ observer.observe(this, config);
356
382
  }
357
383
 
358
384
  /**
@@ -360,8 +386,8 @@ function initMutationObserver() {
360
386
  * @return {string}
361
387
  */
362
388
  function getTemplate() {
363
- // language=HTML
364
- return `
389
+ // language=HTML
390
+ return `
365
391
  <div data-monster-role="control" part="control">
366
392
  <slot></slot>
367
393
  </div>