aria-ease 6.12.2 → 7.0.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/dist/AccordionComponentStrategy-2SWMNUR6.js +1 -0
- package/dist/ComboboxComponentStrategy-YSYLR2U5.js +5 -0
- package/dist/MenuComponentStrategy-C22BZEBH.js +5 -0
- package/dist/RelativeTargetResolver-T4P25J2M.js +1 -0
- package/dist/TabsComponentStrategy-ADEEFJXM.js +1 -0
- package/dist/audit-APAPHXRO.js +9 -0
- package/dist/badgeHelper-IB5RTMAG.js +11 -0
- package/dist/badgeHelper-JSROP5ML.js +1 -0
- package/dist/buildContracts-T4XQZBDU.js +13 -0
- package/dist/chunk-52I3INNG.js +11 -0
- package/dist/chunk-APUMBDOT.js +1 -0
- package/dist/chunk-BHNO4ZI3.js +1 -0
- package/dist/chunk-CNU4N4AY.js +1 -0
- package/dist/chunk-SM6ZKEDR.js +1 -0
- package/dist/chunk-ZNQ5BXVJ.js +1 -0
- package/dist/cli.cjs +132 -3494
- package/dist/cli.js +19 -161
- package/dist/configLoader-ZEJVXLX7.js +1 -0
- package/dist/configLoader-ZXTSCIP6.js +1 -0
- package/dist/contractTestRunnerPlaywright-FOCQTM4L.js +46 -0
- package/dist/contractTestRunnerPlaywright-QPU6HZXG.js +46 -0
- package/dist/formatters-H3CPDLG5.js +87 -0
- package/dist/index.cjs +64 -4657
- package/dist/index.d.cts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +17 -2351
- package/dist/src/accordion/index.cjs +1 -183
- package/dist/src/accordion/index.js +1 -181
- package/dist/src/block/index.cjs +1 -124
- package/dist/src/block/index.js +1 -122
- package/dist/src/checkbox/index.cjs +1 -109
- package/dist/src/checkbox/index.js +1 -107
- package/dist/src/combobox/index.cjs +1 -265
- package/dist/src/combobox/index.js +1 -263
- package/dist/src/menu/index.cjs +1 -339
- package/dist/src/menu/index.js +1 -337
- package/dist/src/radio/index.cjs +1 -117
- package/dist/src/radio/index.js +1 -115
- package/dist/src/tabs/index.cjs +1 -265
- package/dist/src/tabs/index.js +1 -263
- package/dist/src/toggle/index.cjs +1 -119
- package/dist/src/toggle/index.js +1 -117
- package/dist/src/utils/test/AccordionComponentStrategy-X2GSQ5KT.js +1 -0
- package/dist/src/utils/test/ComboboxComponentStrategy-SICWLI27.js +5 -0
- package/dist/src/utils/test/MenuComponentStrategy-R4VPAHDE.js +5 -0
- package/dist/src/utils/test/RelativeTargetResolver-UQQMZHI6.js +1 -0
- package/dist/src/utils/test/TabsComponentStrategy-L2PYNEW6.js +1 -0
- package/dist/src/utils/test/badgeHelper-ER5ZOHWF.js +11 -0
- package/dist/src/utils/test/chunk-APUMBDOT.js +1 -0
- package/dist/src/utils/test/chunk-BHNO4ZI3.js +1 -0
- package/dist/src/utils/test/configLoader-NCYRL2O6.js +1 -0
- package/dist/src/utils/test/contractTestRunnerPlaywright-YZCMF64Q.js +46 -0
- package/dist/src/utils/test/dsl/index.cjs +1 -486
- package/dist/src/utils/test/dsl/index.d.cts +21 -0
- package/dist/src/utils/test/dsl/index.d.ts +21 -0
- package/dist/src/utils/test/dsl/index.js +1 -484
- package/dist/src/utils/test/index.cjs +64 -2578
- package/dist/src/utils/test/index.d.cts +2 -2
- package/dist/src/utils/test/index.d.ts +2 -2
- package/dist/src/utils/test/index.js +16 -340
- package/dist/test-VXSCSKV5.js +19 -0
- package/package.json +7 -9
- package/dist/AccordionComponentStrategy-4ZEIQ2V6.js +0 -42
- package/dist/ComboboxComponentStrategy-DU342VMB.js +0 -64
- package/dist/MenuComponentStrategy-JAMTCSNF.js +0 -81
- package/dist/RelativeTargetResolver-DJAITO6D.js +0 -7
- package/dist/TabsComponentStrategy-3SQURPMX.js +0 -29
- package/dist/audit-JYEPKLHR.js +0 -63
- package/dist/badgeHelper-JOWO6RQG.js +0 -15
- package/dist/badgeHelper-RDOMCC6E.js +0 -108
- package/dist/buildContracts-FT6KWUJN.js +0 -465
- package/dist/chunk-4DU5Z5BR.js +0 -340
- package/dist/chunk-GJGUY643.js +0 -182
- package/dist/chunk-GLT43UVH.js +0 -43
- package/dist/chunk-I2KLQ2HA.js +0 -22
- package/dist/chunk-JJEPLK7L.js +0 -107
- package/dist/chunk-PK5L2SAF.js +0 -17
- package/dist/configLoader-Q7N5XV4P.js +0 -183
- package/dist/configLoader-REHK3S3Q.js +0 -7
- package/dist/contractTestRunnerPlaywright-47DCBO4A.js +0 -1300
- package/dist/contractTestRunnerPlaywright-UJKXRXBS.js +0 -1300
- package/dist/formatters-32KQIIYS.js +0 -183
- package/dist/src/utils/test/AccordionComponentStrategy-WRHZOEN6.js +0 -38
- package/dist/src/utils/test/ComboboxComponentStrategy-XKQ72RFD.js +0 -60
- package/dist/src/utils/test/MenuComponentStrategy-VKZQYLBE.js +0 -77
- package/dist/src/utils/test/RelativeTargetResolver-G2XDN2VV.js +0 -1
- package/dist/src/utils/test/TabsComponentStrategy-BKG53SEV.js +0 -26
- package/dist/src/utils/test/badgeHelper-HZKGOPB4.js +0 -102
- package/dist/src/utils/test/chunk-4DU5Z5BR.js +0 -332
- package/dist/src/utils/test/chunk-GLT43UVH.js +0 -41
- package/dist/src/utils/test/configLoader-NA7IBCS3.js +0 -181
- package/dist/src/utils/test/contractTestRunnerPlaywright-AZ4QKLYT.js +0 -1278
- package/dist/test-6Y4CIQOM.js +0 -358
package/dist/src/menu/index.js
CHANGED
|
@@ -1,337 +1 @@
|
|
|
1
|
-
|
|
2
|
-
function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
3
|
-
const menuDiv = document.querySelector(`#${menuId}`);
|
|
4
|
-
if (!menuDiv) {
|
|
5
|
-
console.error(`[aria-ease] Element with id="${menuId}" not found. Make sure the menu element exists before calling makeMenuAccessible.`);
|
|
6
|
-
return { openMenu: () => {
|
|
7
|
-
}, closeMenu: () => {
|
|
8
|
-
}, cleanup: () => {
|
|
9
|
-
} };
|
|
10
|
-
}
|
|
11
|
-
const triggerButton = document.querySelector(`#${triggerId}`);
|
|
12
|
-
if (!triggerButton) {
|
|
13
|
-
console.error(`[aria-ease] Element with id="${triggerId}" not found. Make sure the trigger button element exists before calling makeMenuAccessible.`);
|
|
14
|
-
return { openMenu: () => {
|
|
15
|
-
}, closeMenu: () => {
|
|
16
|
-
}, cleanup: () => {
|
|
17
|
-
} };
|
|
18
|
-
}
|
|
19
|
-
if (!/^[\w-]+$/.test(menuId)) {
|
|
20
|
-
console.error("[aria-ease] Invalid menuId: must be alphanumeric");
|
|
21
|
-
return { openMenu: () => {
|
|
22
|
-
}, closeMenu: () => {
|
|
23
|
-
}, cleanup: () => {
|
|
24
|
-
} };
|
|
25
|
-
}
|
|
26
|
-
triggerButton.setAttribute("aria-haspopup", "true");
|
|
27
|
-
triggerButton.setAttribute("aria-controls", menuId);
|
|
28
|
-
triggerButton.setAttribute("aria-expanded", "false");
|
|
29
|
-
menuDiv.setAttribute("role", "menu");
|
|
30
|
-
menuDiv.setAttribute("aria-labelledby", triggerId);
|
|
31
|
-
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
32
|
-
const submenuInstances = /* @__PURE__ */ new Map();
|
|
33
|
-
let cachedItems = null;
|
|
34
|
-
let filteredItems = null;
|
|
35
|
-
function getItems() {
|
|
36
|
-
if (!cachedItems) {
|
|
37
|
-
cachedItems = menuDiv.querySelectorAll(`.${menuItemsClass}`);
|
|
38
|
-
}
|
|
39
|
-
return cachedItems;
|
|
40
|
-
}
|
|
41
|
-
function getFilteredItems() {
|
|
42
|
-
if (!filteredItems) {
|
|
43
|
-
const allItems = getItems();
|
|
44
|
-
filteredItems = [];
|
|
45
|
-
for (let i = 0; i < allItems.length; i++) {
|
|
46
|
-
const item = allItems.item(i);
|
|
47
|
-
const isNested = isItemInNestedSubmenu(item);
|
|
48
|
-
const isDisabled = item.getAttribute("aria-disabled") === "true";
|
|
49
|
-
if (!isNested) {
|
|
50
|
-
if (!item.hasAttribute("tabindex")) {
|
|
51
|
-
item.setAttribute("tabindex", "-1");
|
|
52
|
-
}
|
|
53
|
-
if (!isDisabled) {
|
|
54
|
-
filteredItems.push(item);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return filteredItems;
|
|
60
|
-
}
|
|
61
|
-
function toNodeListLike(items) {
|
|
62
|
-
const nodeListLike = {
|
|
63
|
-
length: items.length,
|
|
64
|
-
item: (index) => items[index],
|
|
65
|
-
forEach: (callback2) => {
|
|
66
|
-
items.forEach(callback2);
|
|
67
|
-
},
|
|
68
|
-
[Symbol.iterator]: function* () {
|
|
69
|
-
for (const item of items) {
|
|
70
|
-
yield item;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
return nodeListLike;
|
|
75
|
-
}
|
|
76
|
-
function intializeMenuItems() {
|
|
77
|
-
const items = getItems();
|
|
78
|
-
items.forEach((item) => {
|
|
79
|
-
item.setAttribute("role", "menuitem");
|
|
80
|
-
const submenuId = item.getAttribute("data-submenu-id") ?? item.getAttribute("aria-controls");
|
|
81
|
-
const hasSubmenuTriggerAttributes = item.hasAttribute("aria-haspopup") && submenuId;
|
|
82
|
-
if (submenuId && (item.hasAttribute("data-submenu-id") || hasSubmenuTriggerAttributes)) {
|
|
83
|
-
item.setAttribute("aria-haspopup", "menu");
|
|
84
|
-
item.setAttribute("aria-controls", submenuId);
|
|
85
|
-
if (!item.hasAttribute("aria-expanded")) {
|
|
86
|
-
item.setAttribute("aria-expanded", "false");
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
function moveFocus(elementItems, currentIndex, direction) {
|
|
92
|
-
const len = elementItems.length;
|
|
93
|
-
const nextIndex = (currentIndex + direction + len) % len;
|
|
94
|
-
elementItems.item(nextIndex).focus();
|
|
95
|
-
}
|
|
96
|
-
function focusItemAtIndex(items, index) {
|
|
97
|
-
if (items.length === 0) return;
|
|
98
|
-
items[index]?.focus();
|
|
99
|
-
}
|
|
100
|
-
function hasSubmenu(menuItem) {
|
|
101
|
-
return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
|
|
102
|
-
}
|
|
103
|
-
function closeAncestorMenusFromTrigger(triggerEl) {
|
|
104
|
-
let currentTrigger = triggerEl;
|
|
105
|
-
while (currentTrigger && currentTrigger.getAttribute("role") === "menuitem") {
|
|
106
|
-
const parentMenu = currentTrigger.closest('[role="menu"]');
|
|
107
|
-
if (!parentMenu) break;
|
|
108
|
-
parentMenu.style.display = "none";
|
|
109
|
-
currentTrigger.setAttribute("aria-expanded", "false");
|
|
110
|
-
const parentTriggerId = parentMenu.getAttribute("aria-labelledby");
|
|
111
|
-
if (!parentTriggerId) break;
|
|
112
|
-
const nextTrigger = document.getElementById(parentTriggerId);
|
|
113
|
-
if (!nextTrigger) break;
|
|
114
|
-
currentTrigger = nextTrigger;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
intializeMenuItems();
|
|
118
|
-
function handleItemsKeydown(event, menuItem, menuItemIndex) {
|
|
119
|
-
switch (event.key) {
|
|
120
|
-
case "ArrowLeft": {
|
|
121
|
-
if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
|
|
122
|
-
event.preventDefault();
|
|
123
|
-
closeMenu();
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
case "ArrowUp": {
|
|
129
|
-
event.preventDefault();
|
|
130
|
-
moveFocus(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
case "ArrowRight": {
|
|
134
|
-
if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
|
|
135
|
-
event.preventDefault();
|
|
136
|
-
const submenuId = menuItem.getAttribute("aria-controls");
|
|
137
|
-
if (submenuId) {
|
|
138
|
-
openSubmenu(submenuId);
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
break;
|
|
143
|
-
}
|
|
144
|
-
case "ArrowDown": {
|
|
145
|
-
event.preventDefault();
|
|
146
|
-
moveFocus(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
case "Home": {
|
|
150
|
-
event.preventDefault();
|
|
151
|
-
focusItemAtIndex(getFilteredItems(), 0);
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
case "End": {
|
|
155
|
-
event.preventDefault();
|
|
156
|
-
const items = getFilteredItems();
|
|
157
|
-
focusItemAtIndex(items, items.length - 1);
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
case "Escape": {
|
|
161
|
-
event.preventDefault();
|
|
162
|
-
closeMenu();
|
|
163
|
-
triggerButton.focus();
|
|
164
|
-
if (onOpenChange) {
|
|
165
|
-
onOpenChange(false);
|
|
166
|
-
}
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
case "Enter":
|
|
170
|
-
case " ": {
|
|
171
|
-
event.preventDefault();
|
|
172
|
-
if (hasSubmenu(menuItem)) {
|
|
173
|
-
const submenuId = menuItem.getAttribute("aria-controls");
|
|
174
|
-
if (submenuId) {
|
|
175
|
-
openSubmenu(submenuId);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
menuItem.click();
|
|
180
|
-
closeMenu();
|
|
181
|
-
if (onOpenChange) {
|
|
182
|
-
onOpenChange(false);
|
|
183
|
-
}
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
case "Tab": {
|
|
187
|
-
closeMenu();
|
|
188
|
-
closeAncestorMenusFromTrigger(triggerButton);
|
|
189
|
-
if (onOpenChange) {
|
|
190
|
-
onOpenChange(false);
|
|
191
|
-
}
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
function isItemInNestedSubmenu(item) {
|
|
197
|
-
let parent = item.parentElement;
|
|
198
|
-
while (parent && parent !== menuDiv) {
|
|
199
|
-
if (parent.getAttribute("role") === "menu") {
|
|
200
|
-
return true;
|
|
201
|
-
}
|
|
202
|
-
if (parent.id) {
|
|
203
|
-
const parentMenuTrigger = menuDiv.querySelector(`[aria-controls="${parent.id}"]`);
|
|
204
|
-
if (parentMenuTrigger) {
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
parent = parent.parentElement;
|
|
209
|
-
}
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
function setAria(isOpen) {
|
|
213
|
-
triggerButton.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
214
|
-
}
|
|
215
|
-
function openSubmenu(submenuId) {
|
|
216
|
-
let submenuInstance = submenuInstances.get(submenuId);
|
|
217
|
-
if (!submenuInstance) {
|
|
218
|
-
const submenuTrigger = menuDiv.querySelector(`[aria-controls="${submenuId}"]`);
|
|
219
|
-
if (!submenuTrigger) {
|
|
220
|
-
console.error(`[aria-ease] Submenu trigger with aria-controls="${submenuId}" not found in menu "${menuId}".`);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
if (!submenuTrigger.id) {
|
|
224
|
-
const generatedId = `trigger-${submenuId}`;
|
|
225
|
-
submenuTrigger.id = generatedId;
|
|
226
|
-
console.warn(`[aria-ease] Submenu trigger for "${submenuId}" had no ID. Auto-generated ID: "${generatedId}".`);
|
|
227
|
-
}
|
|
228
|
-
const submenuElement = document.querySelector(`#${submenuId}`);
|
|
229
|
-
if (!submenuElement) {
|
|
230
|
-
console.error(`[aria-ease] Submenu element with id="${submenuId}" not found. Cannot create submenu instance.`);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
submenuInstance = makeMenuAccessible({
|
|
234
|
-
menuId: submenuId,
|
|
235
|
-
menuItemsClass,
|
|
236
|
-
triggerId: submenuTrigger.id,
|
|
237
|
-
callback
|
|
238
|
-
});
|
|
239
|
-
submenuInstances.set(submenuId, submenuInstance);
|
|
240
|
-
}
|
|
241
|
-
submenuInstance.openMenu();
|
|
242
|
-
}
|
|
243
|
-
function onOpenChange(isOpen) {
|
|
244
|
-
if (callback?.onOpenChange) {
|
|
245
|
-
try {
|
|
246
|
-
callback.onOpenChange(isOpen);
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error("[aria-ease] Error in menu onOpenChange callback:", error);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
function addListeners() {
|
|
253
|
-
const items = getFilteredItems();
|
|
254
|
-
items.forEach((menuItem, index) => {
|
|
255
|
-
if (!handlerMap.has(menuItem)) {
|
|
256
|
-
const handler = (event) => handleItemsKeydown(event, menuItem, index);
|
|
257
|
-
menuItem.addEventListener("keydown", handler);
|
|
258
|
-
handlerMap.set(menuItem, handler);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
function removeListeners() {
|
|
263
|
-
const items = getFilteredItems();
|
|
264
|
-
items.forEach((menuItem) => {
|
|
265
|
-
const handler = handlerMap.get(menuItem);
|
|
266
|
-
if (handler) {
|
|
267
|
-
menuItem.removeEventListener("keydown", handler);
|
|
268
|
-
handlerMap.delete(menuItem);
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
function openMenu() {
|
|
273
|
-
setAria(true);
|
|
274
|
-
menuDiv.style.display = "block";
|
|
275
|
-
const items = getFilteredItems();
|
|
276
|
-
addListeners();
|
|
277
|
-
if (items && items.length > 0) {
|
|
278
|
-
items[0].focus();
|
|
279
|
-
}
|
|
280
|
-
if (callback?.onOpenChange) {
|
|
281
|
-
try {
|
|
282
|
-
callback.onOpenChange(true);
|
|
283
|
-
} catch (error) {
|
|
284
|
-
console.error("[aria-ease] Error in menu onOpenChange callback:", error);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
function closeMenu() {
|
|
289
|
-
submenuInstances.forEach((instance) => instance.closeMenu());
|
|
290
|
-
setAria(false);
|
|
291
|
-
menuDiv.style.display = "none";
|
|
292
|
-
removeListeners();
|
|
293
|
-
triggerButton.focus();
|
|
294
|
-
if (callback?.onOpenChange) {
|
|
295
|
-
try {
|
|
296
|
-
callback.onOpenChange(false);
|
|
297
|
-
} catch (error) {
|
|
298
|
-
console.error("[aria-ease] Error in menu onOpenChange callback:", error);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
function handleTriggerClick() {
|
|
303
|
-
const isOpen = triggerButton.getAttribute("aria-expanded") === "true";
|
|
304
|
-
if (isOpen) {
|
|
305
|
-
closeMenu();
|
|
306
|
-
} else {
|
|
307
|
-
openMenu();
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
function handleClickOutside(event) {
|
|
311
|
-
const isMenuOpen = triggerButton.getAttribute("aria-expanded") === "true";
|
|
312
|
-
if (!isMenuOpen) return;
|
|
313
|
-
const clickedTrigger = triggerButton.contains(event.target);
|
|
314
|
-
const clickedMenu = menuDiv.contains(event.target);
|
|
315
|
-
if (!clickedTrigger && !clickedMenu) {
|
|
316
|
-
closeMenu();
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
triggerButton.addEventListener("click", handleTriggerClick);
|
|
320
|
-
document.addEventListener("click", handleClickOutside);
|
|
321
|
-
function cleanup() {
|
|
322
|
-
removeListeners();
|
|
323
|
-
triggerButton.removeEventListener("click", handleTriggerClick);
|
|
324
|
-
document.removeEventListener("click", handleClickOutside);
|
|
325
|
-
menuDiv.style.display = "none";
|
|
326
|
-
setAria(false);
|
|
327
|
-
submenuInstances.forEach((instance) => instance.cleanup());
|
|
328
|
-
submenuInstances.clear();
|
|
329
|
-
}
|
|
330
|
-
function refresh() {
|
|
331
|
-
cachedItems = null;
|
|
332
|
-
filteredItems = null;
|
|
333
|
-
}
|
|
334
|
-
return { openMenu, closeMenu, cleanup, refresh };
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export { makeMenuAccessible };
|
|
1
|
+
function F({menuId:f,menuItemsClass:E,triggerId:p,callback:s}){let o=document.querySelector(`#${f}`);if(!o)return console.error(`[aria-ease] Element with id="${f}" not found. Make sure the menu element exists before calling makeMenuAccessible.`),{openMenu:()=>{},closeMenu:()=>{},cleanup:()=>{}};let i=document.querySelector(`#${p}`);if(!i)return console.error(`[aria-ease] Element with id="${p}" not found. Make sure the trigger button element exists before calling makeMenuAccessible.`),{openMenu:()=>{},closeMenu:()=>{},cleanup:()=>{}};if(!/^[\w-]+$/.test(f))return console.error("[aria-ease] Invalid menuId: must be alphanumeric"),{openMenu:()=>{},closeMenu:()=>{},cleanup:()=>{}};i.setAttribute("aria-haspopup","true"),i.setAttribute("aria-controls",f),i.setAttribute("aria-expanded","false"),o.setAttribute("role","menu"),o.setAttribute("aria-labelledby",p);let g=new WeakMap,d=new Map,m=null,b=null;function M(){return m||(m=o.querySelectorAll(`.${E}`)),m}function u(){if(!b){let e=M();b=[];for(let t=0;t<e.length;t++){let n=e.item(t),r=S(n),a=n.getAttribute("aria-disabled")==="true";r||(n.hasAttribute("tabindex")||n.setAttribute("tabindex","-1"),a||b.push(n));}}return b}function A(e){return {length:e.length,item:n=>e[n],forEach:n=>{e.forEach(n);},[Symbol.iterator]:function*(){for(let n of e)yield n;}}}function C(){M().forEach(t=>{t.setAttribute("role","menuitem");let n=t.getAttribute("data-submenu-id")??t.getAttribute("aria-controls"),r=t.hasAttribute("aria-haspopup")&&n;n&&(t.hasAttribute("data-submenu-id")||r)&&(t.setAttribute("aria-haspopup","menu"),t.setAttribute("aria-controls",n),t.hasAttribute("aria-expanded")||t.setAttribute("aria-expanded","false"));});}function L(e,t,n){let r=e.length,a=(t+n+r)%r;e.item(a).focus();}function k(e,t){e.length!==0&&e[t]?.focus();}function T(e){return e.hasAttribute("aria-controls")&&e.hasAttribute("aria-haspopup")&&e.getAttribute("role")==="menuitem"}function $(e){let t=e;for(;t&&t.getAttribute("role")==="menuitem";){let n=t.closest('[role="menu"]');if(!n)break;n.style.display="none",t.setAttribute("aria-expanded","false");let r=n.getAttribute("aria-labelledby");if(!r)break;let a=document.getElementById(r);if(!a)break;t=a;}}C();function v(e,t,n){switch(e.key){case "ArrowLeft":{if(e.key==="ArrowLeft"&&i.getAttribute("role")==="menuitem"){e.preventDefault(),l();return}break}case "ArrowUp":{e.preventDefault(),L(A(u()),n,-1);break}case "ArrowRight":{if(e.key==="ArrowRight"&&T(t)){e.preventDefault();let r=t.getAttribute("aria-controls");if(r){y(r);return}}break}case "ArrowDown":{e.preventDefault(),L(A(u()),n,1);break}case "Home":{e.preventDefault(),k(u(),0);break}case "End":{e.preventDefault();let r=u();k(r,r.length-1);break}case "Escape":{e.preventDefault(),l(),i.focus(),c&&c(false);break}case "Enter":case " ":{if(e.preventDefault(),T(t)){let r=t.getAttribute("aria-controls");if(r){y(r);return}}t.click(),l(),c&&c(false);break}case "Tab":{l(),$(i),c&&c(false);break}}}function S(e){let t=e.parentElement;for(;t&&t!==o;){if(t.getAttribute("role")==="menu"||t.id&&o.querySelector(`[aria-controls="${t.id}"]`))return true;t=t.parentElement;}return false}function h(e){i.setAttribute("aria-expanded",e?"true":"false");}function y(e){let t=d.get(e);if(!t){let n=o.querySelector(`[aria-controls="${e}"]`);if(!n){console.error(`[aria-ease] Submenu trigger with aria-controls="${e}" not found in menu "${f}".`);return}if(!n.id){let a=`trigger-${e}`;n.id=a,console.warn(`[aria-ease] Submenu trigger for "${e}" had no ID. Auto-generated ID: "${a}".`);}if(!document.querySelector(`#${e}`)){console.error(`[aria-ease] Submenu element with id="${e}" not found. Cannot create submenu instance.`);return}t=F({menuId:e,menuItemsClass:E,triggerId:n.id,callback:s}),d.set(e,t);}t.openMenu();}function c(e){if(s?.onOpenChange)try{s.onOpenChange(e);}catch(t){console.error("[aria-ease] Error in menu onOpenChange callback:",t);}}function D(){u().forEach((t,n)=>{if(!g.has(t)){let r=a=>v(a,t,n);t.addEventListener("keydown",r),g.set(t,r);}});}function H(){u().forEach(t=>{let n=g.get(t);n&&(t.removeEventListener("keydown",n),g.delete(t));});}function w(){h(true),o.style.display="block";let e=u();if(D(),e&&e.length>0&&e[0].focus(),s?.onOpenChange)try{s.onOpenChange(!0);}catch(t){console.error("[aria-ease] Error in menu onOpenChange callback:",t);}}function l(){if(d.forEach(e=>e.closeMenu()),h(false),o.style.display="none",H(),i.focus(),s?.onOpenChange)try{s.onOpenChange(!1);}catch(e){console.error("[aria-ease] Error in menu onOpenChange callback:",e);}}function O(){i.getAttribute("aria-expanded")==="true"?l():w();}function x(e){if(!(i.getAttribute("aria-expanded")==="true"))return;let n=i.contains(e.target),r=o.contains(e.target);!n&&!r&&l();}i.addEventListener("click",O),document.addEventListener("click",x);function N(){H(),i.removeEventListener("click",O),document.removeEventListener("click",x),o.style.display="none",h(false),d.forEach(e=>e.cleanup()),d.clear();}function q(){m=null,b=null;}return {openMenu:w,closeMenu:l,cleanup:N,refresh:q}}export{F as makeMenuAccessible};
|
package/dist/src/radio/index.cjs
CHANGED
|
@@ -1,117 +1 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// src/radio/src/makeRadioAccessible/makeRadioAccessible.ts
|
|
4
|
-
function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex = 0 }) {
|
|
5
|
-
const radioGroup = document.querySelector(`#${radioGroupId}`);
|
|
6
|
-
if (!radioGroup) {
|
|
7
|
-
console.error(`[aria-ease] Element with id="${radioGroupId}" not found. Make sure the radio group container exists before calling makeRadioAccessible.`);
|
|
8
|
-
return { cleanup: () => {
|
|
9
|
-
} };
|
|
10
|
-
}
|
|
11
|
-
const radios = Array.from(radioGroup.querySelectorAll(`.${radiosClass}`));
|
|
12
|
-
if (radios.length === 0) {
|
|
13
|
-
console.error(`[aria-ease] No elements with class="${radiosClass}" found. Make sure radio buttons exist before calling makeRadioAccessible.`);
|
|
14
|
-
return { cleanup: () => {
|
|
15
|
-
} };
|
|
16
|
-
}
|
|
17
|
-
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
18
|
-
const clickHandlerMap = /* @__PURE__ */ new WeakMap();
|
|
19
|
-
let currentSelectedIndex = defaultSelectedIndex;
|
|
20
|
-
function initialize() {
|
|
21
|
-
if (!radioGroup.getAttribute("role")) {
|
|
22
|
-
radioGroup.setAttribute("role", "radiogroup");
|
|
23
|
-
}
|
|
24
|
-
radios.forEach((radio, index) => {
|
|
25
|
-
radio.setAttribute("role", "radio");
|
|
26
|
-
radio.setAttribute("tabindex", index === currentSelectedIndex ? "0" : "-1");
|
|
27
|
-
if (index === currentSelectedIndex) {
|
|
28
|
-
radio.setAttribute("aria-checked", "true");
|
|
29
|
-
} else {
|
|
30
|
-
radio.setAttribute("aria-checked", "false");
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
function selectRadio(index) {
|
|
35
|
-
if (index < 0 || index >= radios.length) {
|
|
36
|
-
console.error(`[aria-ease] Invalid radio index: ${index}`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (currentSelectedIndex >= 0 && currentSelectedIndex < radios.length) {
|
|
40
|
-
radios[currentSelectedIndex].setAttribute("aria-checked", "false");
|
|
41
|
-
radios[currentSelectedIndex].setAttribute("tabindex", "-1");
|
|
42
|
-
}
|
|
43
|
-
radios[index].setAttribute("aria-checked", "true");
|
|
44
|
-
radios[index].setAttribute("tabindex", "0");
|
|
45
|
-
radios[index].focus();
|
|
46
|
-
currentSelectedIndex = index;
|
|
47
|
-
}
|
|
48
|
-
function handleRadioClick(index) {
|
|
49
|
-
return () => {
|
|
50
|
-
selectRadio(index);
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function handleRadioKeydown(index) {
|
|
54
|
-
return (event) => {
|
|
55
|
-
const { key } = event;
|
|
56
|
-
let nextIndex = index;
|
|
57
|
-
switch (key) {
|
|
58
|
-
case "ArrowDown":
|
|
59
|
-
case "ArrowRight":
|
|
60
|
-
event.preventDefault();
|
|
61
|
-
nextIndex = (index + 1) % radios.length;
|
|
62
|
-
selectRadio(nextIndex);
|
|
63
|
-
break;
|
|
64
|
-
case "ArrowUp":
|
|
65
|
-
case "ArrowLeft":
|
|
66
|
-
event.preventDefault();
|
|
67
|
-
nextIndex = (index - 1 + radios.length) % radios.length;
|
|
68
|
-
selectRadio(nextIndex);
|
|
69
|
-
break;
|
|
70
|
-
case " ":
|
|
71
|
-
case "Enter":
|
|
72
|
-
event.preventDefault();
|
|
73
|
-
selectRadio(index);
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
function addListeners() {
|
|
79
|
-
radios.forEach((radio, index) => {
|
|
80
|
-
const clickHandler = handleRadioClick(index);
|
|
81
|
-
const keydownHandler = handleRadioKeydown(index);
|
|
82
|
-
radio.addEventListener("click", clickHandler);
|
|
83
|
-
radio.addEventListener("keydown", keydownHandler);
|
|
84
|
-
handlerMap.set(radio, keydownHandler);
|
|
85
|
-
clickHandlerMap.set(radio, clickHandler);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
function removeListeners() {
|
|
89
|
-
radios.forEach((radio) => {
|
|
90
|
-
const keydownHandler = handlerMap.get(radio);
|
|
91
|
-
const clickHandler = clickHandlerMap.get(radio);
|
|
92
|
-
if (keydownHandler) {
|
|
93
|
-
radio.removeEventListener("keydown", keydownHandler);
|
|
94
|
-
handlerMap.delete(radio);
|
|
95
|
-
}
|
|
96
|
-
if (clickHandler) {
|
|
97
|
-
radio.removeEventListener("click", clickHandler);
|
|
98
|
-
clickHandlerMap.delete(radio);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
function cleanup() {
|
|
103
|
-
removeListeners();
|
|
104
|
-
}
|
|
105
|
-
function getSelectedIndex() {
|
|
106
|
-
return currentSelectedIndex;
|
|
107
|
-
}
|
|
108
|
-
initialize();
|
|
109
|
-
addListeners();
|
|
110
|
-
return {
|
|
111
|
-
selectRadio,
|
|
112
|
-
getSelectedIndex,
|
|
113
|
-
cleanup
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
exports.makeRadioAccessible = makeRadioAccessible;
|
|
1
|
+
'use strict';function E({radioGroupId:u,radiosClass:d,defaultSelectedIndex:f=0}){let i=document.querySelector(`#${u}`);if(!i)return console.error(`[aria-ease] Element with id="${u}" not found. Make sure the radio group container exists before calling makeRadioAccessible.`),{cleanup:()=>{}};let t=Array.from(i.querySelectorAll(`.${d}`));if(t.length===0)return console.error(`[aria-ease] No elements with class="${d}" found. Make sure radio buttons exist before calling makeRadioAccessible.`),{cleanup:()=>{}};let s=new WeakMap,l=new WeakMap,r=f;function b(){i.getAttribute("role")||i.setAttribute("role","radiogroup"),t.forEach((e,n)=>{e.setAttribute("role","radio"),e.setAttribute("tabindex",n===r?"0":"-1"),n===r?e.setAttribute("aria-checked","true"):e.setAttribute("aria-checked","false");});}function o(e){if(e<0||e>=t.length){console.error(`[aria-ease] Invalid radio index: ${e}`);return}r>=0&&r<t.length&&(t[r].setAttribute("aria-checked","false"),t[r].setAttribute("tabindex","-1")),t[e].setAttribute("aria-checked","true"),t[e].setAttribute("tabindex","0"),t[e].focus(),r=e;}function k(e){return ()=>{o(e);}}function m(e){return n=>{let{key:a}=n,c=e;switch(a){case "ArrowDown":case "ArrowRight":n.preventDefault(),c=(e+1)%t.length,o(c);break;case "ArrowUp":case "ArrowLeft":n.preventDefault(),c=(e-1+t.length)%t.length,o(c);break;case " ":case "Enter":n.preventDefault(),o(e);break}}}function h(){t.forEach((e,n)=>{let a=k(n),c=m(n);e.addEventListener("click",a),e.addEventListener("keydown",c),s.set(e,c),l.set(e,a);});}function A(){t.forEach(e=>{let n=s.get(e),a=l.get(e);n&&(e.removeEventListener("keydown",n),s.delete(e)),a&&(e.removeEventListener("click",a),l.delete(e));});}function g(){A();}function p(){return r}return b(),h(),{selectRadio:o,getSelectedIndex:p,cleanup:g}}exports.makeRadioAccessible=E;
|
package/dist/src/radio/index.js
CHANGED
|
@@ -1,115 +1 @@
|
|
|
1
|
-
|
|
2
|
-
function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex = 0 }) {
|
|
3
|
-
const radioGroup = document.querySelector(`#${radioGroupId}`);
|
|
4
|
-
if (!radioGroup) {
|
|
5
|
-
console.error(`[aria-ease] Element with id="${radioGroupId}" not found. Make sure the radio group container exists before calling makeRadioAccessible.`);
|
|
6
|
-
return { cleanup: () => {
|
|
7
|
-
} };
|
|
8
|
-
}
|
|
9
|
-
const radios = Array.from(radioGroup.querySelectorAll(`.${radiosClass}`));
|
|
10
|
-
if (radios.length === 0) {
|
|
11
|
-
console.error(`[aria-ease] No elements with class="${radiosClass}" found. Make sure radio buttons exist before calling makeRadioAccessible.`);
|
|
12
|
-
return { cleanup: () => {
|
|
13
|
-
} };
|
|
14
|
-
}
|
|
15
|
-
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
16
|
-
const clickHandlerMap = /* @__PURE__ */ new WeakMap();
|
|
17
|
-
let currentSelectedIndex = defaultSelectedIndex;
|
|
18
|
-
function initialize() {
|
|
19
|
-
if (!radioGroup.getAttribute("role")) {
|
|
20
|
-
radioGroup.setAttribute("role", "radiogroup");
|
|
21
|
-
}
|
|
22
|
-
radios.forEach((radio, index) => {
|
|
23
|
-
radio.setAttribute("role", "radio");
|
|
24
|
-
radio.setAttribute("tabindex", index === currentSelectedIndex ? "0" : "-1");
|
|
25
|
-
if (index === currentSelectedIndex) {
|
|
26
|
-
radio.setAttribute("aria-checked", "true");
|
|
27
|
-
} else {
|
|
28
|
-
radio.setAttribute("aria-checked", "false");
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
function selectRadio(index) {
|
|
33
|
-
if (index < 0 || index >= radios.length) {
|
|
34
|
-
console.error(`[aria-ease] Invalid radio index: ${index}`);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (currentSelectedIndex >= 0 && currentSelectedIndex < radios.length) {
|
|
38
|
-
radios[currentSelectedIndex].setAttribute("aria-checked", "false");
|
|
39
|
-
radios[currentSelectedIndex].setAttribute("tabindex", "-1");
|
|
40
|
-
}
|
|
41
|
-
radios[index].setAttribute("aria-checked", "true");
|
|
42
|
-
radios[index].setAttribute("tabindex", "0");
|
|
43
|
-
radios[index].focus();
|
|
44
|
-
currentSelectedIndex = index;
|
|
45
|
-
}
|
|
46
|
-
function handleRadioClick(index) {
|
|
47
|
-
return () => {
|
|
48
|
-
selectRadio(index);
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function handleRadioKeydown(index) {
|
|
52
|
-
return (event) => {
|
|
53
|
-
const { key } = event;
|
|
54
|
-
let nextIndex = index;
|
|
55
|
-
switch (key) {
|
|
56
|
-
case "ArrowDown":
|
|
57
|
-
case "ArrowRight":
|
|
58
|
-
event.preventDefault();
|
|
59
|
-
nextIndex = (index + 1) % radios.length;
|
|
60
|
-
selectRadio(nextIndex);
|
|
61
|
-
break;
|
|
62
|
-
case "ArrowUp":
|
|
63
|
-
case "ArrowLeft":
|
|
64
|
-
event.preventDefault();
|
|
65
|
-
nextIndex = (index - 1 + radios.length) % radios.length;
|
|
66
|
-
selectRadio(nextIndex);
|
|
67
|
-
break;
|
|
68
|
-
case " ":
|
|
69
|
-
case "Enter":
|
|
70
|
-
event.preventDefault();
|
|
71
|
-
selectRadio(index);
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
function addListeners() {
|
|
77
|
-
radios.forEach((radio, index) => {
|
|
78
|
-
const clickHandler = handleRadioClick(index);
|
|
79
|
-
const keydownHandler = handleRadioKeydown(index);
|
|
80
|
-
radio.addEventListener("click", clickHandler);
|
|
81
|
-
radio.addEventListener("keydown", keydownHandler);
|
|
82
|
-
handlerMap.set(radio, keydownHandler);
|
|
83
|
-
clickHandlerMap.set(radio, clickHandler);
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
function removeListeners() {
|
|
87
|
-
radios.forEach((radio) => {
|
|
88
|
-
const keydownHandler = handlerMap.get(radio);
|
|
89
|
-
const clickHandler = clickHandlerMap.get(radio);
|
|
90
|
-
if (keydownHandler) {
|
|
91
|
-
radio.removeEventListener("keydown", keydownHandler);
|
|
92
|
-
handlerMap.delete(radio);
|
|
93
|
-
}
|
|
94
|
-
if (clickHandler) {
|
|
95
|
-
radio.removeEventListener("click", clickHandler);
|
|
96
|
-
clickHandlerMap.delete(radio);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
function cleanup() {
|
|
101
|
-
removeListeners();
|
|
102
|
-
}
|
|
103
|
-
function getSelectedIndex() {
|
|
104
|
-
return currentSelectedIndex;
|
|
105
|
-
}
|
|
106
|
-
initialize();
|
|
107
|
-
addListeners();
|
|
108
|
-
return {
|
|
109
|
-
selectRadio,
|
|
110
|
-
getSelectedIndex,
|
|
111
|
-
cleanup
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export { makeRadioAccessible };
|
|
1
|
+
function E({radioGroupId:u,radiosClass:d,defaultSelectedIndex:f=0}){let i=document.querySelector(`#${u}`);if(!i)return console.error(`[aria-ease] Element with id="${u}" not found. Make sure the radio group container exists before calling makeRadioAccessible.`),{cleanup:()=>{}};let t=Array.from(i.querySelectorAll(`.${d}`));if(t.length===0)return console.error(`[aria-ease] No elements with class="${d}" found. Make sure radio buttons exist before calling makeRadioAccessible.`),{cleanup:()=>{}};let s=new WeakMap,l=new WeakMap,r=f;function b(){i.getAttribute("role")||i.setAttribute("role","radiogroup"),t.forEach((e,n)=>{e.setAttribute("role","radio"),e.setAttribute("tabindex",n===r?"0":"-1"),n===r?e.setAttribute("aria-checked","true"):e.setAttribute("aria-checked","false");});}function o(e){if(e<0||e>=t.length){console.error(`[aria-ease] Invalid radio index: ${e}`);return}r>=0&&r<t.length&&(t[r].setAttribute("aria-checked","false"),t[r].setAttribute("tabindex","-1")),t[e].setAttribute("aria-checked","true"),t[e].setAttribute("tabindex","0"),t[e].focus(),r=e;}function k(e){return ()=>{o(e);}}function m(e){return n=>{let{key:a}=n,c=e;switch(a){case "ArrowDown":case "ArrowRight":n.preventDefault(),c=(e+1)%t.length,o(c);break;case "ArrowUp":case "ArrowLeft":n.preventDefault(),c=(e-1+t.length)%t.length,o(c);break;case " ":case "Enter":n.preventDefault(),o(e);break}}}function h(){t.forEach((e,n)=>{let a=k(n),c=m(n);e.addEventListener("click",a),e.addEventListener("keydown",c),s.set(e,c),l.set(e,a);});}function A(){t.forEach(e=>{let n=s.get(e),a=l.get(e);n&&(e.removeEventListener("keydown",n),s.delete(e)),a&&(e.removeEventListener("click",a),l.delete(e));});}function g(){A();}function p(){return r}return b(),h(),{selectRadio:o,getSelectedIndex:p,cleanup:g}}export{E as makeRadioAccessible};
|