aria-ease 3.0.3 → 4.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.
Files changed (42) hide show
  1. package/bin/cli.cjs +32 -0
  2. package/bin/cli.js +1 -1
  3. package/bin/{contractTestRunnerPlaywright-3VJUZSYK.js → contractTestRunnerPlaywright-EZLNNJV5.js} +32 -0
  4. package/bin/{test-D374H2ZS.js → test-45KMD4F4.js} +1 -1
  5. package/dist/{contractTestRunnerPlaywright-4UOHWGWD.js → contractTestRunnerPlaywright-UQQI5MYS.js} +32 -0
  6. package/dist/index.cjs +624 -1
  7. package/dist/index.d.cts +83 -2
  8. package/dist/index.d.ts +83 -2
  9. package/dist/index.js +589 -2
  10. package/dist/src/{Types.d-uG0Hm1yK.d.ts → Types.d-BrHSyS03.d.cts} +17 -0
  11. package/dist/src/{Types.d-uG0Hm1yK.d.cts → Types.d-BrHSyS03.d.ts} +17 -0
  12. package/dist/src/accordion/index.cjs +159 -0
  13. package/dist/src/accordion/index.d.cts +19 -2
  14. package/dist/src/accordion/index.d.ts +19 -2
  15. package/dist/src/accordion/index.js +159 -1
  16. package/dist/src/block/index.cjs +1 -1
  17. package/dist/src/block/index.d.cts +6 -2
  18. package/dist/src/block/index.d.ts +6 -2
  19. package/dist/src/block/index.js +1 -1
  20. package/dist/src/checkbox/index.cjs +129 -0
  21. package/dist/src/checkbox/index.d.cts +15 -2
  22. package/dist/src/checkbox/index.d.ts +15 -2
  23. package/dist/src/checkbox/index.js +129 -1
  24. package/dist/src/combobox/index.d.cts +1 -1
  25. package/dist/src/combobox/index.d.ts +1 -1
  26. package/dist/src/menu/index.cjs +32 -0
  27. package/dist/src/menu/index.d.cts +1 -1
  28. package/dist/src/menu/index.d.ts +1 -1
  29. package/dist/src/menu/index.js +32 -0
  30. package/dist/src/radio/index.cjs +122 -0
  31. package/dist/src/radio/index.d.cts +17 -2
  32. package/dist/src/radio/index.d.ts +17 -2
  33. package/dist/src/radio/index.js +122 -1
  34. package/dist/src/toggle/index.cjs +145 -0
  35. package/dist/src/toggle/index.d.cts +17 -2
  36. package/dist/src/toggle/index.d.ts +17 -2
  37. package/dist/src/toggle/index.js +145 -1
  38. package/dist/src/utils/test/{contractTestRunnerPlaywright-4UOHWGWD.js → contractTestRunnerPlaywright-UQQI5MYS.js} +32 -0
  39. package/dist/src/utils/test/contracts/MenuContract.json +0 -1
  40. package/dist/src/utils/test/index.cjs +32 -0
  41. package/dist/src/utils/test/index.js +1 -1
  42. package/package.json +1 -1
@@ -29,4 +29,132 @@ function updateCheckboxAriaAttributes(checkboxId, checkboxesClass, checkboxState
29
29
  });
30
30
  }
31
31
 
