aria-ease 6.2.2 → 6.3.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 (47) hide show
  1. package/README.md +91 -12
  2. package/bin/{chunk-7RMRFSJL.js → chunk-XLG3MIPQ.js} +5 -1
  3. package/bin/cli.cjs +52 -11
  4. package/bin/cli.js +1 -1
  5. package/bin/{contractTestRunnerPlaywright-ACAWN34W.js → contractTestRunnerPlaywright-JXQUUKFO.js} +48 -11
  6. package/bin/{test-A3ESFXOR.js → test-XSDP2NX3.js} +2 -2
  7. package/dist/{chunk-PDZQOXUN.js → chunk-RDEAG4KE.js} +5 -1
  8. package/dist/{contractTestRunnerPlaywright-O7FF7GV4.js → contractTestRunnerPlaywright-EUXD6ZZK.js} +48 -11
  9. package/dist/index.cjs +316 -69
  10. package/dist/index.d.cts +34 -4
  11. package/dist/index.d.ts +34 -4
  12. package/dist/index.js +265 -60
  13. package/dist/src/{Types.d-CRjhbrcw.d.cts → Types.d-DYfYR3Vc.d.cts} +18 -1
  14. package/dist/src/{Types.d-CRjhbrcw.d.ts → Types.d-DYfYR3Vc.d.ts} +18 -1
  15. package/dist/src/accordion/index.d.cts +2 -2
  16. package/dist/src/accordion/index.d.ts +2 -2
  17. package/dist/src/block/index.d.cts +1 -1
  18. package/dist/src/block/index.d.ts +1 -1
  19. package/dist/src/checkbox/index.cjs +0 -22
  20. package/dist/src/checkbox/index.d.cts +2 -2
  21. package/dist/src/checkbox/index.d.ts +2 -2
  22. package/dist/src/checkbox/index.js +0 -22
  23. package/dist/src/combobox/index.d.cts +1 -1
  24. package/dist/src/combobox/index.d.ts +1 -1
  25. package/dist/src/menu/index.d.cts +1 -1
  26. package/dist/src/menu/index.d.ts +1 -1
  27. package/dist/src/radio/index.cjs +0 -8
  28. package/dist/src/radio/index.d.cts +2 -2
  29. package/dist/src/radio/index.d.ts +2 -2
  30. package/dist/src/radio/index.js +0 -8
  31. package/dist/src/tabs/index.cjs +265 -0
  32. package/dist/src/tabs/index.d.cts +16 -0
  33. package/dist/src/tabs/index.d.ts +16 -0
  34. package/dist/src/tabs/index.js +263 -0
  35. package/dist/src/toggle/index.cjs +0 -28
  36. package/dist/src/toggle/index.d.cts +1 -1
  37. package/dist/src/toggle/index.d.ts +1 -1
  38. package/dist/src/toggle/index.js +0 -28
  39. package/dist/src/utils/test/{chunk-7RMRFSJL.js → chunk-XLG3MIPQ.js} +5 -1
  40. package/dist/src/utils/test/{contractTestRunnerPlaywright-7BPRTIN4.js → contractTestRunnerPlaywright-N77NEY25.js} +48 -11
  41. package/dist/src/utils/test/contracts/AccordionContract.json +18 -17
  42. package/dist/src/utils/test/contracts/ComboboxContract.json +32 -48
  43. package/dist/src/utils/test/contracts/MenuContract.json +19 -25
  44. package/dist/src/utils/test/contracts/TabsContract.json +348 -0
  45. package/dist/src/utils/test/index.cjs +52 -11
  46. package/dist/src/utils/test/index.js +2 -2
  47. package/package.json +8 -3
