@birdapi/velinstyle 0.6.1 → 0.7.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/README.de.md +315 -316
- package/README.md +8 -9
- package/cli/index.js +4 -3
- package/cli/scanner.js +46 -0
- package/components/focus-manager.js +106 -80
- package/components/index.js +1 -1
- package/components/velin-accordion.js +112 -98
- package/components/velin-carousel.js +40 -5
- package/components/velin-collapse.js +95 -65
- package/components/velin-drawer.js +6 -3
- package/components/velin-dropdown.js +33 -2
- package/components/velin-modal.js +3 -1
- package/components/velin-popover.js +61 -21
- package/components/velin-tooltip-wc.js +26 -2
- package/dist/velinstyle-components.iife.js +220 -51
- package/dist/velinstyle-components.js +222 -51
- package/dist/velinstyle-components.min.js +67 -68
- package/dist/velinstyle.css +168 -41
- package/dist/velinstyle.min.css +1 -1
- package/package.json +6 -3
- package/src/a11y/focus-not-obscured.css +21 -0
- package/src/a11y/forced-colors.css +86 -38
- package/src/a11y/high-contrast-aaa.css +37 -0
- package/src/a11y/preferences.css +106 -85
- package/src/a11y/security.css +101 -104
- package/src/a11y/target-size.css +32 -0
- package/src/base/root.css +4 -4
- package/src/velinstyle.css +6 -1
|
@@ -10,10 +10,15 @@
|
|
|
10
10
|
"summary",
|
|
11
11
|
"details"
|
|
12
12
|
].join(", ");
|
|
13
|
+
function isFocusable(el) {
|
|
14
|
+
if (el.hasAttribute("disabled") || el.getAttribute("aria-hidden") === "true") return false;
|
|
15
|
+
if (el.closest("[inert]")) return false;
|
|
16
|
+
const style = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
17
|
+
if (style && (style.visibility === "hidden" || style.display === "none")) return false;
|
|
18
|
+
return el.getClientRects().length > 0;
|
|
19
|
+
}
|
|
13
20
|
function getFocusableElements(root) {
|
|
14
|
-
return [...root.querySelectorAll(FOCUSABLE_SELECTOR)].filter(
|
|
15
|
-
(el) => !el.hasAttribute("disabled") && el.offsetParent !== null
|
|
16
|
-
);
|
|
21
|
+
return [...root.querySelectorAll(FOCUSABLE_SELECTOR)].filter(isFocusable);
|
|
17
22
|
}
|
|
18
23
|
function trapFocus(root, event) {
|
|
19
24
|
if (event.key !== "Tab") return;
|
|
@@ -69,6 +74,23 @@
|
|
|
69
74
|
element.focus();
|
|
70
75
|
}
|
|
71
76
|
}
|
|
77
|
+
var _inertSiblings = [];
|
|
78
|
+
function setBackgroundInert(except) {
|
|
79
|
+
_inertSiblings = [];
|
|
80
|
+
for (const child of document.body.children) {
|
|
81
|
+
if (child === except || child.contains(except)) continue;
|
|
82
|
+
if (!child.hasAttribute("inert")) {
|
|
83
|
+
child.setAttribute("inert", "");
|
|
84
|
+
_inertSiblings.push(child);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function clearBackgroundInert() {
|
|
89
|
+
for (const el of _inertSiblings) {
|
|
90
|
+
el.removeAttribute("inert");
|
|
91
|
+
}
|
|
92
|
+
_inertSiblings = [];
|
|
93
|
+
}
|
|
72
94
|
|
|
73
95
|
// components/sanitize.js
|
|
74
96
|
var ESCAPE_MAP = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
@@ -221,6 +243,7 @@
|
|
|
221
243
|
}
|
|
222
244
|
_open() {
|
|
223
245
|
this._previouslyFocused = saveFocus();
|
|
246
|
+
setBackgroundInert(this);
|
|
224
247
|
document.addEventListener("keydown", this._onKeydown);
|
|
225
248
|
document.body.style.overflow = "hidden";
|
|
226
249
|
requestAnimationFrame(() => {
|
|
@@ -231,6 +254,7 @@
|
|
|
231
254
|
_close() {
|
|
232
255
|
document.removeEventListener("keydown", this._onKeydown);
|
|
233
256
|
document.body.style.overflow = "";
|
|
257
|
+
clearBackgroundInert();
|
|
234
258
|
restoreFocus(this._previouslyFocused);
|
|
235
259
|
}
|
|
236
260
|
_onKeydown(event) {
|
|
@@ -309,6 +333,8 @@
|
|
|
309
333
|
this.attachShadow({ mode: "open", delegatesFocus: true });
|
|
310
334
|
this._onDocClick = this._onDocClick.bind(this);
|
|
311
335
|
this._onKeydown = this._onKeydown.bind(this);
|
|
336
|
+
this._typeahead = "";
|
|
337
|
+
this._typeaheadTimer = null;
|
|
312
338
|
}
|
|
313
339
|
connectedCallback() {
|
|
314
340
|
const align = this.getAttribute("align") || "start";
|
|
@@ -320,16 +346,29 @@
|
|
|
320
346
|
</div>
|
|
321
347
|
`;
|
|
322
348
|
const triggerSlot = this.shadowRoot.querySelector('slot[name="trigger"]');
|
|
349
|
+
const menuSlot = this.shadowRoot.querySelector("slot:not([name])");
|
|
323
350
|
triggerSlot.addEventListener("click", () => this.toggle());
|
|
324
351
|
triggerSlot.addEventListener("slotchange", () => {
|
|
325
352
|
const trigger = triggerSlot.assignedElements()[0];
|
|
326
353
|
if (trigger) {
|
|
327
354
|
trigger.setAttribute("aria-haspopup", "menu");
|
|
328
355
|
trigger.setAttribute("aria-expanded", this.hasAttribute("open") ? "true" : "false");
|
|
356
|
+
const menuId = this._menuId || (this._menuId = `velin-dropdown-menu-${Math.random().toString(36).slice(2, 9)}`);
|
|
357
|
+
trigger.setAttribute("aria-controls", menuId);
|
|
358
|
+
this.shadowRoot.querySelector(".menu")?.setAttribute("id", menuId);
|
|
329
359
|
}
|
|
330
360
|
});
|
|
361
|
+
menuSlot?.addEventListener("slotchange", () => this._normalizeMenuItems());
|
|
362
|
+
this._normalizeMenuItems();
|
|
331
363
|
this.addEventListener("keydown", this._onKeydown);
|
|
332
364
|
}
|
|
365
|
+
_normalizeMenuItems() {
|
|
366
|
+
const items = this._getMenuItems();
|
|
367
|
+
items.forEach((el, i) => {
|
|
368
|
+
if (!el.hasAttribute("role")) el.setAttribute("role", "menuitem");
|
|
369
|
+
el.setAttribute("tabindex", i === 0 ? "0" : "-1");
|
|
370
|
+
});
|
|
371
|
+
}
|
|
333
372
|
toggle() {
|
|
334
373
|
if (this.hasAttribute("open")) {
|
|
335
374
|
this.close();
|
|
@@ -373,9 +412,24 @@
|
|
|
373
412
|
return;
|
|
374
413
|
}
|
|
375
414
|
const items = this._getMenuItems();
|
|
376
|
-
if (items.length
|
|
377
|
-
|
|
415
|
+
if (items.length === 0) return;
|
|
416
|
+
if (event.key.length === 1 && /[a-z0-9]/i.test(event.key)) {
|
|
417
|
+
clearTimeout(this._typeaheadTimer);
|
|
418
|
+
this._typeahead += event.key.toLowerCase();
|
|
419
|
+
this._typeaheadTimer = setTimeout(() => {
|
|
420
|
+
this._typeahead = "";
|
|
421
|
+
}, 500);
|
|
422
|
+
const match = items.find(
|
|
423
|
+
(el) => (el.textContent?.trim().toLowerCase() || "").startsWith(this._typeahead)
|
|
424
|
+
);
|
|
425
|
+
if (match) {
|
|
426
|
+
event.preventDefault();
|
|
427
|
+
items.forEach((item) => item.setAttribute("tabindex", item === match ? "0" : "-1"));
|
|
428
|
+
match.focus();
|
|
429
|
+
}
|
|
430
|
+
return;
|
|
378
431
|
}
|
|
432
|
+
rovingTabindex(this, items, event);
|
|
379
433
|
}
|
|
380
434
|
disconnectedCallback() {
|
|
381
435
|
document.removeEventListener("click", this._onDocClick, true);
|
|
@@ -423,12 +477,25 @@
|
|
|
423
477
|
connectedCallback() {
|
|
424
478
|
this.shadowRoot.innerHTML = `
|
|
425
479
|
<style>${styles3}</style>
|
|
426
|
-
<
|
|
480
|
+
<slot></slot>
|
|
427
481
|
`;
|
|
428
482
|
this._exclusive = this.hasAttribute("exclusive");
|
|
483
|
+
this._wireDetails();
|
|
429
484
|
this.addEventListener("toggle", this._onToggle, true);
|
|
430
485
|
this.addEventListener("keydown", this._onKeydown.bind(this));
|
|
431
486
|
}
|
|
487
|
+
_wireDetails() {
|
|
488
|
+
let panelIndex = 0;
|
|
489
|
+
for (const details of this.querySelectorAll("details")) {
|
|
490
|
+
const summary = details.querySelector("summary");
|
|
491
|
+
const panel = details.querySelector(":scope > :not(summary)");
|
|
492
|
+
const panelId = panel?.id || `velin-accordion-panel-${++panelIndex}`;
|
|
493
|
+
if (panel && !panel.id) panel.id = panelId;
|
|
494
|
+
if (summary && panel) {
|
|
495
|
+
summary.setAttribute("aria-controls", panelId);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
432
499
|
_onToggle(event) {
|
|
433
500
|
if (!this._exclusive) return;
|
|
434
501
|
const openedDetail = event.target;
|
|
@@ -886,12 +953,13 @@
|
|
|
886
953
|
connectedCallback() {
|
|
887
954
|
const title = this.getAttribute("title") || "";
|
|
888
955
|
const safeTitle = escapeHTML(title);
|
|
956
|
+
const titleId = "velin-drawer-title";
|
|
889
957
|
this.shadowRoot.innerHTML = `
|
|
890
958
|
<style>${styles6}</style>
|
|
891
959
|
<div class="overlay" part="overlay"></div>
|
|
892
|
-
<div class="drawer" role="dialog" aria-modal="true" aria-
|
|
960
|
+
<div class="drawer" role="dialog" aria-modal="true" aria-labelledby="${titleId}" part="drawer">
|
|
893
961
|
<div class="header" part="header">
|
|
894
|
-
<h2 class="title">${safeTitle}</h2>
|
|
962
|
+
<h2 class="title" id="${titleId}">${safeTitle}</h2>
|
|
895
963
|
<button class="close-btn" aria-label="Close" part="close">×</button>
|
|
896
964
|
</div>
|
|
897
965
|
<div class="body" part="body"><slot></slot></div>
|
|
@@ -912,6 +980,7 @@
|
|
|
912
980
|
}
|
|
913
981
|
_open() {
|
|
914
982
|
this._prev = saveFocus();
|
|
983
|
+
setBackgroundInert(this);
|
|
915
984
|
document.addEventListener("keydown", this._onKey);
|
|
916
985
|
document.body.style.overflow = "hidden";
|
|
917
986
|
requestAnimationFrame(() => {
|
|
@@ -922,6 +991,7 @@
|
|
|
922
991
|
_close() {
|
|
923
992
|
document.removeEventListener("keydown", this._onKey);
|
|
924
993
|
document.body.style.overflow = "";
|
|
994
|
+
clearBackgroundInert();
|
|
925
995
|
restoreFocus(this._prev);
|
|
926
996
|
}
|
|
927
997
|
_onKey(e) {
|
|
@@ -1039,7 +1109,6 @@
|
|
|
1039
1109
|
transition: opacity 150ms ease, visibility 150ms ease;
|
|
1040
1110
|
}
|
|
1041
1111
|
:host([open]) .popover { opacity: 1; visibility: visible; }
|
|
1042
|
-
/* Placement */
|
|
1043
1112
|
.popover--top { inset-block-end: calc(100% + 0.5rem); inset-inline-start: 50%; transform: translateX(-50%); }
|
|
1044
1113
|
.popover--bottom { inset-block-start: calc(100% + 0.5rem); inset-inline-start: 50%; transform: translateX(-50%); }
|
|
1045
1114
|
.popover--start { inset-inline-end: calc(100% + 0.5rem); inset-block-start: 50%; transform: translateY(-50%); }
|
|
@@ -1052,6 +1121,7 @@
|
|
|
1052
1121
|
}
|
|
1053
1122
|
@media (prefers-reduced-motion: reduce) { .popover { transition: none; } }
|
|
1054
1123
|
`;
|
|
1124
|
+
var popoverId = 0;
|
|
1055
1125
|
var VelinPopover = class extends HTMLElement {
|
|
1056
1126
|
static get observedAttributes() {
|
|
1057
1127
|
return ["open"];
|
|
@@ -1059,42 +1129,52 @@
|
|
|
1059
1129
|
constructor() {
|
|
1060
1130
|
super();
|
|
1061
1131
|
this.attachShadow({ mode: "open" });
|
|
1132
|
+
this._popoverId = `velin-popover-${++popoverId}`;
|
|
1062
1133
|
this._onOutside = this._onOutside.bind(this);
|
|
1063
1134
|
this._onKey = this._onKey.bind(this);
|
|
1135
|
+
this._prevFocus = null;
|
|
1136
|
+
this._isDialog = false;
|
|
1064
1137
|
}
|
|
1065
1138
|
connectedCallback() {
|
|
1066
1139
|
const placement = this.getAttribute("placement") || "bottom";
|
|
1067
1140
|
const triggerType = this.getAttribute("trigger") || "click";
|
|
1068
1141
|
const title = this.getAttribute("title") || "";
|
|
1069
1142
|
const role = triggerType === "hover" ? "tooltip" : "dialog";
|
|
1143
|
+
this._isDialog = role === "dialog";
|
|
1070
1144
|
this.shadowRoot.innerHTML = `
|
|
1071
1145
|
<style>${styles8}</style>
|
|
1072
1146
|
<slot name="trigger"></slot>
|
|
1073
|
-
<div class="popover popover--${placement}" role="${role}" part="popover">
|
|
1147
|
+
<div class="popover popover--${placement}" id="${this._popoverId}" role="${role}" part="popover">
|
|
1074
1148
|
${title ? `<div class="popover__title" part="title">${escapeHTML(title)}</div>` : ""}
|
|
1075
1149
|
<slot></slot>
|
|
1076
1150
|
</div>
|
|
1077
1151
|
`;
|
|
1078
1152
|
const triggerSlot = this.shadowRoot.querySelector('slot[name="trigger"]');
|
|
1079
|
-
triggerSlot.addEventListener("slotchange", () =>
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1153
|
+
triggerSlot.addEventListener("slotchange", () => this._wireTrigger(triggerType));
|
|
1154
|
+
this._wireTrigger(triggerType);
|
|
1155
|
+
}
|
|
1156
|
+
_wireTrigger(triggerType) {
|
|
1157
|
+
const trigger = this.shadowRoot.querySelector('slot[name="trigger"]')?.assignedElements()[0];
|
|
1158
|
+
if (!trigger) return;
|
|
1159
|
+
const isHover = triggerType === "hover";
|
|
1160
|
+
trigger.setAttribute("aria-haspopup", isHover ? "true" : "dialog");
|
|
1161
|
+
trigger.setAttribute("aria-expanded", this.hasAttribute("open") ? "true" : "false");
|
|
1162
|
+
if (this._isDialog) {
|
|
1163
|
+
trigger.setAttribute("aria-controls", this._popoverId);
|
|
1164
|
+
}
|
|
1165
|
+
if (triggerType === "click") {
|
|
1166
|
+
trigger.onclick = () => this.toggle();
|
|
1167
|
+
} else if (triggerType === "hover") {
|
|
1168
|
+
this.onmouseenter = () => this.open();
|
|
1169
|
+
this.onmouseleave = () => this.close();
|
|
1170
|
+
this.onfocusin = () => this.open();
|
|
1171
|
+
this.onfocusout = (e) => {
|
|
1172
|
+
if (!this.contains(e.relatedTarget)) this.close();
|
|
1173
|
+
};
|
|
1174
|
+
} else if (triggerType === "focus") {
|
|
1175
|
+
trigger.onfocusin = () => this.open();
|
|
1176
|
+
trigger.onfocusout = () => this.close();
|
|
1177
|
+
}
|
|
1098
1178
|
}
|
|
1099
1179
|
open() {
|
|
1100
1180
|
this.setAttribute("open", "");
|
|
@@ -1102,6 +1182,14 @@
|
|
|
1102
1182
|
if (trigger) trigger.setAttribute("aria-expanded", "true");
|
|
1103
1183
|
document.addEventListener("click", this._onOutside, true);
|
|
1104
1184
|
document.addEventListener("keydown", this._onKey);
|
|
1185
|
+
if (this._isDialog) {
|
|
1186
|
+
this._prevFocus = saveFocus();
|
|
1187
|
+
requestAnimationFrame(() => {
|
|
1188
|
+
const focusable = getFocusableElements(this.shadowRoot.querySelector(".popover"));
|
|
1189
|
+
if (focusable.length) focusable[0].focus();
|
|
1190
|
+
else this.shadowRoot.querySelector(".popover")?.focus();
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1105
1193
|
}
|
|
1106
1194
|
close() {
|
|
1107
1195
|
this.removeAttribute("open");
|
|
@@ -1109,6 +1197,10 @@
|
|
|
1109
1197
|
if (trigger) trigger.setAttribute("aria-expanded", "false");
|
|
1110
1198
|
document.removeEventListener("click", this._onOutside, true);
|
|
1111
1199
|
document.removeEventListener("keydown", this._onKey);
|
|
1200
|
+
if (this._isDialog && this._prevFocus) {
|
|
1201
|
+
restoreFocus(this._prevFocus);
|
|
1202
|
+
this._prevFocus = null;
|
|
1203
|
+
}
|
|
1112
1204
|
}
|
|
1113
1205
|
toggle() {
|
|
1114
1206
|
this.hasAttribute("open") ? this.close() : this.open();
|
|
@@ -1117,7 +1209,15 @@
|
|
|
1117
1209
|
if (!this.contains(e.target)) this.close();
|
|
1118
1210
|
}
|
|
1119
1211
|
_onKey(e) {
|
|
1120
|
-
if (e.key === "Escape")
|
|
1212
|
+
if (e.key === "Escape") {
|
|
1213
|
+
this.close();
|
|
1214
|
+
const trigger = this.querySelector('[slot="trigger"]');
|
|
1215
|
+
if (trigger) trigger.focus();
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
if (this._isDialog && this.hasAttribute("open")) {
|
|
1219
|
+
trapFocus(this.shadowRoot.querySelector(".popover"), e);
|
|
1220
|
+
}
|
|
1121
1221
|
}
|
|
1122
1222
|
disconnectedCallback() {
|
|
1123
1223
|
document.removeEventListener("click", this._onOutside, true);
|
|
@@ -1281,9 +1381,15 @@
|
|
|
1281
1381
|
button:disabled { opacity: 0.3; cursor: not-allowed; }
|
|
1282
1382
|
svg { width: 1.25rem; height: 1.25rem; }
|
|
1283
1383
|
.indicators {
|
|
1284
|
-
display: flex; justify-content: center; gap: var(--velin-space-2, 0.5rem);
|
|
1384
|
+
display: flex; justify-content: center; align-items: center; gap: var(--velin-space-2, 0.5rem);
|
|
1285
1385
|
padding-block: var(--velin-space-3, 0.75rem);
|
|
1286
1386
|
}
|
|
1387
|
+
.pause-btn {
|
|
1388
|
+
font-size: var(--velin-text-xs, 0.75rem);
|
|
1389
|
+
padding-inline: var(--velin-space-3, 0.75rem);
|
|
1390
|
+
min-inline-size: auto;
|
|
1391
|
+
border-radius: var(--velin-radius-md, 0.375rem);
|
|
1392
|
+
}
|
|
1287
1393
|
.dot {
|
|
1288
1394
|
display: inline-flex; align-items: center; justify-content: center;
|
|
1289
1395
|
min-width: 2.75rem; min-height: 2.75rem;
|
|
@@ -1310,6 +1416,7 @@
|
|
|
1310
1416
|
this._index = 0;
|
|
1311
1417
|
this._timer = null;
|
|
1312
1418
|
this._startX = 0;
|
|
1419
|
+
this._autoplayPaused = false;
|
|
1313
1420
|
}
|
|
1314
1421
|
connectedCallback() {
|
|
1315
1422
|
this.shadowRoot.innerHTML = `
|
|
@@ -1323,7 +1430,7 @@
|
|
|
1323
1430
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 6 15 12 9 18"/></svg>
|
|
1324
1431
|
</button>
|
|
1325
1432
|
</div>
|
|
1326
|
-
<div class="indicators" role="
|
|
1433
|
+
<div class="indicators" role="group" aria-label="Slide indicators" part="indicators"></div>
|
|
1327
1434
|
`;
|
|
1328
1435
|
this.shadowRoot.querySelector(".prev").addEventListener("click", () => this.prev());
|
|
1329
1436
|
this.shadowRoot.querySelector(".next").addEventListener("click", () => this.next());
|
|
@@ -1402,12 +1509,37 @@
|
|
|
1402
1509
|
c.innerHTML = "";
|
|
1403
1510
|
this._slides.forEach((_, i) => {
|
|
1404
1511
|
const d = document.createElement("button");
|
|
1512
|
+
d.type = "button";
|
|
1405
1513
|
d.className = "dot";
|
|
1406
|
-
d.setAttribute("
|
|
1407
|
-
d.setAttribute("aria-label", `Slide ${i + 1}`);
|
|
1514
|
+
d.setAttribute("aria-label", `Go to slide ${i + 1}`);
|
|
1408
1515
|
d.addEventListener("click", () => this.goTo(i));
|
|
1409
1516
|
c.appendChild(d);
|
|
1410
1517
|
});
|
|
1518
|
+
if (this.hasAttribute("autoplay")) {
|
|
1519
|
+
const pause = document.createElement("button");
|
|
1520
|
+
pause.type = "button";
|
|
1521
|
+
pause.className = "pause-btn";
|
|
1522
|
+
pause.setAttribute("aria-pressed", "false");
|
|
1523
|
+
pause.setAttribute("aria-label", "Pause automatic slide show");
|
|
1524
|
+
pause.textContent = "Pause";
|
|
1525
|
+
pause.addEventListener("click", () => this._toggleAutoplayPause(pause));
|
|
1526
|
+
c.appendChild(pause);
|
|
1527
|
+
}
|
|
1528
|
+
this._update();
|
|
1529
|
+
}
|
|
1530
|
+
_toggleAutoplayPause(btn) {
|
|
1531
|
+
this._autoplayPaused = !this._autoplayPaused;
|
|
1532
|
+
if (this._autoplayPaused) {
|
|
1533
|
+
this._pause();
|
|
1534
|
+
btn.setAttribute("aria-pressed", "true");
|
|
1535
|
+
btn.setAttribute("aria-label", "Resume automatic slide show");
|
|
1536
|
+
btn.textContent = "Play";
|
|
1537
|
+
} else {
|
|
1538
|
+
this._resume();
|
|
1539
|
+
btn.setAttribute("aria-pressed", "false");
|
|
1540
|
+
btn.setAttribute("aria-label", "Pause automatic slide show");
|
|
1541
|
+
btn.textContent = "Pause";
|
|
1542
|
+
}
|
|
1411
1543
|
}
|
|
1412
1544
|
_emit() {
|
|
1413
1545
|
this.dispatchEvent(new CustomEvent("velin-slide-change", { bubbles: true, detail: { index: this._index } }));
|
|
@@ -1423,7 +1555,7 @@
|
|
|
1423
1555
|
}
|
|
1424
1556
|
}
|
|
1425
1557
|
_resume() {
|
|
1426
|
-
if (this.hasAttribute("autoplay") && !this._timer) this._startAutoplay();
|
|
1558
|
+
if (this.hasAttribute("autoplay") && !this._timer && !this._autoplayPaused) this._startAutoplay();
|
|
1427
1559
|
}
|
|
1428
1560
|
disconnectedCallback() {
|
|
1429
1561
|
this._pause();
|
|
@@ -1445,6 +1577,11 @@
|
|
|
1445
1577
|
.inner { min-height: 0; }
|
|
1446
1578
|
@media (prefers-reduced-motion: reduce) { .content { transition: none; } }
|
|
1447
1579
|
`;
|
|
1580
|
+
var collapseId = 0;
|
|
1581
|
+
function isButtonLike(el) {
|
|
1582
|
+
const tag = el.tagName;
|
|
1583
|
+
return tag === "BUTTON" || tag === "A" && el.hasAttribute("href") || el.getAttribute("role") === "button";
|
|
1584
|
+
}
|
|
1448
1585
|
var VelinCollapse = class extends HTMLElement {
|
|
1449
1586
|
static get observedAttributes() {
|
|
1450
1587
|
return ["open"];
|
|
@@ -1452,28 +1589,41 @@
|
|
|
1452
1589
|
constructor() {
|
|
1453
1590
|
super();
|
|
1454
1591
|
this.attachShadow({ mode: "open" });
|
|
1592
|
+
this._contentId = `velin-collapse-panel-${++collapseId}`;
|
|
1593
|
+
this._onTriggerKey = this._onTriggerKey.bind(this);
|
|
1455
1594
|
}
|
|
1456
1595
|
connectedCallback() {
|
|
1457
|
-
|
|
1458
|
-
<style
|
|
1459
|
-
<slot name="trigger"></slot>
|
|
1460
|
-
<div class="content" role="region" part="content">
|
|
1461
|
-
<div class="inner"><slot></slot></div>
|
|
1462
|
-
</div>
|
|
1463
|
-
`;
|
|
1596
|
+
const panelId = this._contentId;
|
|
1597
|
+
this.shadowRoot.innerHTML = "<style>" + styles12 + '</style><slot name="trigger"></slot><div class="content" id="' + panelId + '" part="content"><div class="inner"><slot></slot></div></div>';
|
|
1464
1598
|
const triggerSlot = this.shadowRoot.querySelector('slot[name="trigger"]');
|
|
1465
|
-
triggerSlot.addEventListener("slotchange", () =>
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1599
|
+
triggerSlot.addEventListener("slotchange", () => this._wireTrigger());
|
|
1600
|
+
this._wireTrigger();
|
|
1601
|
+
}
|
|
1602
|
+
_wireTrigger() {
|
|
1603
|
+
const trigger = this.shadowRoot.querySelector('slot[name="trigger"]')?.assignedElements()[0];
|
|
1604
|
+
if (!trigger) return;
|
|
1605
|
+
if (!isButtonLike(trigger)) {
|
|
1606
|
+
trigger.setAttribute("role", "button");
|
|
1607
|
+
if (!trigger.hasAttribute("tabindex")) trigger.setAttribute("tabindex", "0");
|
|
1608
|
+
}
|
|
1609
|
+
trigger.setAttribute("aria-controls", this._contentId);
|
|
1610
|
+
trigger.setAttribute("aria-expanded", this.hasAttribute("open") ? "true" : "false");
|
|
1611
|
+
trigger.removeEventListener("click", this._onClick);
|
|
1612
|
+
trigger.removeEventListener("keydown", this._onTriggerKey);
|
|
1613
|
+
this._onClick = () => this.toggle();
|
|
1614
|
+
trigger.addEventListener("click", this._onClick);
|
|
1615
|
+
trigger.addEventListener("keydown", this._onTriggerKey);
|
|
1616
|
+
}
|
|
1617
|
+
_onTriggerKey(e) {
|
|
1618
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1619
|
+
e.preventDefault();
|
|
1620
|
+
this.toggle();
|
|
1621
|
+
}
|
|
1472
1622
|
}
|
|
1473
1623
|
attributeChangedCallback(name) {
|
|
1474
1624
|
if (name === "open") {
|
|
1475
1625
|
const trigger = this.shadowRoot.querySelector('slot[name="trigger"]')?.assignedElements()[0];
|
|
1476
|
-
if (trigger) trigger.setAttribute("aria-expanded", this.hasAttribute("open"));
|
|
1626
|
+
if (trigger) trigger.setAttribute("aria-expanded", this.hasAttribute("open") ? "true" : "false");
|
|
1477
1627
|
}
|
|
1478
1628
|
}
|
|
1479
1629
|
toggle() {
|
|
@@ -1561,6 +1711,7 @@
|
|
|
1561
1711
|
.tip[data-placement="end"] { left: calc(100% + 6px); top: 50%; transform: translateY(-50%); }
|
|
1562
1712
|
@media (prefers-reduced-motion: reduce) { .tip { transition: none; } }
|
|
1563
1713
|
`;
|
|
1714
|
+
var tooltipId = 0;
|
|
1564
1715
|
var VelinTooltipWC = class extends HTMLElement {
|
|
1565
1716
|
static get observedAttributes() {
|
|
1566
1717
|
return ["content", "placement"];
|
|
@@ -1568,13 +1719,15 @@
|
|
|
1568
1719
|
constructor() {
|
|
1569
1720
|
super();
|
|
1570
1721
|
this.attachShadow({ mode: "open" });
|
|
1722
|
+
this._tipId = `velin-tooltip-${++tooltipId}`;
|
|
1571
1723
|
}
|
|
1572
1724
|
connectedCallback() {
|
|
1573
1725
|
const placement = this.getAttribute("placement") || "top";
|
|
1726
|
+
const D = "div";
|
|
1574
1727
|
this.shadowRoot.innerHTML = `
|
|
1575
1728
|
<style>${styles13}</style>
|
|
1576
1729
|
<slot></slot>
|
|
1577
|
-
|
|
1730
|
+
<${D} class="tip" id="${this._tipId}" role="tooltip" data-placement="${escapeHTML(placement)}" part="tip">${escapeHTML(this.getAttribute("content") || "")}</${D}>
|
|
1578
1731
|
`;
|
|
1579
1732
|
this.addEventListener("mouseenter", () => this._show());
|
|
1580
1733
|
this.addEventListener("mouseleave", () => this._hide());
|
|
@@ -1583,13 +1736,29 @@
|
|
|
1583
1736
|
this.addEventListener("keydown", (e) => {
|
|
1584
1737
|
if (e.key === "Escape") this._hide();
|
|
1585
1738
|
});
|
|
1739
|
+
const slot = this.shadowRoot.querySelector("slot");
|
|
1740
|
+
slot.addEventListener("slotchange", () => this._linkTrigger());
|
|
1741
|
+
this._linkTrigger();
|
|
1742
|
+
}
|
|
1743
|
+
_linkTrigger() {
|
|
1744
|
+
const trigger = this.shadowRoot.querySelector("slot")?.assignedElements()[0];
|
|
1745
|
+
if (!trigger) return;
|
|
1746
|
+
if (this.hasAttribute("visible")) {
|
|
1747
|
+
trigger.setAttribute("aria-describedby", this._tipId);
|
|
1748
|
+
} else {
|
|
1749
|
+
trigger.removeAttribute("aria-describedby");
|
|
1750
|
+
}
|
|
1586
1751
|
}
|
|
1587
1752
|
_show() {
|
|
1588
1753
|
this.setAttribute("visible", "");
|
|
1754
|
+
const trigger = this.shadowRoot.querySelector("slot")?.assignedElements()[0];
|
|
1755
|
+
if (trigger) trigger.setAttribute("aria-describedby", this._tipId);
|
|
1589
1756
|
this._flip();
|
|
1590
1757
|
}
|
|
1591
1758
|
_hide() {
|
|
1592
1759
|
this.removeAttribute("visible");
|
|
1760
|
+
const trigger = this.shadowRoot.querySelector("slot")?.assignedElements()[0];
|
|
1761
|
+
if (trigger) trigger.removeAttribute("aria-describedby");
|
|
1593
1762
|
}
|
|
1594
1763
|
_flip() {
|
|
1595
1764
|
const tip = this.shadowRoot.querySelector(".tip");
|