32
- export { updateCheckboxAriaAttributes };
32
+ // src/checkbox/src/makeCheckboxAccessible/makeCheckboxAccessible.ts
33
+ function makeCheckboxAccessible({ checkboxGroupId, checkboxesClass }) {
34
+ const checkboxGroup = document.querySelector(`#${checkboxGroupId}`);
35
+ if (!checkboxGroup) {
36
+ console.error(`[aria-ease] Element with id="${checkboxGroupId}" not found. Make sure the checkbox group container exists before calling makeCheckboxAccessible.`);
37
+ return { cleanup: () => {
38
+ } };
39
+ }
40
+ const checkboxes = Array.from(checkboxGroup.querySelectorAll(`.${checkboxesClass}`));
41
+ if (checkboxes.length === 0) {
42
+ console.error(`[aria-ease] No elements with class="${checkboxesClass}" found. Make sure checkboxes exist before calling makeCheckboxAccessible.`);
43
+ return { cleanup: () => {
44
+ } };
45
+ }
46
+ const handlerMap = /* @__PURE__ */ new WeakMap();
47
+ const clickHandlerMap = /* @__PURE__ */ new WeakMap();
48
+ function initialize() {
49
+ if (!checkboxGroup.getAttribute("role")) {
50
+ checkboxGroup.setAttribute("role", "group");
51
+ }
52
+ checkboxes.forEach((checkbox) => {
53
+ checkbox.setAttribute("role", "checkbox");
54
+ if (!checkbox.hasAttribute("aria-checked")) {
55
+ checkbox.setAttribute("aria-checked", "false");
56
+ }
57
+ if (!checkbox.hasAttribute("tabindex")) {
58
+ checkbox.setAttribute("tabindex", "0");
59
+ }
60
+ });
61
+ }
62
+ function toggleCheckbox(index) {
63
+ if (index < 0 || index >= checkboxes.length) {
64
+ console.error(`[aria-ease] Invalid checkbox index: ${index}`);
65
+ return;
66
+ }
67
+ const checkbox = checkboxes[index];
68
+ const isChecked = checkbox.getAttribute("aria-checked") === "true";
69
+ checkbox.setAttribute("aria-checked", isChecked ? "false" : "true");
70
+ }
71
+ function setCheckboxState(index, checked) {
72
+ if (index < 0 || index >= checkboxes.length) {
73
+ console.error(`[aria-ease] Invalid checkbox index: ${index}`);
74
+ return;
75
+ }
76
+ checkboxes[index].setAttribute("aria-checked", checked ? "true" : "false");
77
+ }
78
+ function handleCheckboxClick(index) {
79
+ return () => {
80
+ toggleCheckbox(index);
81
+ };
82
+ }
83
+ function handleCheckboxKeydown(index) {
84
+ return (event) => {
85
+ const { key } = event;
86
+ switch (key) {
87
+ case " ":
88
+ event.preventDefault();
89
+ toggleCheckbox(index);
90
+ break;
91
+ case "ArrowDown":
92
+ event.preventDefault();
93
+ {
94
+ const nextIndex = (index + 1) % checkboxes.length;
95
+ checkboxes[nextIndex].focus();
96
+ }
97
+ break;
98
+ case "ArrowUp":
99
+ event.preventDefault();
100
+ {
101
+ const prevIndex = (index - 1 + checkboxes.length) % checkboxes.length;
102
+ checkboxes[prevIndex].focus();
103
+ }
104
+ break;
105
+ case "Home":
106
+ event.preventDefault();
107
+ checkboxes[0].focus();
108
+ break;
109
+ case "End":
110
+ event.preventDefault();
111
+ checkboxes[checkboxes.length - 1].focus();
112
+ break;
113
+ }
114
+ };
115
+ }
116
+ function addListeners() {
117
+ checkboxes.forEach((checkbox, index) => {
118
+ const clickHandler = handleCheckboxClick(index);
119
+ const keydownHandler = handleCheckboxKeydown(index);
120
+ checkbox.addEventListener("click", clickHandler);
121
+ checkbox.addEventListener("keydown", keydownHandler);
122
+ handlerMap.set(checkbox, keydownHandler);
123
+ clickHandlerMap.set(checkbox, clickHandler);
124
+ });
125
+ }
126
+ function removeListeners() {
127
+ checkboxes.forEach((checkbox) => {
128
+ const keydownHandler = handlerMap.get(checkbox);
129
+ const clickHandler = clickHandlerMap.get(checkbox);
130
+ if (keydownHandler) {
131
+ checkbox.removeEventListener("keydown", keydownHandler);
132
+ handlerMap.delete(checkbox);
133
+ }
134
+ if (clickHandler) {
135
+ checkbox.removeEventListener("click", clickHandler);
136
+ clickHandlerMap.delete(checkbox);
137
+ }
138
+ });
139
+ }
140
+ function cleanup() {
141
+ removeListeners();
142
+ }
143
+ function getCheckedStates() {
144
+ return checkboxes.map((checkbox) => checkbox.getAttribute("aria-checked") === "true");
145
+ }
146
+ function getCheckedIndices() {
147
+ return checkboxes.map((checkbox, index) => checkbox.getAttribute("aria-checked") === "true" ? index : -1).filter((index) => index !== -1);
148
+ }
149
+ initialize();
150
+ addListeners();
151
+ return {
152
+ toggleCheckbox,
153
+ setCheckboxState,
154
+ getCheckedStates,
155
+ getCheckedIndices,
156
+ cleanup
157
+ };
158
+ }
159
+
160
+ export { makeCheckboxAccessible, updateCheckboxAriaAttributes };
@@ -1,4 +1,4 @@
1
- import { b as ComboboxConfig, a as AccessibilityInstance } from '../Types.d-uG0Hm1yK.cjs';
1
+ import { b as ComboboxConfig, a as AccessibilityInstance } from '../Types.d-BrHSyS03.cjs';
2
2
 
