@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.
@@ -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 = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
@@ -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 > 0) {
376
- rovingTabindex(this, items, event);
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
- <div role="region" part="region"><slot></slot></div>
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-label="${safeTitle}" part="drawer">
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">&#215;</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
- const trigger = triggerSlot.assignedElements()[0];
1080
- if (!trigger) return;
1081
- trigger.setAttribute("aria-haspopup", role === "tooltip" ? "true" : "dialog");
1082
- trigger.setAttribute("aria-expanded", "false");
1083
- if (triggerType === "click") {
1084
- trigger.addEventListener("click", () => this.toggle());
1085
- } else if (triggerType === "hover") {
1086
- this.addEventListener("mouseenter", () => this.open());
1087
- this.addEventListener("mouseleave", () => this.close());
1088
- this.addEventListener("focusin", () => this.open());
1089
- this.addEventListener("focusout", (e) => {
1090
- if (!this.contains(e.relatedTarget)) this.close();
1091
- });
1092
- } else if (triggerType === "focus") {
1093
- trigger.addEventListener("focusin", () => this.open());
1094
- trigger.addEventListener("focusout", () => this.close());
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") this.close();
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="tablist" aria-label="Slide indicators" part="indicators"></div>
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("role", "tab");
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
- this.shadowRoot.innerHTML = `
1457
- <style>${styles12}</style>
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
- const trigger = triggerSlot.assignedElements()[0];
1466
- if (trigger) {
1467
- trigger.addEventListener("click", () => this.toggle());
1468
- trigger.setAttribute("aria-expanded", this.hasAttribute("open"));
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
- <div class="tip" role="tooltip" data-placement="${escapeHTML(placement)}" part="tip">${escapeHTML(this.getAttribute("content") || "")}</div>
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
  };