@schukai/monster 4.37.2 → 4.38.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.
- package/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/source/components/content/viewer/html.mjs +131 -131
- package/source/components/content/viewer/message.mjs +739 -740
- package/source/components/content/viewer/stylesheet/html.mjs +13 -6
- package/source/components/content/viewer/stylesheet/message.mjs +13 -6
- package/source/components/data/kpi-tile.mjs +111 -0
- package/source/components/data/style/kpi-tile.pcss +161 -0
- package/source/components/data/stylesheet/kpi-tile.mjs +38 -0
- package/source/components/form/select.mjs +2850 -2850
- package/source/components/layout/tabs.mjs +1024 -1024
- package/source/components/layout/utils/attach-tabs-hash-sync.mjs +158 -152
- package/source/components/notify/stylesheet/message.mjs +13 -6
- package/source/components/state/log.mjs +1 -1
- package/source/dom/customelement.mjs +4 -2
- package/source/dom/error.mjs +47 -47
- package/source/types/version.mjs +1 -1
- package/test/cases/monster.mjs +1 -1
- package/test/web/test.html +2 -2
- package/test/web/tests.js +13 -6
@@ -17,24 +17,24 @@ import { createPopper } from "@popperjs/core";
|
|
17
17
|
import { extend } from "../../data/extend.mjs";
|
18
18
|
import { Pathfinder } from "../../data/pathfinder.mjs";
|
19
19
|
import {
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
addAttributeToken,
|
21
|
+
addToObjectLink,
|
22
|
+
hasObjectLink,
|
23
23
|
} from "../../dom/attributes.mjs";
|
24
24
|
import {
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
ATTRIBUTE_ERRORMESSAGE,
|
26
|
+
ATTRIBUTE_PREFIX,
|
27
|
+
ATTRIBUTE_ROLE,
|
28
28
|
} from "../../dom/constants.mjs";
|
29
29
|
import {
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
assembleMethodSymbol,
|
31
|
+
CustomElement,
|
32
|
+
getSlottedElements,
|
33
|
+
registerCustomElement,
|
34
34
|
} from "../../dom/customelement.mjs";
|
35
35
|
import {
|
36
|
-
|
37
|
-
|
36
|
+
findTargetElementFromEvent,
|
37
|
+
fireCustomEvent,
|
38
38
|
} from "../../dom/events.mjs";
|
39
39
|
import { getDocument, getWindow } from "../../dom/util.mjs";
|
40
40
|
import { random } from "../../math/random.mjs";
|
@@ -46,18 +46,18 @@ import { clone } from "../../util/clone.mjs";
|
|
46
46
|
import { DeadMansSwitch } from "../../util/deadmansswitch.mjs";
|
47
47
|
import { Processing } from "../../util/processing.mjs";
|
48
48
|
import {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
ATTRIBUTE_BUTTON_LABEL,
|
50
|
+
ATTRIBUTE_FORM_RELOAD,
|
51
|
+
ATTRIBUTE_FORM_URL,
|
52
|
+
STYLE_DISPLAY_MODE_BLOCK,
|
53
53
|
} from "../form/constants.mjs";
|
54
54
|
|
55
55
|
import { TabsStyleSheet } from "./stylesheet/tabs.mjs";
|
56
56
|
import { loadAndAssignContent } from "../form/util/fetch.mjs";
|
57
57
|
import { ThemeStyleSheet } from "../stylesheet/theme.mjs";
|
58
58
|
import {
|
59
|
-
|
60
|
-
|
59
|
+
popperInstanceSymbol,
|
60
|
+
setEventListenersModifiers,
|
61
61
|
} from "../form/util/popper.mjs";
|
62
62
|
import { getLocaleOfDocument } from "../../dom/locale.mjs";
|
63
63
|
|
@@ -161,319 +161,319 @@ const resizeObserverSymbol = Symbol("resizeObserver");
|
|
161
161
|
* @summary This CustomControl creates a tab element with a variety of options.
|
162
162
|
*/
|
163
163
|
class Tabs extends CustomElement {
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
164
|
+
/**
|
165
|
+
* This method is called by the `instanceof` operator.
|
166
|
+
* @return {symbol}
|
167
|
+
*/
|
168
|
+
static get [instanceSymbol]() {
|
169
|
+
return Symbol.for("@schukai/monster/components/layout/tabs");
|
170
|
+
}
|
171
|
+
|
172
|
+
/**
|
173
|
+
* To set the options via the HTML tag, the attribute `data-monster-options` must be used.
|
174
|
+
* @see {@link https://monsterjs.org/en/doc/#configurate-a-monster-control}
|
175
|
+
*
|
176
|
+
* The individual configuration values can be found in the table.
|
177
|
+
*
|
178
|
+
* @property {Object} templates Template definitions
|
179
|
+
* @property {string} templates.main Main template
|
180
|
+
* @property {Object} labels
|
181
|
+
* @property {string} labels.new-tab-label="New Tab"
|
182
|
+
* @property {Object} features
|
183
|
+
* @property {number} features.openDelay=500 Open delay in milliseconds
|
184
|
+
* @property {string} features.removeBehavior="auto" Remove behavior, auto (default), next, previous and none
|
185
|
+
* @property {boolean} features.openFirst=true Open the first tab
|
186
|
+
* @property {Object} fetch Fetch [see Using Fetch mozilla.org](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)
|
187
|
+
* @property {String} fetch.redirect=error
|
188
|
+
* @property {String} fetch.method=GET
|
189
|
+
* @property {String} fetch.mode=same-origin
|
190
|
+
* @property {String} fetch.credentials=same-origin
|
191
|
+
* @property {Object} fetch.headers={"accept":"text/html"}}
|
192
|
+
* @property {Object} popper [PopperJS Options](https://popper.js.org/docs/v2/)
|
193
|
+
* @property {string} popper.placement=bottom PopperJS placement
|
194
|
+
* @property {Object[]} modifiers={name:offset} PopperJS placement
|
195
|
+
*/
|
196
|
+
get defaults() {
|
197
|
+
return Object.assign({}, super.defaults, {
|
198
|
+
templates: {
|
199
|
+
main: getTemplate(),
|
200
|
+
},
|
201
|
+
labels: getTranslations(),
|
202
|
+
buttons: {
|
203
|
+
standard: [],
|
204
|
+
popper: [],
|
205
|
+
},
|
206
|
+
fetch: {
|
207
|
+
redirect: "error",
|
208
|
+
method: "GET",
|
209
|
+
mode: "same-origin",
|
210
|
+
credentials: "same-origin",
|
211
|
+
headers: {
|
212
|
+
accept: "text/html",
|
213
|
+
},
|
214
|
+
},
|
215
|
+
|
216
|
+
features: {
|
217
|
+
openDelay: null,
|
218
|
+
removeBehavior: "auto",
|
219
|
+
openFirst: true,
|
220
|
+
},
|
221
|
+
|
222
|
+
classes: {
|
223
|
+
button: "monster-theme-primary-1",
|
224
|
+
popper: "monster-theme-primary-1",
|
225
|
+
navigation: "monster-theme-primary-1",
|
226
|
+
},
|
227
|
+
|
228
|
+
popper: {
|
229
|
+
placement: "bottom",
|
230
|
+
modifiers: [
|
231
|
+
{
|
232
|
+
name: "offset",
|
233
|
+
options: {
|
234
|
+
offset: [0, 2],
|
235
|
+
},
|
236
|
+
},
|
237
|
+
|
238
|
+
{
|
239
|
+
name: "eventListeners",
|
240
|
+
enabled: false,
|
241
|
+
},
|
242
|
+
],
|
243
|
+
},
|
244
|
+
});
|
245
|
+
}
|
246
|
+
|
247
|
+
/**
|
248
|
+
* This method is called internal and should not be called directly.
|
249
|
+
*/
|
250
|
+
[assembleMethodSymbol]() {
|
251
|
+
super[assembleMethodSymbol]();
|
252
|
+
|
253
|
+
initControlReferences.call(this);
|
254
|
+
|
255
|
+
this[dimensionsSymbol] = new Pathfinder({ data: {} });
|
256
|
+
|
257
|
+
initEventHandler.call(this);
|
258
|
+
|
259
|
+
// setup structure
|
260
|
+
initTabButtons.call(this).then(() => {
|
261
|
+
initPopperSwitch.call(this);
|
262
|
+
initPopper.call(this);
|
263
|
+
attachResizeObserver.call(this);
|
264
|
+
attachTabChangeObserver.call(this);
|
265
|
+
});
|
266
|
+
}
|
267
|
+
|
268
|
+
/**
|
269
|
+
* This method is called internal and should not be called directly.
|
270
|
+
*
|
271
|
+
* @return {CSSStyleSheet[]}
|
272
|
+
*/
|
273
|
+
static getCSSStyleSheet() {
|
274
|
+
return [TabsStyleSheet];
|
275
|
+
}
|
276
|
+
|
277
|
+
/**
|
278
|
+
* This method is called internal and should not be called directly.
|
279
|
+
*
|
280
|
+
* @return {string}
|
281
|
+
*/
|
282
|
+
static getTag() {
|
283
|
+
return "monster-tabs";
|
284
|
+
}
|
285
|
+
|
286
|
+
/**
|
287
|
+
* This method is called internal and should not be called directly.
|
288
|
+
* @param tabId
|
289
|
+
* @returns {Tabs}
|
290
|
+
*/
|
291
|
+
removeTab(tabId) {
|
292
|
+
const tabs = this.getTabs();
|
293
|
+
for (const tab of tabs) {
|
294
|
+
if (
|
295
|
+
(tab.getAttribute("id") === tabId ||
|
296
|
+
tab.getAttribute("data-monster-name") === tabId) &&
|
297
|
+
tab.hasAttribute("data-monster-removable")
|
298
|
+
) {
|
299
|
+
tab.remove();
|
300
|
+
initTabButtons.call(this);
|
301
|
+
return this;
|
302
|
+
}
|
303
|
+
}
|
304
|
+
return this;
|
305
|
+
}
|
306
|
+
|
307
|
+
/**
|
308
|
+
* This method is called internal and should not be called directly.
|
309
|
+
* @returns {[]}
|
310
|
+
*/
|
311
|
+
getTabs() {
|
312
|
+
const nodes = getSlottedElements.call(this);
|
313
|
+
const tabs = [];
|
314
|
+
for (const node of nodes) {
|
315
|
+
if (node instanceof HTMLElement) {
|
316
|
+
tabs.push(node);
|
317
|
+
}
|
318
|
+
}
|
319
|
+
return tabs;
|
320
|
+
}
|
321
|
+
|
322
|
+
/**
|
323
|
+
* This method is called internal and should not be called directly.
|
324
|
+
* @param content
|
325
|
+
* @param active
|
326
|
+
* @param label
|
327
|
+
* @param tabId
|
328
|
+
* @param removable
|
329
|
+
* @returns {Tabs}
|
330
|
+
*/
|
331
|
+
addTab(
|
332
|
+
content,
|
333
|
+
{ active = false, label = null, tabId = null, removable = true } = {},
|
334
|
+
) {
|
335
|
+
const tab = document.createElement("div");
|
336
|
+
if (!isString(label) || label.trim() === "") {
|
337
|
+
label = this.getOption("labels.new-tab-label");
|
338
|
+
}
|
339
|
+
tab.setAttribute("data-monster-button-label", label);
|
340
|
+
|
341
|
+
if (!isString(tabId) || tabId.trim() === "") {
|
342
|
+
let thisID = this.getAttribute("id");
|
343
|
+
if (!thisID) {
|
344
|
+
thisID = new ID("tab").toString();
|
345
|
+
}
|
346
|
+
|
347
|
+
tabId = new ID(thisID).toString();
|
348
|
+
}
|
349
|
+
|
350
|
+
// check if id is already used
|
351
|
+
const existingTabs = this.getTabs();
|
352
|
+
for (const existingTab of existingTabs) {
|
353
|
+
if (
|
354
|
+
existingTab.getAttribute("id") === tabId ||
|
355
|
+
existingTab.getAttribute("data-monster-name") === tabId
|
356
|
+
) {
|
357
|
+
throw new Error(`Tab with id "${tabId}" already exists.`);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
tab.setAttribute("id", tabId);
|
362
|
+
|
363
|
+
if (active === true) {
|
364
|
+
tab.classList.add("active");
|
365
|
+
}
|
366
|
+
|
367
|
+
tab.setAttribute(ATTRIBUTE_ROLE, "tab");
|
368
|
+
|
369
|
+
if (removable === true) {
|
370
|
+
tab.setAttribute("data-monster-removable", "true");
|
371
|
+
}
|
372
|
+
|
373
|
+
if (content instanceof HTMLElement) {
|
374
|
+
tab.appendChild(content);
|
375
|
+
} else if (isString(content)) {
|
376
|
+
tab.innerHTML = content;
|
377
|
+
}
|
378
|
+
|
379
|
+
this.appendChild(tab);
|
380
|
+
return this;
|
381
|
+
}
|
382
|
+
|
383
|
+
/**
|
384
|
+
* A function that activates a tab based on the provided name.
|
385
|
+
*
|
386
|
+
* The tabs have to be named with the `data-monster-name` attribute.
|
387
|
+
*
|
388
|
+
* @param {type} idOrName - the name or id of the tab to activate
|
389
|
+
* @return {Tabs} - The current instance
|
390
|
+
*/
|
391
|
+
activeTab(idOrName) {
|
392
|
+
let found = false;
|
393
|
+
|
394
|
+
getSlottedElements.call(this).forEach((node) => {
|
395
|
+
if (found === true) {
|
396
|
+
return;
|
397
|
+
}
|
398
|
+
|
399
|
+
if (node.getAttribute("data-monster-name") === idOrName) {
|
400
|
+
const tabControl = this.shadowRoot.querySelector(
|
401
|
+
`[data-monster-tab-reference="${node.getAttribute("id")}"]`,
|
402
|
+
);
|
403
|
+
|
404
|
+
if (tabControl) {
|
405
|
+
tabControl.click();
|
406
|
+
found = true;
|
407
|
+
}
|
408
|
+
}
|
409
|
+
|
410
|
+
if (node.getAttribute("id") === idOrName) {
|
411
|
+
const tabControl = this.shadowRoot.querySelector(
|
412
|
+
`[data-monster-tab-reference="${node.getAttribute("id")}"]`,
|
413
|
+
);
|
414
|
+
|
415
|
+
if (tabControl) {
|
416
|
+
tabControl.click();
|
417
|
+
found = true;
|
418
|
+
}
|
419
|
+
}
|
420
|
+
});
|
421
|
+
|
422
|
+
return this;
|
423
|
+
}
|
424
|
+
|
425
|
+
/**
|
426
|
+
* A function that returns the name or id of the currently active tab.
|
427
|
+
*
|
428
|
+
* The tabs have to be named with the `data-monster-name` attribute.
|
429
|
+
*
|
430
|
+
* @return {string|null}
|
431
|
+
*/
|
432
|
+
getActiveTab() {
|
433
|
+
const nodes = getSlottedElements.call(this);
|
434
|
+
for (const node of nodes) {
|
435
|
+
if (node.matches(".active") === true) {
|
436
|
+
if (node.hasAttribute("data-monster-name")) {
|
437
|
+
return node.getAttribute("data-monster-name");
|
438
|
+
}
|
439
|
+
|
440
|
+
return node.getAttribute("id");
|
441
|
+
}
|
442
|
+
}
|
443
|
+
return null;
|
444
|
+
}
|
445
|
+
|
446
|
+
/**
|
447
|
+
* This method is called by the dom and should not be called directly.
|
448
|
+
*
|
449
|
+
* @return {void}
|
450
|
+
*/
|
451
|
+
connectedCallback() {
|
452
|
+
super.connectedCallback();
|
453
|
+
|
454
|
+
const document = getDocument();
|
455
|
+
|
456
|
+
for (const [, type] of Object.entries(["click", "touch"])) {
|
457
|
+
// close on outside ui-events
|
458
|
+
document.addEventListener(type, this[closeEventHandler]);
|
459
|
+
}
|
460
|
+
}
|
461
|
+
|
462
|
+
/**
|
463
|
+
* This method is called by the dom and should not be called directly.
|
464
|
+
*
|
465
|
+
* @return {void}
|
466
|
+
*/
|
467
|
+
disconnectedCallback() {
|
468
|
+
super.disconnectedCallback();
|
469
|
+
|
470
|
+
const document = getDocument();
|
471
|
+
|
472
|
+
// close on outside ui-events
|
473
|
+
for (const [, type] of Object.entries(["click", "touch"])) {
|
474
|
+
document.removeEventListener(type, this[closeEventHandler]);
|
475
|
+
}
|
476
|
+
}
|
477
477
|
}
|
478
478
|
|
479
479
|
/**
|
@@ -481,188 +481,188 @@ class Tabs extends CustomElement {
|
|
481
481
|
* @returns {object}
|
482
482
|
*/
|
483
483
|
function getTranslations() {
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
484
|
+
const locale = getLocaleOfDocument();
|
485
|
+
switch (locale.language) {
|
486
|
+
case "de":
|
487
|
+
return {
|
488
|
+
"new-tab-label": "Neuer Tab",
|
489
|
+
};
|
490
|
+
case "fr":
|
491
|
+
return {
|
492
|
+
"new-tab-label": "Nouvel Onglet",
|
493
|
+
};
|
494
|
+
case "sp":
|
495
|
+
return {
|
496
|
+
"new-tab-label": "Nueva Pestaña",
|
497
|
+
};
|
498
|
+
case "it":
|
499
|
+
return {
|
500
|
+
"new-tab-label": "Nuova Scheda",
|
501
|
+
};
|
502
|
+
case "pl":
|
503
|
+
return {
|
504
|
+
"new-tab-label": "Nowa Karta",
|
505
|
+
};
|
506
|
+
case "no":
|
507
|
+
return {
|
508
|
+
"new-tab-label": "Ny Fane",
|
509
|
+
};
|
510
|
+
case "dk":
|
511
|
+
return {
|
512
|
+
"new-tab-label": "Ny Fane",
|
513
|
+
};
|
514
|
+
case "sw":
|
515
|
+
return {
|
516
|
+
"new-tab-label": "Ny Flik",
|
517
|
+
};
|
518
|
+
default:
|
519
|
+
case "en":
|
520
|
+
return {
|
521
|
+
"new-tab-label": "New Tab",
|
522
|
+
};
|
523
|
+
}
|
524
524
|
}
|
525
525
|
|
526
526
|
/**
|
527
527
|
* @private
|
528
528
|
*/
|
529
529
|
function initPopperSwitch() {
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
530
|
+
const nodes = getSlottedElements.call(this, `[${ATTRIBUTE_ROLE}="switch"]`); // null ↦ only unnamed slots
|
531
|
+
let switchButton;
|
532
|
+
if (nodes.size === 0) {
|
533
|
+
switchButton = document.createElement("button");
|
534
|
+
switchButton.setAttribute(ATTRIBUTE_ROLE, "switch");
|
535
|
+
switchButton.setAttribute("part", "switch");
|
536
|
+
switchButton.classList.add("hidden");
|
537
|
+
const classList = this.getOption("classes.button");
|
538
|
+
if (classList) {
|
539
|
+
switchButton.classList.add(classList);
|
540
|
+
}
|
541
|
+
switchButton.innerHTML =
|
542
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/></svg>';
|
543
|
+
this[navElementSymbol].prepend(switchButton);
|
544
|
+
} else {
|
545
|
+
switchButton = nodes.next();
|
546
|
+
}
|
547
|
+
|
548
|
+
/**
|
549
|
+
* @param {Event} event
|
550
|
+
*/
|
551
|
+
this[popperSwitchEventHandler] = (event) => {
|
552
|
+
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "switch");
|
553
|
+
|
554
|
+
if (element instanceof HTMLButtonElement) {
|
555
|
+
togglePopper.call(this);
|
556
|
+
}
|
557
|
+
};
|
558
|
+
|
559
|
+
for (const type of ["click", "touch"]) {
|
560
|
+
switchButton.addEventListener(type, this[popperSwitchEventHandler]);
|
561
|
+
}
|
562
|
+
|
563
|
+
this[switchElementSymbol] = switchButton;
|
564
564
|
}
|
565
565
|
|
566
566
|
/**
|
567
567
|
* @private
|
568
568
|
*/
|
569
569
|
function hidePopper() {
|
570
|
-
|
571
|
-
|
572
|
-
|
570
|
+
if (!this[popperInstanceSymbol]) {
|
571
|
+
return;
|
572
|
+
}
|
573
573
|
|
574
|
-
|
575
|
-
|
576
|
-
|
574
|
+
this[popperElementSymbol].style.display = "none";
|
575
|
+
// performance https://popper.js.org/docs/v2/tutorial/#performance
|
576
|
+
setEventListenersModifiers.call(this, false);
|
577
577
|
}
|
578
578
|
|
579
579
|
/**
|
580
580
|
* @private
|
581
581
|
*/
|
582
582
|
function showPopper() {
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
583
|
+
if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
|
584
|
+
return;
|
585
|
+
}
|
586
|
+
|
587
|
+
this[popperElementSymbol].style.visibility = "hidden";
|
588
|
+
this[popperElementSymbol].style.display = STYLE_DISPLAY_MODE_BLOCK;
|
589
|
+
// performance https://popper.js.org/docs/v2/tutorial/#performance
|
590
|
+
setEventListenersModifiers.call(this, true);
|
591
|
+
|
592
|
+
this[popperInstanceSymbol].update();
|
593
|
+
|
594
|
+
new Processing(() => {
|
595
|
+
this[popperElementSymbol].style.removeProperty("visibility");
|
596
|
+
})
|
597
|
+
.run(undefined)
|
598
|
+
.then(() => {})
|
599
|
+
.catch((e) => {
|
600
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
|
601
|
+
});
|
602
602
|
}
|
603
603
|
|
604
604
|
/**
|
605
605
|
* @private
|
606
606
|
*/
|
607
607
|
function togglePopper() {
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
608
|
+
if (this[popperElementSymbol].style.display === STYLE_DISPLAY_MODE_BLOCK) {
|
609
|
+
hidePopper.call(this);
|
610
|
+
} else {
|
611
|
+
showPopper.call(this);
|
612
|
+
}
|
613
613
|
}
|
614
614
|
|
615
615
|
/**
|
616
616
|
* @private
|
617
617
|
*/
|
618
618
|
function attachResizeObserver() {
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
619
|
+
// against flickering
|
620
|
+
this[resizeObserverSymbol] = new ResizeObserver((entries) => {
|
621
|
+
if (this[timerCallbackSymbol] instanceof DeadMansSwitch) {
|
622
|
+
try {
|
623
|
+
this[timerCallbackSymbol].touch();
|
624
|
+
return;
|
625
|
+
} catch (e) {
|
626
|
+
delete this[timerCallbackSymbol];
|
627
|
+
}
|
628
|
+
}
|
629
|
+
|
630
|
+
this[timerCallbackSymbol] = new DeadMansSwitch(200, () => {
|
631
|
+
this[dimensionsSymbol].setVia("data.calculated", false);
|
632
|
+
checkAndRearrangeButtons.call(this);
|
633
|
+
});
|
634
|
+
});
|
635
|
+
|
636
|
+
this[resizeObserverSymbol].observe(this[navElementSymbol]);
|
637
637
|
}
|
638
638
|
|
639
639
|
/**
|
640
640
|
* @private
|
641
641
|
*/
|
642
642
|
function attachTabChangeObserver() {
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
643
|
+
// against flickering
|
644
|
+
new MutationObserver((mutations) => {
|
645
|
+
let runUpdate = false;
|
646
|
+
|
647
|
+
for (const mutation of mutations) {
|
648
|
+
if (mutation.type === "childList") {
|
649
|
+
if (
|
650
|
+
mutation.addedNodes.length > 0 ||
|
651
|
+
mutation.removedNodes.length > 0
|
652
|
+
) {
|
653
|
+
runUpdate = true;
|
654
|
+
break;
|
655
|
+
}
|
656
|
+
}
|
657
|
+
}
|
658
|
+
|
659
|
+
if (runUpdate === true) {
|
660
|
+
this[dimensionsSymbol].setVia("data.calculated", false);
|
661
|
+
initTabButtons.call(this);
|
662
|
+
}
|
663
|
+
}).observe(this, {
|
664
|
+
childList: true,
|
665
|
+
});
|
666
666
|
}
|
667
667
|
|
668
668
|
/**
|
@@ -671,41 +671,41 @@ function attachTabChangeObserver() {
|
|
671
671
|
* @external "external:createPopper"
|
672
672
|
*/
|
673
673
|
function initPopper() {
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
674
|
+
const self = this;
|
675
|
+
|
676
|
+
const options = extend({}, self.getOption("popper"));
|
677
|
+
|
678
|
+
self[popperInstanceSymbol] = createPopper(
|
679
|
+
self[switchElementSymbol],
|
680
|
+
self[popperElementSymbol],
|
681
|
+
options,
|
682
|
+
);
|
683
|
+
|
684
|
+
const observer1 = new MutationObserver(function (mutations) {
|
685
|
+
let runUpdate = false;
|
686
|
+
for (const mutation of mutations) {
|
687
|
+
if (mutation.type === "childList") {
|
688
|
+
if (
|
689
|
+
mutation.addedNodes.length > 0 ||
|
690
|
+
mutation.removedNodes.length > 0
|
691
|
+
) {
|
692
|
+
runUpdate = true;
|
693
|
+
break;
|
694
|
+
}
|
695
|
+
}
|
696
|
+
}
|
697
|
+
|
698
|
+
if (runUpdate === true) {
|
699
|
+
self[popperInstanceSymbol].update();
|
700
|
+
}
|
701
|
+
});
|
702
|
+
|
703
|
+
observer1.observe(self[popperNavElementSymbol], {
|
704
|
+
childList: true,
|
705
|
+
subtree: true,
|
706
|
+
});
|
707
|
+
|
708
|
+
return self;
|
709
709
|
}
|
710
710
|
|
711
711
|
/**
|
@@ -713,230 +713,230 @@ function initPopper() {
|
|
713
713
|
* @param {HTMLElement} element
|
714
714
|
*/
|
715
715
|
function show(element) {
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
716
|
+
if (!this.shadowRoot) {
|
717
|
+
throw new Error("no shadow-root is defined");
|
718
|
+
}
|
719
|
+
|
720
|
+
const reference = element.getAttribute(`${ATTRIBUTE_PREFIX}tab-reference`);
|
721
|
+
|
722
|
+
const nodes = getSlottedElements.call(this);
|
723
|
+
for (const node of nodes) {
|
724
|
+
const id = node.getAttribute("id");
|
725
|
+
|
726
|
+
if (id === reference) {
|
727
|
+
node.classList.add("active");
|
728
|
+
|
729
|
+
fireCustomEvent(this, "monster-tab-change", {
|
730
|
+
reference,
|
731
|
+
});
|
732
|
+
|
733
|
+
const openDelay = Number.parseInt(
|
734
|
+
this.getOption("features.openDelay"),
|
735
|
+
10,
|
736
|
+
);
|
737
|
+
|
738
|
+
if (!Number.isNaN(openDelay) && openDelay > 0) {
|
739
|
+
node.style.visibility = "hidden";
|
740
|
+
|
741
|
+
setTimeout(() => {
|
742
|
+
node.style.visibility = "visible";
|
743
|
+
}, openDelay);
|
744
|
+
}
|
745
|
+
|
746
|
+
// get all data- from button and filter out data-monster-attributes and data-monster-insert
|
747
|
+
const data = {};
|
748
|
+
const mask = [
|
749
|
+
"data-monster-attributes",
|
750
|
+
"data-monster-insert-reference",
|
751
|
+
"data-monster-state",
|
752
|
+
"data-monster-button-label",
|
753
|
+
"data-monster-objectlink",
|
754
|
+
"data-monster-role",
|
755
|
+
];
|
756
|
+
|
757
|
+
for (const [, attr] of Object.entries(node.attributes)) {
|
758
|
+
if (attr.name.startsWith("data-") && mask.indexOf(attr.name) === -1) {
|
759
|
+
data[attr.name] = attr.value;
|
760
|
+
}
|
761
|
+
}
|
762
|
+
|
763
|
+
if (node.hasAttribute(ATTRIBUTE_FORM_URL)) {
|
764
|
+
const url = node.getAttribute(ATTRIBUTE_FORM_URL);
|
765
|
+
|
766
|
+
if (
|
767
|
+
!node.hasAttribute(ATTRIBUTE_FORM_RELOAD) ||
|
768
|
+
node.getAttribute(ATTRIBUTE_FORM_RELOAD).toLowerCase() === "onshow"
|
769
|
+
) {
|
770
|
+
node.removeAttribute(ATTRIBUTE_FORM_URL);
|
771
|
+
}
|
772
|
+
|
773
|
+
const options = this.getOption("fetch", {});
|
774
|
+
const filter = undefined;
|
775
|
+
loadAndAssignContent(node, url, options, filter)
|
776
|
+
.then(() => {
|
777
|
+
fireCustomEvent(this, "monster-tab-changed", {
|
778
|
+
reference,
|
779
|
+
});
|
780
|
+
})
|
781
|
+
.catch((e) => {
|
782
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
|
783
|
+
});
|
784
|
+
} else {
|
785
|
+
fireCustomEvent(this, "monster-tab-changed", {
|
786
|
+
reference,
|
787
|
+
data,
|
788
|
+
});
|
789
|
+
}
|
790
|
+
} else {
|
791
|
+
node.classList.remove("active");
|
792
|
+
}
|
793
|
+
}
|
794
|
+
|
795
|
+
const standardButtons = this.getOption("buttons.standard");
|
796
|
+
for (const index in standardButtons) {
|
797
|
+
const button = standardButtons[index];
|
798
|
+
const state = button["reference"] === reference ? "active" : "inactive";
|
799
|
+
this.setOption(`buttons.standard.${index}.state`, state);
|
800
|
+
}
|
801
|
+
|
802
|
+
const popperButton = this.getOption("buttons.popper");
|
803
|
+
for (const index in popperButton) {
|
804
|
+
const button = popperButton[index];
|
805
|
+
const state = button["reference"] === reference ? "active" : "inactive";
|
806
|
+
this.setOption(`buttons.popper.${index}.state`, state);
|
807
|
+
}
|
808
|
+
|
809
|
+
hidePopper.call(this);
|
810
810
|
}
|
811
811
|
|
812
812
|
/**
|
813
813
|
* @private
|
814
814
|
*/
|
815
815
|
function initEventHandler() {
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
816
|
+
const self = this;
|
817
|
+
|
818
|
+
if (!this.shadowRoot) {
|
819
|
+
throw new Error("no shadow-root is defined");
|
820
|
+
}
|
821
|
+
|
822
|
+
/**
|
823
|
+
* @param {Event} event
|
824
|
+
* @fires monster-tab-remove
|
825
|
+
*/
|
826
|
+
this[removeTabEventHandler] = (event) => {
|
827
|
+
const element = findTargetElementFromEvent(
|
828
|
+
event,
|
829
|
+
ATTRIBUTE_ROLE,
|
830
|
+
"remove-tab",
|
831
|
+
);
|
832
|
+
|
833
|
+
if (element instanceof HTMLElement) {
|
834
|
+
const button = findTargetElementFromEvent(
|
835
|
+
event,
|
836
|
+
ATTRIBUTE_ROLE,
|
837
|
+
"button",
|
838
|
+
);
|
839
|
+
|
840
|
+
if (button instanceof HTMLButtonElement && button.disabled !== true) {
|
841
|
+
const reference = button.getAttribute(
|
842
|
+
`${ATTRIBUTE_PREFIX}tab-reference`,
|
843
|
+
);
|
844
|
+
|
845
|
+
let doChange = false;
|
846
|
+
let nextName = null;
|
847
|
+
let previousName = null;
|
848
|
+
|
849
|
+
const btn = this.getOption("buttons");
|
850
|
+
for (let i = 0; i < btn.standard.length; i++) {
|
851
|
+
if (btn.standard[i].reference === reference) {
|
852
|
+
if (btn.standard[i].state === "active") {
|
853
|
+
doChange = i;
|
854
|
+
if (i < btn.standard.length - 1) {
|
855
|
+
nextName = btn.standard[i + 1]?.reference;
|
856
|
+
}
|
857
|
+
if (i > 0) {
|
858
|
+
previousName = btn.standard[i - 1]?.reference;
|
859
|
+
}
|
860
|
+
}
|
861
|
+
break;
|
862
|
+
}
|
863
|
+
}
|
864
|
+
|
865
|
+
if (reference) {
|
866
|
+
const container = this.querySelector(`[id=${reference}]`);
|
867
|
+
if (container instanceof HTMLElement) {
|
868
|
+
if (doChange) {
|
869
|
+
switch (this.getOption("features.removeBehavior")) {
|
870
|
+
case "auto":
|
871
|
+
if (nextName !== null) {
|
872
|
+
self.activeTab(nextName);
|
873
|
+
} else {
|
874
|
+
if (previousName !== null) {
|
875
|
+
self.activeTab(previousName);
|
876
|
+
}
|
877
|
+
}
|
878
|
+
break;
|
879
|
+
case "next":
|
880
|
+
if (nextName !== null) {
|
881
|
+
self.activeTab(nextName);
|
882
|
+
}
|
883
|
+
break;
|
884
|
+
case "previous":
|
885
|
+
if (previousName !== null) {
|
886
|
+
self.activeTab(previousName);
|
887
|
+
}
|
888
|
+
break;
|
889
|
+
|
890
|
+
default: // and "none"
|
891
|
+
break;
|
892
|
+
}
|
893
|
+
}
|
894
|
+
|
895
|
+
container.remove();
|
896
|
+
initTabButtons.call(this);
|
897
|
+
fireCustomEvent(this, "monster-tab-remove", {
|
898
|
+
reference,
|
899
|
+
});
|
900
|
+
}
|
901
|
+
}
|
902
|
+
}
|
903
|
+
}
|
904
|
+
};
|
905
|
+
|
906
|
+
/**
|
907
|
+
* @param {Event} event
|
908
|
+
*/
|
909
|
+
this[changeTabEventHandler] = (event) => {
|
910
|
+
const element = findTargetElementFromEvent(event, ATTRIBUTE_ROLE, "button");
|
911
|
+
|
912
|
+
if (element instanceof HTMLButtonElement && element.disabled !== true) {
|
913
|
+
show.call(this, element);
|
914
|
+
}
|
915
|
+
};
|
916
|
+
|
917
|
+
/**
|
918
|
+
* @param {Event} event
|
919
|
+
*/
|
920
|
+
this[closeEventHandler] = (event) => {
|
921
|
+
const path = event.composedPath();
|
922
|
+
|
923
|
+
for (const [, element] of Object.entries(path)) {
|
924
|
+
if (element === this) {
|
925
|
+
return;
|
926
|
+
}
|
927
|
+
}
|
928
|
+
|
929
|
+
hidePopper.call(this);
|
930
|
+
};
|
931
|
+
|
932
|
+
// the order is important, because the remove must be before the change
|
933
|
+
this[navElementSymbol].addEventListener("touch", this[removeTabEventHandler]);
|
934
|
+
this[navElementSymbol].addEventListener("click", this[removeTabEventHandler]);
|
935
|
+
|
936
|
+
this[navElementSymbol].addEventListener("touch", this[changeTabEventHandler]);
|
937
|
+
this[navElementSymbol].addEventListener("click", this[changeTabEventHandler]);
|
938
|
+
|
939
|
+
return this;
|
940
940
|
}
|
941
941
|
|
942
942
|
/**
|
@@ -944,37 +944,37 @@ function initEventHandler() {
|
|
944
944
|
* @param observedNode
|
945
945
|
*/
|
946
946
|
function attachTabMutationObserver(observedNode) {
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
947
|
+
const self = this;
|
948
|
+
|
949
|
+
if (hasObjectLink(observedNode, mutationObserverSymbol)) {
|
950
|
+
return;
|
951
|
+
}
|
952
|
+
|
953
|
+
/**
|
954
|
+
* this construct monitors a node whether it is disabled or modified
|
955
|
+
* @type {MutationObserver}
|
956
|
+
*/
|
957
|
+
const observer = new MutationObserver(function (mutations) {
|
958
|
+
if (isArray(mutations)) {
|
959
|
+
const mutation = mutations.pop();
|
960
|
+
if (mutation instanceof MutationRecord) {
|
961
|
+
initTabButtons.call(self);
|
962
|
+
}
|
963
|
+
}
|
964
|
+
});
|
965
|
+
|
966
|
+
observer.observe(observedNode, {
|
967
|
+
childList: false,
|
968
|
+
attributes: true,
|
969
|
+
subtree: false,
|
970
|
+
attributeFilter: [
|
971
|
+
"disabled",
|
972
|
+
ATTRIBUTE_BUTTON_LABEL,
|
973
|
+
`${ATTRIBUTE_PREFIX}button-icon`,
|
974
|
+
],
|
975
|
+
});
|
976
|
+
|
977
|
+
addToObjectLink(observedNode, mutationObserverSymbol, observer);
|
978
978
|
}
|
979
979
|
|
980
980
|
/**
|
@@ -983,22 +983,22 @@ function attachTabMutationObserver(observedNode) {
|
|
983
983
|
* @throws {Error} no shadow-root is defined
|
984
984
|
*/
|
985
985
|
function initControlReferences() {
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
986
|
+
if (!this.shadowRoot) {
|
987
|
+
throw new Error("no shadow-root is defined");
|
988
|
+
}
|
989
|
+
|
990
|
+
this[controlElementSymbol] = this.shadowRoot.querySelector(
|
991
|
+
`[${ATTRIBUTE_ROLE}=control]`,
|
992
|
+
);
|
993
|
+
this[navElementSymbol] = this.shadowRoot.querySelector(
|
994
|
+
`nav[${ATTRIBUTE_ROLE}=nav]`,
|
995
|
+
);
|
996
|
+
this[popperElementSymbol] = this.shadowRoot.querySelector(
|
997
|
+
`[${ATTRIBUTE_ROLE}=popper]`,
|
998
|
+
);
|
999
|
+
this[popperNavElementSymbol] = this.shadowRoot.querySelector(
|
1000
|
+
`[${ATTRIBUTE_ROLE}=popper-nav]`,
|
1001
|
+
);
|
1002
1002
|
}
|
1003
1003
|
|
1004
1004
|
/**
|
@@ -1008,110 +1008,110 @@ function initControlReferences() {
|
|
1008
1008
|
*
|
1009
1009
|
*/
|
1010
1010
|
function initTabButtons() {
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1011
|
+
if (!this.shadowRoot) {
|
1012
|
+
throw new Error("no shadow-root is defined");
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
let activeReference;
|
1016
|
+
|
1017
|
+
const dimensionsCalculated = this[dimensionsSymbol].getVia(
|
1018
|
+
"data.calculated",
|
1019
|
+
false,
|
1020
|
+
);
|
1021
|
+
|
1022
|
+
const buttons = [];
|
1023
|
+
const nodes = getSlottedElements.call(this, undefined, null); // null ↦ only unnamed slots
|
1024
|
+
|
1025
|
+
for (const node of nodes) {
|
1026
|
+
if (!(node instanceof HTMLElement)) continue;
|
1027
|
+
let label = getButtonLabel.call(this, node);
|
1028
|
+
|
1029
|
+
let reference;
|
1030
|
+
if (node.hasAttribute("id")) {
|
1031
|
+
reference = node.getAttribute("id");
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
let disabled;
|
1035
|
+
if (node.hasAttribute("disabled") || node.disabled === true) {
|
1036
|
+
disabled = true;
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
if (!reference) {
|
1040
|
+
reference = new ID("tab").toString();
|
1041
|
+
node.setAttribute("id", reference);
|
1042
|
+
}
|
1043
|
+
|
1044
|
+
if (node.hasAttribute(`${ATTRIBUTE_PREFIX}button-icon`)) {
|
1045
|
+
label = `<span part="label">${label}</span><img part="icon" alt="this is an icon" src="${node.getAttribute(
|
1046
|
+
`${ATTRIBUTE_PREFIX}button-icon`,
|
1047
|
+
)}">`;
|
1048
|
+
}
|
1049
|
+
|
1050
|
+
let remove = false;
|
1051
|
+
if (node.hasAttribute(`${ATTRIBUTE_PREFIX}removable`)) {
|
1052
|
+
remove = true;
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
if (node.matches(".active") === true && disabled !== true) {
|
1056
|
+
node.classList.remove("active");
|
1057
|
+
activeReference = reference;
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
const state = "";
|
1061
|
+
const classes = dimensionsCalculated ? "" : "invisible";
|
1062
|
+
|
1063
|
+
buttons.push({
|
1064
|
+
reference,
|
1065
|
+
label,
|
1066
|
+
state,
|
1067
|
+
class: classes,
|
1068
|
+
disabled,
|
1069
|
+
remove,
|
1070
|
+
});
|
1071
|
+
|
1072
|
+
attachTabMutationObserver.call(this, node);
|
1073
|
+
}
|
1074
|
+
|
1075
|
+
this.setOption("buttons.standard", clone(buttons));
|
1076
|
+
this.setOption("buttons.popper", []);
|
1077
|
+
this.setOption("marker", random());
|
1078
|
+
|
1079
|
+
return adjustButtonVisibility.call(this).then(() => {
|
1080
|
+
if (!activeReference && this.getOption("features.openFirst") === true) {
|
1081
|
+
const firstButton = this.getOption("buttons.standard").find(
|
1082
|
+
(button) => button.disabled !== true,
|
1083
|
+
);
|
1084
|
+
if (firstButton) {
|
1085
|
+
activeReference = firstButton.reference;
|
1086
|
+
}
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
if (activeReference) {
|
1090
|
+
return new Processing(() => {
|
1091
|
+
const button = this.shadowRoot.querySelector(
|
1092
|
+
`[${ATTRIBUTE_PREFIX}tab-reference="${activeReference}"]`,
|
1093
|
+
);
|
1094
|
+
if (button instanceof HTMLButtonElement && button.disabled !== true) {
|
1095
|
+
show.call(this, button);
|
1096
|
+
}
|
1097
|
+
})
|
1098
|
+
.run(undefined)
|
1099
|
+
.then(() => {})
|
1100
|
+
.catch((e) => {
|
1101
|
+
addAttributeToken(this, ATTRIBUTE_ERRORMESSAGE, e.message);
|
1102
|
+
});
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
return Promise.resolve();
|
1106
|
+
});
|
1107
1107
|
}
|
1108
1108
|
|
1109
1109
|
function checkAndRearrangeButtons() {
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1110
|
+
if (this[dimensionsSymbol].getVia("data.calculated", false) !== true) {
|
1111
|
+
calculateNavigationButtonsDimensions.call(this);
|
1112
|
+
}
|
1113
1113
|
|
1114
|
-
|
1114
|
+
rearrangeButtons.call(this);
|
1115
1115
|
}
|
1116
1116
|
|
1117
1117
|
/**
|
@@ -1119,29 +1119,29 @@ function checkAndRearrangeButtons() {
|
|
1119
1119
|
* @return {Promise<unknown>}
|
1120
1120
|
*/
|
1121
1121
|
function adjustButtonVisibility() {
|
1122
|
-
|
1122
|
+
const self = this;
|
1123
1123
|
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1124
|
+
return new Promise((resolve) => {
|
1125
|
+
const observer = new MutationObserver(function (mutations) {
|
1126
|
+
const defCount = self.getOption("buttons.standard").length;
|
1127
|
+
const domCount = self[navElementSymbol].querySelectorAll(
|
1128
|
+
'button[data-monster-role="button"]',
|
1129
|
+
).length;
|
1130
1130
|
|
1131
|
-
|
1132
|
-
|
1131
|
+
// in drawing
|
1132
|
+
if (defCount !== domCount) return;
|
1133
1133
|
|
1134
|
-
|
1134
|
+
observer.disconnect();
|
1135
1135
|
|
1136
|
-
|
1136
|
+
checkAndRearrangeButtons.call(self);
|
1137
1137
|
|
1138
|
-
|
1139
|
-
|
1138
|
+
resolve();
|
1139
|
+
});
|
1140
1140
|
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1141
|
+
observer.observe(self[navElementSymbol], {
|
1142
|
+
attributes: true,
|
1143
|
+
});
|
1144
|
+
});
|
1145
1145
|
}
|
1146
1146
|
|
1147
1147
|
/**
|
@@ -1150,17 +1150,17 @@ function adjustButtonVisibility() {
|
|
1150
1150
|
* @return {number}
|
1151
1151
|
*/
|
1152
1152
|
function getDimValue(value) {
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1153
|
+
if ([undefined, null].indexOf(value) !== -1) {
|
1154
|
+
return 0;
|
1155
|
+
}
|
1156
1156
|
|
1157
|
-
|
1157
|
+
const valueAsInt = parseInt(value, 10);
|
1158
1158
|
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1159
|
+
if (isNaN(valueAsInt)) {
|
1160
|
+
return 0;
|
1161
|
+
}
|
1162
1162
|
|
1163
|
-
|
1163
|
+
return valueAsInt;
|
1164
1164
|
}
|
1165
1165
|
|
1166
1166
|
/**
|
@@ -1169,18 +1169,18 @@ function getDimValue(value) {
|
|
1169
1169
|
* @return {number}
|
1170
1170
|
*/
|
1171
1171
|
function calcBoxWidth(node) {
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1172
|
+
const dim = getGlobal("window").getComputedStyle(node);
|
1173
|
+
const bounding = node.getBoundingClientRect();
|
1174
|
+
|
1175
|
+
return (
|
1176
|
+
getDimValue(dim["border-left-width"]) +
|
1177
|
+
getDimValue(dim["padding-left"]) +
|
1178
|
+
getDimValue(dim["margin-left"]) +
|
1179
|
+
getDimValue(bounding["width"]) +
|
1180
|
+
getDimValue(dim["border-right-width"]) +
|
1181
|
+
getDimValue(dim["margin-right"]) +
|
1182
|
+
getDimValue(dim["padding-left"])
|
1183
|
+
);
|
1184
1184
|
}
|
1185
1185
|
|
1186
1186
|
/**
|
@@ -1188,40 +1188,40 @@ function calcBoxWidth(node) {
|
|
1188
1188
|
* @return {Object}
|
1189
1189
|
*/
|
1190
1190
|
function rearrangeButtons() {
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1191
|
+
getWindow().requestAnimationFrame(() => {
|
1192
|
+
const standardButtons = [];
|
1193
|
+
const popperButtons = [];
|
1194
|
+
let sum = 0;
|
1195
|
+
const space = this[dimensionsSymbol].getVia("data.space");
|
1196
|
+
|
1197
|
+
if (space <= 0) {
|
1198
|
+
return;
|
1199
|
+
}
|
1200
|
+
|
1201
|
+
const buttons = this.getOption("buttons.standard");
|
1202
|
+
for (const [, button] of buttons.entries()) {
|
1203
|
+
const ref = button?.reference;
|
1204
|
+
|
1205
|
+
sum += this[dimensionsSymbol].getVia(`data.button.${ref}`);
|
1206
|
+
|
1207
|
+
if (sum > space) {
|
1208
|
+
popperButtons.push(clone(button));
|
1209
|
+
} else {
|
1210
|
+
standardButtons.push(clone(button));
|
1211
|
+
}
|
1212
|
+
}
|
1213
|
+
|
1214
|
+
this.setOption("buttons.standard", standardButtons);
|
1215
|
+
this.setOption("buttons.popper", popperButtons);
|
1216
|
+
|
1217
|
+
if (this[switchElementSymbol]) {
|
1218
|
+
if (popperButtons.length > 0) {
|
1219
|
+
this[switchElementSymbol].classList.remove("hidden");
|
1220
|
+
} else {
|
1221
|
+
this[switchElementSymbol].classList.add("hidden");
|
1222
|
+
}
|
1223
|
+
}
|
1224
|
+
});
|
1225
1225
|
}
|
1226
1226
|
|
1227
1227
|
/**
|
@@ -1229,53 +1229,53 @@ function rearrangeButtons() {
|
|
1229
1229
|
* @return {Object}
|
1230
1230
|
*/
|
1231
1231
|
function calculateNavigationButtonsDimensions() {
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1232
|
+
const width = this[navElementSymbol].getBoundingClientRect().width;
|
1233
|
+
|
1234
|
+
let startEndWidth = 0;
|
1235
|
+
|
1236
|
+
getSlottedElements.call(this, undefined, "start").forEach((node) => {
|
1237
|
+
startEndWidth += calcBoxWidth.call(this, node);
|
1238
|
+
});
|
1239
|
+
|
1240
|
+
getSlottedElements.call(this, undefined, "end").forEach((node) => {
|
1241
|
+
startEndWidth += calcBoxWidth.call(this, node);
|
1242
|
+
});
|
1243
|
+
|
1244
|
+
this[dimensionsSymbol].setVia("data.space", width - startEndWidth - 2);
|
1245
|
+
this[dimensionsSymbol].setVia("data.visible", !(width === 0));
|
1246
|
+
|
1247
|
+
const buttons = this.getOption("buttons.standard").concat(
|
1248
|
+
this.getOption("buttons.popper"),
|
1249
|
+
);
|
1250
|
+
|
1251
|
+
for (const [i, button] of buttons.entries()) {
|
1252
|
+
const ref = button?.reference;
|
1253
|
+
const element = this[navElementSymbol].querySelector(
|
1254
|
+
`:scope > [${ATTRIBUTE_PREFIX}tab-reference="${ref}"]`,
|
1255
|
+
);
|
1256
|
+
if (!(element instanceof HTMLButtonElement)) continue;
|
1257
|
+
|
1258
|
+
this[dimensionsSymbol].setVia(
|
1259
|
+
`data.button.${ref}`,
|
1260
|
+
calcBoxWidth.call(this, element),
|
1261
|
+
);
|
1262
|
+
button["class"] = new TokenList(button["class"])
|
1263
|
+
.remove("invisible")
|
1264
|
+
.toString();
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
const slots = this[controlElementSymbol].querySelectorAll(
|
1268
|
+
`nav[${ATTRIBUTE_PREFIX}role=nav] > slot.invisible, slot[${ATTRIBUTE_PREFIX}role=slot].invisible`,
|
1269
|
+
);
|
1270
|
+
for (const [, slot] of slots.entries()) {
|
1271
|
+
slot.classList.remove("invisible");
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
this.setOption("buttons.standard", clone(buttons));
|
1275
|
+
|
1276
|
+
getWindow().requestAnimationFrame(() => {
|
1277
|
+
this[dimensionsSymbol].setVia("data.calculated", true);
|
1278
|
+
});
|
1279
1279
|
}
|
1280
1280
|
|
1281
1281
|
/**
|
@@ -1284,34 +1284,34 @@ function calculateNavigationButtonsDimensions() {
|
|
1284
1284
|
* @return {string}
|
1285
1285
|
*/
|
1286
1286
|
function getButtonLabel(node) {
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1287
|
+
let label;
|
1288
|
+
let setLabel = false;
|
1289
|
+
if (node.hasAttribute(ATTRIBUTE_BUTTON_LABEL)) {
|
1290
|
+
label = node.getAttribute(ATTRIBUTE_BUTTON_LABEL);
|
1291
|
+
} else {
|
1292
|
+
label = node.innerText;
|
1293
|
+
setLabel = true;
|
1294
|
+
}
|
1295
|
+
|
1296
|
+
if (!isString(label)) {
|
1297
|
+
label = "";
|
1298
|
+
}
|
1299
|
+
|
1300
|
+
label = label.trim();
|
1301
|
+
|
1302
|
+
if (label === "") {
|
1303
|
+
label = this.getOption("labels.new-tab-label", "New Tab");
|
1304
|
+
}
|
1305
|
+
|
1306
|
+
if (label.length > 100) {
|
1307
|
+
label = `${label.substring(0, 99)}…`;
|
1308
|
+
}
|
1309
|
+
|
1310
|
+
if (setLabel === true) {
|
1311
|
+
node.setAttribute(ATTRIBUTE_BUTTON_LABEL, label);
|
1312
|
+
}
|
1313
|
+
|
1314
|
+
return label;
|
1315
1315
|
}
|
1316
1316
|
|
1317
1317
|
/**
|
@@ -1319,8 +1319,8 @@ function getButtonLabel(node) {
|
|
1319
1319
|
* @return {string}
|
1320
1320
|
*/
|
1321
1321
|
function getTemplate() {
|
1322
|
-
|
1323
|
-
|
1322
|
+
// language=HTML
|
1323
|
+
return `
|
1324
1324
|
<template id="buttons">
|
1325
1325
|
<button part="button"
|
1326
1326
|
tabindex="0"
|