3
3
  /**
4
4
  * Makes a Combobox accessible by adding appropriate ARIA attributes, keyboard interactions and focus management.
@@ -1,4 +1,4 @@
1
- import { b as ComboboxConfig, a as AccessibilityInstance } from '../Types.d-uG0Hm1yK.js';
1
+ import { b as ComboboxConfig, a as AccessibilityInstance } from '../Types.d-BrHSyS03.js';
2
2
 
3
3
  /**
4
4
  * Makes a Combobox accessible by adding appropriate ARIA attributes, keyboard interactions and focus management.
@@ -149,6 +149,7 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId }) {
149
149
  triggerButton.setAttribute("aria-controls", menuId);
150
150
  triggerButton.setAttribute("aria-expanded", "false");
151
151
  menuDiv.setAttribute("role", "menu");
152
+ menuDiv.setAttribute("aria-labelledby", triggerId);
152
153
  const handlerMap = /* @__PURE__ */ new WeakMap();
153
154
  const submenuInstances = /* @__PURE__ */ new Map();
154
155
  let cachedItems = null;
@@ -285,8 +286,39 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId }) {
285
286
  });
286
287
  }
287
288
  intializeMenuItems();
289
+ function handleTriggerKeydown(event) {
290
+ if (event.key === "Enter" || event.key === " ") {
291
+ event.preventDefault();
292
+ const isOpen = menuDiv.style.display !== "none";
293
+ if (isOpen) {
294
+ closeMenu();
295
+ } else {
296
+ openMenu();
297
+ }
298
+ }
299
+ }
300
+ function handleTriggerClick() {
301
+ const isOpen = menuDiv.style.display !== "none";
302
+ if (isOpen) {
303
+ closeMenu();
304
+ } else {
305
+ openMenu();
306
+ }
307
+ }
308
+ function handleClickOutside(event) {
309
+ if (menuDiv && triggerButton && !menuDiv.contains(event.target) && !triggerButton.contains(event.target) && getComputedStyle(menuDiv).display !== "none" && triggerButton.getAttribute("aria-expanded") === "true") {
310
+ closeMenu();
311
+ }
312
+ }
313
+ triggerButton.addEventListener("keydown", handleTriggerKeydown);
314
+ triggerButton.addEventListener("click", handleTriggerClick);
315
+ document.addEventListener("click", handleClickOutside);
316
+ triggerButton.setAttribute("data-menu-initialized", "true");
288
317
  function cleanup() {
289
318
  removeListeners();
319
+ triggerButton.removeEventListener("keydown", handleTriggerKeydown);
320
+ triggerButton.removeEventListener("click", handleTriggerClick);
321
+ document.removeEventListener("click", handleClickOutside);
290
322
  menuDiv.style.display = "none";
291
323
  setAria(false);
292
324
  submenuInstances.forEach((instance) => instance.cleanup());
@@ -1,4 +1,4 @@
1
- import { a as AccessibilityInstance } from '../Types.d-uG0Hm1yK.cjs';
1
+ import { a as AccessibilityInstance } from '../Types.d-BrHSyS03.cjs';
2
2
 
3
3
  /**
4
4
  * Adds keyboard interaction to toggle menu. The menu traps focus and can be interacted with using the keyboard. The first interactive item of the menu has focus when menu open.
@@ -1,4 +1,4 @@
1
- import { a as AccessibilityInstance } from '../Types.d-uG0Hm1yK.js';
1
+ import { a as AccessibilityInstance } from '../Types.d-BrHSyS03.js';
2
2
 
3
3
  /**
4
4
  * Adds keyboard interaction to toggle menu. The menu traps focus and can be interacted with using the keyboard. The first interactive item of the menu has focus when menu open.
@@ -29,6 +29,7 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId }) {
29
29
  triggerButton.setAttribute("aria-controls", menuId);
30
30
  triggerButton.setAttribute("aria-expanded", "false");
31
31
  menuDiv.setAttribute("role", "menu");
32
+ menuDiv.setAttribute("aria-labelledby", triggerId);
32
33
  const handlerMap = /* @__PURE__ */ new WeakMap();
33
34
  const submenuInstances = /* @__PURE__ */ new Map();
34
35
  let cachedItems = null;
@@ -165,8 +166,39 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId }) {
165
166
  });