@@ -0,0 +1,265 @@
1
+ 'use strict';
2
+
3
+ // src/tabs/src/makeTabsAccessible/makeTabsAccessible.ts
4
+ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation = "horizontal", activateOnFocus = true, callback }) {
5
+ const tabList = document.querySelector(`#${tabListId}`);
6
+ if (!tabList) {
7
+ console.error(`[aria-ease] Element with id="${tabListId}" not found. Make sure the tab list container exists before calling makeTabsAccessible.`);
8
+ return { cleanup: () => {
9
+ } };
10
+ }
11
+ const tabs = Array.from(tabList.querySelectorAll(`.${tabsClass}`));
12
+ if (tabs.length === 0) {
13
+ console.error(`[aria-ease] No elements with class="${tabsClass}" found. Make sure tab buttons exist before calling makeTabsAccessible.`);
14
+ return { cleanup: () => {
15
+ } };
16
+ }
17
+ const tabPanels = Array.from(document.querySelectorAll(`.${tabPanelsClass}`));
18
+ if (tabPanels.length === 0) {
19
+ console.error(`[aria-ease] No elements with class="${tabPanelsClass}" found. Make sure tab panels exist before calling makeTabsAccessible.`);
20
+ return { cleanup: () => {
21
+ } };
22
+ }
23
+ if (tabs.length !== tabPanels.length) {
24
+ console.error(`[aria-ease] Tab/panel mismatch: found ${tabs.length} tabs but ${tabPanels.length} panels.`);
25
+ return { cleanup: () => {
26
+ } };
27
+ }
28
+ const handlerMap = /* @__PURE__ */ new WeakMap();
29
+ const clickHandlerMap = /* @__PURE__ */ new WeakMap();
30
+ const contextMenuHandlerMap = /* @__PURE__ */ new WeakMap();
31
+ let activeTabIndex = 0;
32
+ function initialize() {
33
+ tabList.setAttribute("role", "tablist");
34
+ tabList.setAttribute("aria-orientation", orientation);
35
+ tabs.forEach((tab, index) => {
36
+ const panel = tabPanels[index];
37
+ if (!tab.id) {
38
+ tab.id = `${tabListId}-tab-${index}`;
39
+ }
40
+ if (!panel.id) {
41
+ panel.id = `${tabListId}-panel-${index}`;
42
+ }
43
+ tab.setAttribute("role", "tab");
44
+ tab.setAttribute("aria-controls", panel.id);
45
+ tab.setAttribute("aria-selected", "false");
46
+ tab.setAttribute("tabindex", "-1");
47
+ panel.setAttribute("role", "tabpanel");
48
+ panel.setAttribute("aria-labelledby", tab.id);
49
+ panel.hidden = true;
50
+ const hasFocusableContent = panel.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
51
+ if (!hasFocusableContent) {
52
+ panel.setAttribute("tabindex", "0");
53
+ }
54
+ });
55
+ activateTab(0, false);
56
+ }
57
+ function activateTab(index, shouldFocus = true) {
58
+ if (index < 0 || index >= tabs.length) {
59
+ console.error(`[aria-ease] Invalid tab index: ${index}`);
60
+ return;
61
+ }
62
+ const previousIndex = activeTabIndex;
63
+ tabs.forEach((tab, i) => {
64
+ const panel = tabPanels[i];
65
+ tab.setAttribute("aria-selected", "false");
66
+ tab.setAttribute("tabindex", "-1");
67
+ panel.hidden = true;
68
+ });
69
+ const activeTab = tabs[index];
70
+ const activePanel = tabPanels[index];
71
+ activeTab.setAttribute("aria-selected", "true");
72
+ activeTab.setAttribute("tabindex", "0");
73
+ activePanel.hidden = false;
74
+ if (shouldFocus) {
75
+ activeTab.focus();
76
+ }
77
+ activeTabIndex = index;
78
+ if (callback?.onTabChange && previousIndex !== index) {
79
+ try {
80
+ callback.onTabChange(index, previousIndex);
81
+ } catch (error) {
82
+ console.error("[aria-ease] Error in tabs onTabChange callback:", error);
83
+ }
84
+ }
85
+ }
86
+ function moveFocus(direction) {
87
+ const currentFocusedIndex = tabs.findIndex((tab) => tab === document.activeElement);
88
+ const currentIndex = currentFocusedIndex !== -1 ? currentFocusedIndex : activeTabIndex;
89
+ let newIndex = currentIndex;
90
+ switch (direction) {
91
+ case "first":
92
+ newIndex = 0;
93
+ break;
94
+ case "last":
95
+ newIndex = tabs.length - 1;
96
+ break;
97
+ case "next":
98
+ newIndex = (currentIndex + 1) % tabs.length;
99
+ break;
100
+ case "prev":
101
+ newIndex = (currentIndex - 1 + tabs.length) % tabs.length;
102
+ break;
103
+ }
104
+ tabs[newIndex].focus();
105
+ tabs[newIndex].setAttribute("tabindex", "0");
106
+ tabs[activeTabIndex].setAttribute("tabindex", "-1");
107
+ if (activateOnFocus) {
108
+ activateTab(newIndex, false);
109
+ } else {
110
+ const currentActive = activeTabIndex;
111
+ tabs.forEach((tab, i) => {
112
+ if (i === newIndex) {
113
+ tab.setAttribute("tabindex", "0");
114
+ } else if (i !== currentActive) {
115
+ tab.setAttribute("tabindex", "-1");
116
+ }
117
+ });
118
+ }
119
+ }
120
+ function handleTabClick(index) {
121
+ return () => {
122
+ activateTab(index);
123
+ };
124
+ }
125
+ function handleTabKeydown(index) {
126
+ return (event) => {
127
+ const { key } = event;
128
+ let handled = false;
129
+ if (orientation === "horizontal") {
130
+ switch (key) {
131
+ case "ArrowLeft":
132
+ event.preventDefault();
133
+ moveFocus("prev");
134
+ handled = true;
135
+ break;
136
+ case "ArrowRight":
137
+ event.preventDefault();
138
+ moveFocus("next");
139
+ handled = true;
140
+ break;
141
+ }
142
+ } else {
143
+ switch (key) {
144
+ case "ArrowUp":
145
+ event.preventDefault();
146
+ moveFocus("prev");
147
+ handled = true;
148
+ break;
149
+ case "ArrowDown":
150
+ event.preventDefault();
151
+ moveFocus("next");
152
+ handled = true;
153
+ break;
154
+ }
155
+ }
156
+ if (!handled) {
157
+ switch (key) {
158
+ case "Home":
159
+ event.preventDefault();
160
+ moveFocus("first");
161
+ break;
162
+ case "End":
163
+ event.preventDefault();
164
+ moveFocus("last");
165
+ break;
166
+ case " ":
167
+ case "Enter":
168
+ if (!activateOnFocus) {
169
+ event.preventDefault();
170
+ activateTab(index);
171
+ }
172
+ break;
173
+ case "F10":
174
+ if (event.shiftKey && callback?.onContextMenu) {
175
+ event.preventDefault();
176
+ try {
177
+ callback.onContextMenu(index, tabs[index]);
178
+ } catch (error) {
179
+ console.error("[aria-ease] Error in tabs onContextMenu callback:", error);
180
+ }
181
+ }
182
+ break;
183
+ }
184
+ }
185
+ };
186
+ }
187
+ function handleTabContextMenu(index) {
188
+ return (event) => {
189
+ if (callback?.onContextMenu) {
190
+ event.preventDefault();
191
+ try {
192
+ callback.onContextMenu(index, tabs[index]);
193
+ } catch (error) {
194
+ console.error("[aria-ease] Error in tabs onContextMenu callback:", error);
195
+ }
196
+ }
197
+ };
198
+ }
199
+ function addListeners() {
200
+ tabs.forEach((tab, index) => {
201
+ const clickHandler = handleTabClick(index);
202
+ const keydownHandler = handleTabKeydown(index);
203
+ const contextMenuHandler = handleTabContextMenu(index);
204
+ tab.addEventListener("click", clickHandler);
205
+ tab.addEventListener("keydown", keydownHandler);
206
+ if (callback?.onContextMenu) {
207
+ tab.addEventListener("contextmenu", contextMenuHandler);
208
+ contextMenuHandlerMap.set(tab, contextMenuHandler);
209
+ }
210
+ handlerMap.set(tab, keydownHandler);
211
+ clickHandlerMap.set(tab, clickHandler);
212
+ });
213
+ }
214
+ function removeListeners() {
215
+ tabs.forEach((tab) => {
216
+ const keydownHandler = handlerMap.get(tab);
217
+ const clickHandler = clickHandlerMap.get(tab);
218
+ const contextMenuHandler = contextMenuHandlerMap.get(tab);
219
+ if (keydownHandler) {
220
+ tab.removeEventListener("keydown", keydownHandler);
221
+ handlerMap.delete(tab);
222
+ }
223
+ if (clickHandler) {
224
+ tab.removeEventListener("click", clickHandler);
225
+ clickHandlerMap.delete(tab);
226
+ }
227
+ if (contextMenuHandler) {
228
+ tab.removeEventListener("contextmenu", contextMenuHandler);
229
+ contextMenuHandlerMap.delete(tab);
230
+ }
231
+ });
232
+ }
233
+ function cleanup() {
234
+ removeListeners();
235
+ tabs.forEach((tab, index) => {
236
+ const panel = tabPanels[index];
237
+ tab.removeAttribute("role");
238
+ tab.removeAttribute("aria-selected");
239
+ tab.removeAttribute("aria-controls");
240
+ tab.removeAttribute("tabindex");
241
+ panel.removeAttribute("role");
242
+ panel.removeAttribute("aria-labelledby");
243
+ panel.removeAttribute("tabindex");
244
+ panel.hidden = false;
245
+ });
246
+ tabList.removeAttribute("role");
247
+ tabList.removeAttribute("aria-orientation");
248
+ }
249
+ function refresh() {
250
+ removeListeners();
251
+ const newTabs = Array.from(tabList.querySelectorAll(`.${tabsClass}`));
252
+ const newPanels = Array.from(document.querySelectorAll(`.${tabPanelsClass}`));
253
+ tabs.length = 0;
254
+ tabs.push(...newTabs);
255
+ tabPanels.length = 0;
256
+ tabPanels.push(...newPanels);
257
+ initialize();
258
+ addListeners();
259
+ }
260
+ initialize();
261
+ addListeners();
262
+ return { activateTab, cleanup, refresh };
263
+ }
264
+
265
+ exports.makeTabsAccessible = makeTabsAccessible;
@@ -0,0 +1,16 @@
1
+ import { T as TabsConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
+
3
+ /**
4
+ * Makes tabs accessible by managing ARIA attributes, keyboard interaction, and state.
5
+ * Implements WAI-ARIA tabs pattern with full keyboard support and ARIA properties.
6
+ * @param {string} tabListId - The id of the tab list container.
7
+ * @param {string} tabsClass - The shared class of all tab buttons.
8
+ * @param {string} tabPanelsClass - The shared class of all tab panels.
9
+ * @param {('horizontal' | 'vertical')} orientation - Tab list orientation (default: 'horizontal').
10
+ * @param {boolean} activateOnFocus - Whether tabs activate automatically on focus (default: true).
11
+ * @param {TabsCallback} callback - Configuration options for callbacks.
12
+ */
13
+
14
+ declare function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation, activateOnFocus, callback }: TabsConfig): AccessibilityInstance;
15
+
16
+ export { makeTabsAccessible };
@@ -0,0 +1,16 @@
1
+ import { T as TabsConfig, a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
+
3
+ /**
4
+ * Makes tabs accessible by managing ARIA attributes, keyboard interaction, and state.
5
+ * Implements WAI-ARIA tabs pattern with full keyboard support and ARIA properties.
6
+ * @param {string} tabListId - The id of the tab list container.
7
+ * @param {string} tabsClass - The shared class of all tab buttons.
8
+ * @param {string} tabPanelsClass - The shared class of all tab panels.
9
+ * @param {('horizontal' | 'vertical')} orientation - Tab list orientation (default: 'horizontal').
10
+ * @param {boolean} activateOnFocus - Whether tabs activate automatically on focus (default: true).
11
+ * @param {TabsCallback} callback - Configuration options for callbacks.
12
+ */
13
+
14
+ declare function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation, activateOnFocus, callback }: TabsConfig): AccessibilityInstance;
15
+
16
+ export { makeTabsAccessible };
@@ -0,0 +1,263 @@
1
+ // src/tabs/src/makeTabsAccessible/makeTabsAccessible.ts
2
+ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation = "horizontal", activateOnFocus = true, callback }) {
3
+ const tabList = document.querySelector(`#${tabListId}`);
4
+ if (!tabList) {
5
+ console.error(`[aria-ease] Element with id="${tabListId}" not found. Make sure the tab list container exists before calling makeTabsAccessible.`);
6
+ return { cleanup: () => {
7
+ } };
8
+ }
9
+ const tabs = Array.from(tabList.querySelectorAll(`.${tabsClass}`));
10
+ if (tabs.length === 0) {
11
+ console.error(`[aria-ease] No elements with class="${tabsClass}" found. Make sure tab buttons exist before calling makeTabsAccessible.`);
12
+ return { cleanup: () => {
13
+ } };
14
+ }
15
+ const tabPanels = Array.from(document.querySelectorAll(`.${tabPanelsClass}`));
16
+ if (tabPanels.length === 0) {
17
+ console.error(`[aria-ease] No elements with class="${tabPanelsClass}" found. Make sure tab panels exist before calling makeTabsAccessible.`);
18
+ return { cleanup: () => {
19
+ } };
20
+ }
21
+ if (tabs.length !== tabPanels.length) {
22
+ console.error(`[aria-ease] Tab/panel mismatch: found ${tabs.length} tabs but ${tabPanels.length} panels.`);
23
+ return { cleanup: () => {
24
+ } };
25
+ }
26
+ const handlerMap = /* @__PURE__ */ new WeakMap();
27
+ const clickHandlerMap = /* @__PURE__ */ new WeakMap();
28
+ const contextMenuHandlerMap = /* @__PURE__ */ new WeakMap();
29
+ let activeTabIndex = 0;
30
+ function initialize() {
31
+ tabList.setAttribute("role", "tablist");
32
+ tabList.setAttribute("aria-orientation", orientation);
33
+ tabs.forEach((tab, index) => {
34
+ const panel = tabPanels[index];
35
+ if (!tab.id) {
36
+ tab.id = `${tabListId}-tab-${index}`;
37
+ }
38
+ if (!panel.id) {
39
+ panel.id = `${tabListId}-panel-${index}`;
40
+ }
41
+ tab.setAttribute("role", "tab");
42
+ tab.setAttribute("aria-controls", panel.id);
43
+ tab.setAttribute("aria-selected", "false");
44
+ tab.setAttribute("tabindex", "-1");
45
+ panel.setAttribute("role", "tabpanel");
46
+ panel.setAttribute("aria-labelledby", tab.id);
47
+ panel.hidden = true;
48
+ const hasFocusableContent = panel.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
49
+ if (!hasFocusableContent) {
50
+ panel.setAttribute("tabindex", "0");
51
+ }
52
+ });
53
+ activateTab(0, false);
54
+ }
55
+ function activateTab(index, shouldFocus = true) {
56
+ if (index < 0 || index >= tabs.length) {
57
+ console.error(`[aria-ease] Invalid tab index: ${index}`);
58
+ return;
59
+ }
60
+ const previousIndex = activeTabIndex;
61
+ tabs.forEach((tab, i) => {
62
+ const panel = tabPanels[i];
63
+ tab.setAttribute("aria-selected", "false");
64
+ tab.setAttribute("tabindex", "-1");
65
+ panel.hidden = true;
66
+ });
67
+ const activeTab = tabs[index];
68
+ const activePanel = tabPanels[index];
69
+ activeTab.setAttribute("aria-selected", "true");
70
+ activeTab.setAttribute("tabindex", "0");
71
+ activePanel.hidden = false;
72
+ if (shouldFocus) {
73
+ activeTab.focus();
74
+ }
75
+ activeTabIndex = index;
76
+ if (callback?.onTabChange && previousIndex !== index) {
77
+ try {
78
+ callback.onTabChange(index, previousIndex);
79
+ } catch (error) {
80
+ console.error("[aria-ease] Error in tabs onTabChange callback:", error);
81
+ }
82
+ }
83
+ }
84
+ function moveFocus(direction) {
85
+ const currentFocusedIndex = tabs.findIndex((tab) => tab === document.activeElement);
86
+ const currentIndex = currentFocusedIndex !== -1 ? currentFocusedIndex : activeTabIndex;
87
+ let newIndex = currentIndex;
88
+ switch (direction) {
89
+ case "first":
90
+ newIndex = 0;
91
+ break;
92
+ case "last":
93
+ newIndex = tabs.length - 1;
94
+ break;
95
+ case "next":
96
+ newIndex = (currentIndex + 1) % tabs.length;
97
+ break;
98
+ case "prev":
99
+ newIndex = (currentIndex - 1 + tabs.length) % tabs.length;
100
+ break;
101
+ }
102
+ tabs[newIndex].focus();
103
+ tabs[newIndex].setAttribute("tabindex", "0");
104
+ tabs[activeTabIndex].setAttribute("tabindex", "-1");
105
+ if (activateOnFocus) {
106
+ activateTab(newIndex, false);
107
+ } else {
108
+ const currentActive = activeTabIndex;
109
+ tabs.forEach((tab, i) => {
110
+ if (i === newIndex) {
111
+ tab.setAttribute("tabindex", "0");
112
+ } else if (i !== currentActive) {
113
+ tab.setAttribute("tabindex", "-1");
114
+ }
115
+ });
116
+ }
117
+ }
118
+ function handleTabClick(index) {
119
+ return () => {
120
+ activateTab(index);
121
+ };
122
+ }
123
+ function handleTabKeydown(index) {
124
+ return (event) => {
125
+ const { key } = event;
126
+ let handled = false;
127
+ if (orientation === "horizontal") {
128
+ switch (key) {
129
+ case "ArrowLeft":
130
+ event.preventDefault();
131
+ moveFocus("prev");
132
+ handled = true;
133
+ break;
134
+ case "ArrowRight":
135
+ event.preventDefault();
136
+ moveFocus("next");
137
+ handled = true;
138
+ break;
139
+ }
140
+ } else {
141
+ switch (key) {
142
+ case "ArrowUp":
143
+ event.preventDefault();
144
+ moveFocus("prev");
145
+ handled = true;
146
+ break;
147
+ case "ArrowDown":
148
+ event.preventDefault();
149
+ moveFocus("next");
150
+ handled = true;
151
+ break;
152
+ }
153
+ }
154
+ if (!handled) {
155
+ switch (key) {
156
+ case "Home":
157
+ event.preventDefault();
158
+ moveFocus("first");
159
+ break;
160
+ case "End":
161
+ event.preventDefault();
162
+ moveFocus("last");
163
+ break;
164
+ case " ":
165
+ case "Enter":
166
+ if (!activateOnFocus) {
167
+ event.preventDefault();
168
+ activateTab(index);
169
+ }
170
+ break;
171
+ case "F10":
172
+ if (event.shiftKey && callback?.onContextMenu) {
173
+ event.preventDefault();
174
+ try {
175
+ callback.onContextMenu(index, tabs[index]);
176
+ } catch (error) {
177
+ console.error("[aria-ease] Error in tabs onContextMenu callback:", error);
178
+ }
179
+ }
180
+ break;
181
+ }
182
+ }
183
+ };
184
+ }
185
+ function handleTabContextMenu(index) {
186
+ return (event) => {
187
+ if (callback?.onContextMenu) {
188
+ event.preventDefault();
189
+ try {
190
+ callback.onContextMenu(index, tabs[index]);
191
+ } catch (error) {
192
+ console.error("[aria-ease] Error in tabs onContextMenu callback:", error);
193
+ }
194
+ }
195
+ };
196
+ }
197
+ function addListeners() {
198
+ tabs.forEach((tab, index) => {
199
+ const clickHandler = handleTabClick(index);
200
+ const keydownHandler = handleTabKeydown(index);
201
+ const contextMenuHandler = handleTabContextMenu(index);
202
+ tab.addEventListener("click", clickHandler);
203
+ tab.addEventListener("keydown", keydownHandler);
204
+ if (callback?.onContextMenu) {
205
+ tab.addEventListener("contextmenu", contextMenuHandler);
206
+ contextMenuHandlerMap.set(tab, contextMenuHandler);
207
+ }
208
+ handlerMap.set(tab, keydownHandler);
209
+ clickHandlerMap.set(tab, clickHandler);
210
+ });
211
+ }
212
+ function removeListeners() {
213
+ tabs.forEach((tab) => {
214
+ const keydownHandler = handlerMap.get(tab);
215
+ const clickHandler = clickHandlerMap.get(tab);
216
+ const contextMenuHandler = contextMenuHandlerMap.get(tab);
217
+ if (keydownHandler) {
218
+ tab.removeEventListener("keydown", keydownHandler);
219
+ handlerMap.delete(tab);
220
+ }
221
+ if (clickHandler) {
222
+ tab.removeEventListener("click", clickHandler);
223
+ clickHandlerMap.delete(tab);
224
+ }
225
+ if (contextMenuHandler) {
226
+ tab.removeEventListener("contextmenu", contextMenuHandler);
227
+ contextMenuHandlerMap.delete(tab);
228
+ }
229
+ });
230
+ }
231
+ function cleanup() {
232
+ removeListeners();
233
+ tabs.forEach((tab, index) => {
234
+ const panel = tabPanels[index];
235
+ tab.removeAttribute("role");
236
+ tab.removeAttribute("aria-selected");
237
+ tab.removeAttribute("aria-controls");
238
+ tab.removeAttribute("tabindex");
239
+ panel.removeAttribute("role");
240
+ panel.removeAttribute("aria-labelledby");
241
+ panel.removeAttribute("tabindex");
242
+ panel.hidden = false;
243
+ });
244
+ tabList.removeAttribute("role");
245
+ tabList.removeAttribute("aria-orientation");
246
+ }
247
+ function refresh() {
248
+ removeListeners();
249
+ const newTabs = Array.from(tabList.querySelectorAll(`.${tabsClass}`));
250
+ const newPanels = Array.from(document.querySelectorAll(`.${tabPanelsClass}`));
251
+ tabs.length = 0;
252
+ tabs.push(...newTabs);
253
+ tabPanels.length = 0;
254
+ tabPanels.push(...newPanels);
255
+ initialize();
256
+ addListeners();
257
+ }
258
+ initialize();
259
+ addListeners();
260
+ return { activateTab, cleanup, refresh };
261
+ }
262
+
263
+ export { makeTabsAccessible };
@@ -69,34 +69,6 @@ function makeToggleAccessible({ toggleId, togglesClass, isSingleToggle = true })
69
69
  event.preventDefault();
