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