166
167
  }
167
168
  intializeMenuItems();
169
+ function handleTriggerKeydown(event) {
170
+ if (event.key === "Enter" || event.key === " ") {
171
+ event.preventDefault();
172
+ const isOpen = menuDiv.style.display !== "none";
173
+ if (isOpen) {
174
+ closeMenu();
175
+ } else {
176
+ openMenu();
177
+ }
178
+ }
179
+ }
180
+ function handleTriggerClick() {
181
+ const isOpen = menuDiv.style.display !== "none";
182
+ if (isOpen) {
183
+ closeMenu();
184
+ } else {
185
+ openMenu();
186
+ }
187
+ }
188
+ function handleClickOutside(event) {
189
+ if (menuDiv && triggerButton && !menuDiv.contains(event.target) && !triggerButton.contains(event.target) && getComputedStyle(menuDiv).display !== "none" && triggerButton.getAttribute("aria-expanded") === "true") {
190
+ closeMenu();
191
+ }
192
+ }
193
+ triggerButton.addEventListener("keydown", handleTriggerKeydown);
194
+ triggerButton.addEventListener("click", handleTriggerClick);
195
+ document.addEventListener("click", handleClickOutside);
196
+ triggerButton.setAttribute("data-menu-initialized", "true");
168
197
  function cleanup() {
169
198
  removeListeners();
199
+ triggerButton.removeEventListener("keydown", handleTriggerKeydown);
200
+ triggerButton.removeEventListener("click", handleTriggerClick);
201
+ document.removeEventListener("click", handleClickOutside);
170
202
  menuDiv.style.display = "none";
171
203
  setAria(false);
172
204
  submenuInstances.forEach((instance) => instance.cleanup());
@@ -30,4 +30,126 @@ function updateRadioAriaAttributes(radioId, radiosClass, radioStates, currentPre
30
30
  });
31
31
  }
32
32
 