70
70
  toggleButton(index);
71
71
  break;
72
- case "ArrowDown":
73
- case "ArrowRight":
74
- if (!isSingleToggle && toggles.length > 1) {
75
- event.preventDefault();
76
- const nextIndex = (index + 1) % toggles.length;
77
- toggles[nextIndex].focus();
78
- }
79
- break;
80
- case "ArrowUp":
81
- case "ArrowLeft":
82
- if (!isSingleToggle && toggles.length > 1) {
83
- event.preventDefault();
84
- const prevIndex = (index - 1 + toggles.length) % toggles.length;
85
- toggles[prevIndex].focus();
86
- }
87
- break;
88
- case "Home":
89
- if (!isSingleToggle && toggles.length > 1) {
90
- event.preventDefault();
91
- toggles[0].focus();
92
- }
93
- break;
94
- case "End":
95
- if (!isSingleToggle && toggles.length > 1) {
96
- event.preventDefault();
97
- toggles[toggles.length - 1].focus();
98
- }
99
- break;
100
72
  }
101
73
  };
102
74
  }
@@ -1,4 +1,4 @@
1
- import { a as AccessibilityInstance } from '../Types.d-CRjhbrcw.cjs';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.cjs';
2
2
 
3
3
  /**
4
4
  * Makes a toggle button accessible by managing ARIA attributes and keyboard interactions.
@@ -1,4 +1,4 @@
1
- import { a as AccessibilityInstance } from '../Types.d-CRjhbrcw.js';
1
+ import { a as AccessibilityInstance } from '../Types.d-DYfYR3Vc.js';
2
2
 
3
3
  /**
4
4
  * Makes a toggle button accessible by managing ARIA attributes and keyboard interactions.
@@ -67,34 +67,6 @@ function makeToggleAccessible({ toggleId, togglesClass, isSingleToggle = true })
67
67
  event.preventDefault();
68
68
  toggleButton(index);
69
69
  break;
70
- case "ArrowDown":
71
- case "ArrowRight":
72
- if (!isSingleToggle && toggles.length > 1) {
73
- event.preventDefault();
74
- const nextIndex = (index + 1) % toggles.length;
75
- toggles[nextIndex].focus();
76
- }
77
- break;
78
- case "ArrowUp":
79
- case "ArrowLeft":
80
- if (!isSingleToggle && toggles.length > 1) {
81
- event.preventDefault();
82
- const prevIndex = (index - 1 + toggles.length) % toggles.length;
83
- toggles[prevIndex].focus();
84
- }
85
- break;
86
- case "Home":
87
- if (!isSingleToggle && toggles.length > 1) {
88
- event.preventDefault();
89
- toggles[0].focus();
90
- }
91
- break;
92
- case "End":
93
- if (!isSingleToggle && toggles.length > 1) {
94
- event.preventDefault();
95
- toggles[toggles.length - 1].focus();
96
- }
97
- break;
98
70
  }
99
71
  };
100
72
  }
@@ -13,6 +13,10 @@ var contract_default = {
13
13
  accordion: {
14
14
  path: "./contracts/AccordionContract.json",
15
15
  component: "accordion"
16
+ },
17
+ tabs: {
18
+ path: "./contracts/TabsContract.json",
19
+ component: "tabs"
16
20
  }
17
21
  };
18
22
 
@@ -127,7 +131,7 @@ ${"\u2500".repeat(60)}`);
127
131
  this.log(`\u{1F4A1} Optional Enhancements (${suggestions.length}):
128
132
  `);
129
133
  this.log(`These features are optional per APG guidelines but recommended`);
130
- this.log(`for improved user experience and keyboard navigation:
134
+ this.log(`for improved user experience and keyboard interaction:
131
135
  `);
132
136
  suggestions.forEach((test, index) => {
133
137
  this.log(`${index + 1}. ${test.description}`);