33
+ // src/radio/src/makeRadioAccessible/makeRadioAccessible.ts
34
+ function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex = 0 }) {
35
+ const radioGroup = document.querySelector(`#${radioGroupId}`);
36
+ if (!radioGroup) {
37
+ console.error(`[aria-ease] Element with id="${radioGroupId}" not found. Make sure the radio group container exists before calling makeRadioAccessible.`);
38
+ return { cleanup: () => {
39
+ } };
40
+ }
41
+ const radios = Array.from(radioGroup.querySelectorAll(`.${radiosClass}`));
42
+ if (radios.length === 0) {
43
+ console.error(`[aria-ease] No elements with class="${radiosClass}" found. Make sure radio buttons exist before calling makeRadioAccessible.`);
44
+ return { cleanup: () => {
45
+ } };
46
+ }
47
+ const handlerMap = /* @__PURE__ */ new WeakMap();
48
+ const clickHandlerMap = /* @__PURE__ */ new WeakMap();
49
+ let currentSelectedIndex = defaultSelectedIndex;
50
+ function initialize() {
51
+ if (!radioGroup.getAttribute("role")) {
52
+ radioGroup.setAttribute("role", "radiogroup");
53
+ }
54
+ radios.forEach((radio, index) => {
55
+ radio.setAttribute("role", "radio");
56
+ radio.setAttribute("tabindex", index === currentSelectedIndex ? "0" : "-1");
57
+ if (index === currentSelectedIndex) {
58
+ radio.setAttribute("aria-checked", "true");
59
+ } else {
60
+ radio.setAttribute("aria-checked", "false");
61
+ }
62
+ });
63
+ }
64
+ function selectRadio(index) {
65
+ if (index < 0 || index >= radios.length) {
66
+ console.error(`[aria-ease] Invalid radio index: ${index}`);
67
+ return;
68
+ }
69
+ if (currentSelectedIndex >= 0 && currentSelectedIndex < radios.length) {
70
+ radios[currentSelectedIndex].setAttribute("aria-checked", "false");
71
+ radios[currentSelectedIndex].setAttribute("tabindex", "-1");
72
+ }
73
+ radios[index].setAttribute("aria-checked", "true");
74
+ radios[index].setAttribute("tabindex", "0");
75
+ radios[index].focus();
76
+ currentSelectedIndex = index;
77
+ }
78
+ function handleRadioClick(index) {
79
+ return () => {
80
+ selectRadio(index);
81
+ };
82
+ }
83
+ function handleRadioKeydown(index) {
84
+ return (event) => {
85
+ const { key } = event;
86
+ let nextIndex = index;
87
+ switch (key) {
88
+ case "ArrowDown":
89
+ case "ArrowRight":
90
+ event.preventDefault();
91
+ nextIndex = (index + 1) % radios.length;
92
+ selectRadio(nextIndex);
93
+ break;
94
+ case "ArrowUp":
95
+ case "ArrowLeft":
96
+ event.preventDefault();
97
+ nextIndex = (index - 1 + radios.length) % radios.length;
98
+ selectRadio(nextIndex);
99
+ break;
100
+ case " ":
101
+ event.preventDefault();
102
+ selectRadio(index);
103
+ break;
104
+ case "Home":
105
+ event.preventDefault();
106
+ selectRadio(0);
107
+ break;
108
+ case "End":
109
+ event.preventDefault();
110
+ selectRadio(radios.length - 1);
111
+ break;
112
+ }
113
+ };
114
+ }
115
+ function addListeners() {
116
+ radios.forEach((radio, index) => {
117
+ const clickHandler = handleRadioClick(index);
118
+ const keydownHandler = handleRadioKeydown(index);
119
+ radio.addEventListener("click", clickHandler);
120
+ radio.addEventListener("keydown", keydownHandler);
121
+ handlerMap.set(radio, keydownHandler);
122
+ clickHandlerMap.set(radio, clickHandler);
123
+ });
124
+ }
125
+ function removeListeners() {
126
+ radios.forEach((radio) => {
127
+ const keydownHandler = handlerMap.get(radio);
128
+ const clickHandler = clickHandlerMap.get(radio);
129
+ if (keydownHandler) {
130
+ radio.removeEventListener("keydown", keydownHandler);
131
+ handlerMap.delete(radio);
132
+ }
133
+ if (clickHandler) {
134
+ radio.removeEventListener("click", clickHandler);
135
+ clickHandlerMap.delete(radio);
136
+ }
137
+ });
138
+ }
139
+ function cleanup() {
140
+ removeListeners();
141
+ }
142
+ function getSelectedIndex() {
143
+ return currentSelectedIndex;
144
+ }
145
+ initialize();
146
+ addListeners();
147
+ return {
148
+ selectRadio,
149
+ getSelectedIndex,
150
+ cleanup
151
+ };
152
+ }
153
+
154
+ exports.makeRadioAccessible = makeRadioAccessible;
33
155
  exports.updateRadioAriaAttributes = updateRadioAriaAttributes;
@@ -1,4 +1,4 @@
1
- import { R as RadioStates } from '../Types.d-uG0Hm1yK.cjs';
1
+ import { R as RadioStates, a as AccessibilityInstance } from '../Types.d-BrHSyS03.cjs';
2
2
 
3
3
  /**
4
4
  * Adds screen reader accessibility to multiple radio buttons. Updates the aria attributes of the radio buttons. Radio elements must possess the following aria attributes; aria-checked and aria-label.
@@ -10,4 +10,19 @@ import { R as RadioStates } from '../Types.d-uG0Hm1yK.cjs';
10
10
 
11
11
  declare function updateRadioAriaAttributes(radioId: string, radiosClass: string, radioStates: RadioStates[], currentPressedRadioIndex: number): void;
12
12
 
13
- export { updateRadioAriaAttributes };
13
+ /**
14
+ * Makes a radio group accessible by managing ARIA attributes, keyboard navigation, and state.
15
+ * Handles radio button selection with proper focus management and keyboard interactions.
16
+ * @param {string} radioGroupId - The id of the radio group container.
17
+ * @param {string} radiosClass - The shared class of all radio buttons.
18
+ * @param {number} defaultSelectedIndex - The index of the initially selected radio (default: 0).
19
+ */
20
+
21
+ interface RadioConfig {
22
+ radioGroupId: string;
23
+ radiosClass: string;
24
+ defaultSelectedIndex?: number;
25
+ }
26
+ declare function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex }: RadioConfig): AccessibilityInstance;
27
+
28
+ export { makeRadioAccessible, updateRadioAriaAttributes };
@@ -1,4 +1,4 @@
1
- import { R as RadioStates } from '../Types.d-uG0Hm1yK.js';
1
+ import { R as RadioStates, a as AccessibilityInstance } from '../Types.d-BrHSyS03.js';
2
2
 
3
3
  /**
4
4
  * Adds screen reader accessibility to multiple radio buttons. Updates the aria attributes of the radio buttons. Radio elements must possess the following aria attributes; aria-checked and aria-label.
@@ -10,4 +10,19 @@ import { R as RadioStates } from '../Types.d-uG0Hm1yK.js';
10
10
 
11
11
  declare function updateRadioAriaAttributes(radioId: string, radiosClass: string, radioStates: RadioStates[], currentPressedRadioIndex: number): void;
12
12
 
13
- export { updateRadioAriaAttributes };
13
+ /**
14
+ * Makes a radio group accessible by managing ARIA attributes, keyboard navigation, and state.
15
+ * Handles radio button selection with proper focus management and keyboard interactions.
16
+ * @param {string} radioGroupId - The id of the radio group container.
17
+ * @param {string} radiosClass - The shared class of all radio buttons.
18
+ * @param {number} defaultSelectedIndex - The index of the initially selected radio (default: 0).
19
+ */
20
+
21
+ interface RadioConfig {
22
+ radioGroupId: string;
23
+ radiosClass: string;
24
+ defaultSelectedIndex?: number;
25
+ }
26
+ declare function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex }: RadioConfig): AccessibilityInstance;
27
+
28
+ export { makeRadioAccessible, updateRadioAriaAttributes };
@@ -28,4 +28,125 @@ function updateRadioAriaAttributes(radioId, radiosClass, radioStates, currentPre
28
28
  });
29
29
  }
30
30
 
31
- export { updateRadioAriaAttributes };
31
+ // src/radio/src/makeRadioAccessible/makeRadioAccessible.ts
32
+ function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex = 0 }) {
33
+ const radioGroup = document.querySelector(`#${radioGroupId}`);
34
+ if (!radioGroup) {
35
+ console.error(`[aria-ease] Element with id="${radioGroupId}" not found. Make sure the radio group container exists before calling makeRadioAccessible.`);
36
+ return { cleanup: () => {
37
+ } };
38
+ }
39
+ const radios = Array.from(radioGroup.querySelectorAll(`.${radiosClass}`));
40
+ if (radios.length === 0) {
41
+ console.error(`[aria-ease] No elements with class="${radiosClass}" found. Make sure radio buttons exist before calling makeRadioAccessible.`);
42
+ return { cleanup: () => {
43
+ } };
44
+ }
45
+ const handlerMap = /* @__PURE__ */ new WeakMap();
46
+ const clickHandlerMap = /* @__PURE__ */ new WeakMap();
47
+ let currentSelectedIndex = defaultSelectedIndex;
48
+ function initialize() {
49
+ if (!radioGroup.getAttribute("role")) {
50
+ radioGroup.setAttribute("role", "radiogroup");
51
+ }
52
+ radios.forEach((radio, index) => {
53
+ radio.setAttribute("role", "radio");
54
+ radio.setAttribute("tabindex", index === currentSelectedIndex ? "0" : "-1");
55
+ if (index === currentSelectedIndex) {
56
+ radio.setAttribute("aria-checked", "true");
57
+ } else {
58
+ radio.setAttribute("aria-checked", "false");
59
+ }
60
+ });
61
+ }
62
+ function selectRadio(index) {
63
+ if (index < 0 || index >= radios.length) {
64
+ console.error(`[aria-ease] Invalid radio index: ${index}`);
65
+ return;
66
+ }
67
+ if (currentSelectedIndex >= 0 && currentSelectedIndex < radios.length) {
68
+ radios[currentSelectedIndex].setAttribute("aria-checked", "false");
69
+ radios[currentSelectedIndex].setAttribute("tabindex", "-1");
70
+ }
71
+ radios[index].setAttribute("aria-checked", "true");
72
+ radios[index].setAttribute("tabindex", "0");
73
+ radios[index].focus();
74
+ currentSelectedIndex = index;
75
+ }
76
+ function handleRadioClick(index) {
77
+ return () => {
78
+ selectRadio(index);
79
+ };
80
+ }
81
+ function handleRadioKeydown(index) {
82
+ return (event) => {
83
+ const { key } = event;
84
+ let nextIndex = index;
85
+ switch (key) {
86
+ case "ArrowDown":
87
+ case "ArrowRight":
88
+ event.preventDefault();
89
+ nextIndex = (index + 1) % radios.length;
90
+ selectRadio(nextIndex);
91
+ break;
92
+ case "ArrowUp":
93
+ case "ArrowLeft":
94
+ event.preventDefault();
95
+ nextIndex = (index - 1 + radios.length) % radios.length;
96
+ selectRadio(nextIndex);
97
+ break;
98
+ case " ":
99
+ event.preventDefault();
100
+ selectRadio(index);
101
+ break;
102
+ case "Home":
103
+ event.preventDefault();
104
+ selectRadio(0);
105
+ break;
106
+ case "End":
107
+ event.preventDefault();
108
+ selectRadio(radios.length - 1);
109
+ break;
110
+ }
111
+ };
112
+ }
113
+ function addListeners() {
114
+ radios.forEach((radio, index) => {
115
+ const clickHandler = handleRadioClick(index);
116
+ const keydownHandler = handleRadioKeydown(index);
117
+ radio.addEventListener("click", clickHandler);
118
+ radio.addEventListener("keydown", keydownHandler);
119
+ handlerMap.set(radio, keydownHandler);
120
+ clickHandlerMap.set(radio, clickHandler);
121
+ });
122
+ }
123
+ function removeListeners() {
124
+ radios.forEach((radio) => {
125
+ const keydownHandler = handlerMap.get(radio);
126
+ const clickHandler = clickHandlerMap.get(radio);
127
+ if (keydownHandler) {
128
+ radio.removeEventListener("keydown", keydownHandler);
129
+ handlerMap.delete(radio);
130
+ }
131
+ if (clickHandler) {
132
+ radio.removeEventListener("click", clickHandler);
133
+ clickHandlerMap.delete(radio);
134
+ }
135
+ });
136
+ }
137
+ function cleanup() {
138
+ removeListeners();
139
+ }
140
+ function getSelectedIndex() {
141
+ return currentSelectedIndex;
142
+ }
143
+ initialize();
144
+ addListeners();
145
+ return {
146
+ selectRadio,
147
+ getSelectedIndex,
148
+ cleanup
149
+ };
150
+ }
151
+
152
+ export { makeRadioAccessible, updateRadioAriaAttributes };