vue-a11y-directives 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vue A11y Directives Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # Vue A11y Directives
2
+
3
+ A comprehensive collection of Vue 3 directives for building accessible web applications with WCAG compliance.
4
+
5
+ ## 🚀 Features
6
+
7
+ - **7 Powerful Directives** for complete accessibility control
8
+ - **Zero Dependencies** - Lightweight and performant
9
+ - **TypeScript Support** - Full type definitions included
10
+ - **WCAG Compliant** - Follow web accessibility standards
11
+ - **Easy to Use** - Simple directive-based API
12
+ - **Well Documented** - Comprehensive documentation and examples
13
+
14
+ ## 📦 Installation
15
+
16
+ ```bash
17
+ npm install vue-a11y-directives
18
+ ```
19
+
20
+ ## 🎯 Quick Start
21
+
22
+ ```javascript
23
+ import { createApp } from 'vue'
24
+ import App from './App.vue'
25
+ import { installA11yDirectives } from 'vue-a11y-directives'
26
+
27
+ const app = createApp(App)
28
+
29
+ // Install all directives globally
30
+ installA11yDirectives(app)
31
+
32
+ app.mount('#app')
33
+ ```
34
+
35
+ Or import individual directives:
36
+
37
+ ```javascript
38
+ import { focusDirective, keyboardDirective, ariaDirective } from 'vue-a11y-directives'
39
+
40
+ export default {
41
+ directives: {
42
+ 'a11y-focus': focusDirective,
43
+ 'a11y-keyboard': keyboardDirective,
44
+ 'a11y-aria': ariaDirective
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## 📚 Available Directives
50
+
51
+ ### 1. `v-a11y-focus`
52
+ Auto-focus elements with conditional logic.
53
+
54
+ ```vue
55
+ <input v-a11y-focus />
56
+ <input v-a11y-focus="isEditing" />
57
+ ```
58
+
59
+ ### 2. `v-a11y-keyboard`
60
+ Enhanced keyboard navigation support.
61
+
62
+ ```vue
63
+ <div v-a11y-keyboard="{
64
+ arrowDown: () => next(),
65
+ arrowUp: () => prev(),
66
+ enter: () => select()
67
+ }">
68
+ ```
69
+
70
+ ### 3. `v-a11y-aria`
71
+ Dynamic ARIA attributes management.
72
+
73
+ ```vue
74
+ <button v-a11y-aria="{
75
+ label: 'Close dialog',
76
+ expanded: isOpen,
77
+ controls: 'menu-id'
78
+ }">
79
+ ```
80
+
81
+ ### 4. `v-a11y-announce`
82
+ Screen reader announcements.
83
+
84
+ ```vue
85
+ <div v-a11y-announce="statusMessage"></div>
86
+ ```
87
+
88
+ ### 5. `v-a11y-trap-focus`
89
+ Trap focus within modals/dialogs.
90
+
91
+ ```vue
92
+ <div v-a11y-trap-focus="isModalOpen">
93
+ <!-- Focus stays within this container -->
94
+ </div>
95
+ ```
96
+
97
+ ### 6. `v-a11y-skip-link`
98
+ Skip navigation links for keyboard users.
99
+
100
+ ```vue
101
+ <a v-a11y-skip-link href="#main-content">Skip to main content</a>
102
+ ```
103
+
104
+ ### 7. `v-a11y-skip`
105
+ Skip elements from keyboard navigation.
106
+
107
+ ```vue
108
+ <!-- Always skip -->
109
+ <div v-a11y-skip>Hidden content</div>
110
+
111
+ <!-- Conditional skip -->
112
+ <button v-a11y-skip="item.disabled">Button</button>
113
+
114
+ <!-- With visual indication -->
115
+ <div v-a11y-skip.visual="item.inactive">Grayed out</div>
116
+
117
+ <!-- Block all interactions -->
118
+ <div v-a11y-skip.noInteraction="isProcessing">Processing...</div>
119
+ ```
120
+
121
+ ## 🎨 Real-World Examples
122
+
123
+ ### Accessible Navigation Menu
124
+
125
+ ```vue
126
+ <template>
127
+ <nav>
128
+ <div v-a11y-announce="announcement"></div>
129
+ <ul v-a11y-keyboard="keyboardHandlers" tabindex="0">
130
+ <li v-for="item in menuItems" :key="item.id">
131
+ <button
132
+ v-a11y-aria="{
133
+ expanded: item.isOpen,
134
+ controls: `menu-${item.id}`
135
+ }"
136
+ @click="toggle(item)"
137
+ >
138
+ {{ item.label }}
139
+ </button>
140
+ </li>
141
+ </ul>
142
+ </nav>
143
+ </template>
144
+
145
+ <script>
146
+ export default {
147
+ data() {
148
+ return {
149
+ announcement: '',
150
+ menuItems: [
151
+ { id: 1, label: 'Home', isOpen: false },
152
+ { id: 2, label: 'Products', isOpen: false }
153
+ ]
154
+ }
155
+ },
156
+ computed: {
157
+ keyboardHandlers() {
158
+ return {
159
+ arrowDown: () => this.navigateNext(),
160
+ arrowUp: () => this.navigatePrev(),
161
+ enter: () => this.selectItem()
162
+ }
163
+ }
164
+ }
165
+ }
166
+ </script>
167
+ ```
168
+
169
+ ### Accessible Modal Dialog
170
+
171
+ ```vue
172
+ <template>
173
+ <div v-if="isOpen" v-a11y-trap-focus="isOpen" class="modal">
174
+ <div v-a11y-announce="modalAnnouncement"></div>
175
+ <h2 v-a11y-focus>Modal Title</h2>
176
+ <button
177
+ v-a11y-aria="{ label: 'Close modal' }"
178
+ @click="close"
179
+ >
180
+ ×
181
+ </button>
182
+ </div>
183
+ </template>
184
+ ```
185
+
186
+ ## 📖 Documentation
187
+
188
+ For complete documentation, examples, and interactive demos, visit:
189
+ - [GitHub Repository](https://github.com/shkelqimemerllahu/vue-a11y-directives)
190
+ - [Live Documentation](https://vue-a11y-directives.netlify.app) (coming soon)
191
+
192
+ ## 🤝 Contributing
193
+
194
+ Contributions are welcome! Please feel free to submit a Pull Request.
195
+
196
+ ## 📄 License
197
+
198
+ MIT © [Shkelqim Emerllahu](https://github.com/shkelqimemerllahu)
199
+
200
+ ## 🌟 Show Your Support
201
+
202
+ Give a ⭐️ if this project helped you!
203
+
204
+ ## 📞 Support
205
+
206
+ - Create an [issue](https://github.com/shkelqimemerllahu/vue-a11y-directives/issues)
207
+ - Follow me on [GitHub](https://github.com/shkelqimemerllahu)
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const y={mounted(e,i){const t=typeof i.value=="object"?i.value:{delay:i.value||0},n=t.delay||0,a=t.select||!1;setTimeout(()=>{e.focus&&(e.focus(),a&&(e.tagName==="INPUT"||e.tagName==="TEXTAREA")&&e.select())},n)}};function u(e){if(!e)return[];const i=["a[href]","button:not([disabled])","textarea:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])",'[tabindex]:not([tabindex="-1"])','[contenteditable="true"]',"audio[controls]","video[controls]"];return Array.from(e.querySelectorAll(i.join(", "))).filter(n=>{const a=window.getComputedStyle(n);return n.offsetWidth>0&&n.offsetHeight>0&&a.visibility!=="hidden"&&a.display!=="none"})}function c(e,i){Object.entries(i).forEach(([t,n])=>{n!=null&&e.setAttribute(t,n)})}function S(e,i){i.forEach(t=>{e.removeAttribute(t)})}function d(e,i="polite"){const t=document.createElement("div");t.setAttribute("role","status"),t.setAttribute("aria-live",i),t.setAttribute("aria-atomic","true"),t.style.position="absolute",t.style.left="-10000px",t.style.width="1px",t.style.height="1px",t.style.overflow="hidden",document.body.appendChild(t),setTimeout(()=>{t.textContent=e},100),setTimeout(()=>{document.body.removeChild(t)},3e3)}function w(e){const i=u(e);return i.length>0?i[0]:null}function x(e){const i=u(e);return i.length>0?i[i.length-1]:null}function E(e){return e.contains(document.activeElement)}function D(){return document.activeElement}function F(e){e&&e.focus&&e.focus()}function T(e="a11y"){return`${e}-${Math.random().toString(36).substr(2,9)}`}const p={mounted(e,i){const n=(i.value||{}).autoFocus!==!1,a=document.activeElement;e.__previousFocus=a,n&&setTimeout(()=>{const r=u(e);r.length>0&&r[0].focus()},100);const o=r=>{if(r.key!=="Tab")return;const s=u(e);if(s.length===0)return;const l=s[0],f=s[s.length-1];r.shiftKey&&document.activeElement===l?(r.preventDefault(),f.focus()):!r.shiftKey&&document.activeElement===f&&(r.preventDefault(),l.focus())};e.addEventListener("keydown",o),e.__handleKeydown=o},unmounted(e){e.__handleKeydown&&(e.removeEventListener("keydown",e.__handleKeydown),delete e.__handleKeydown),e.__previousFocus&&(setTimeout(()=>{if(e.__previousFocus&&document.body.contains(e.__previousFocus)&&typeof e.__previousFocus.focus=="function")try{e.__previousFocus.focus()}catch(i){console.warn("Could not restore focus:",i)}},0),delete e.__previousFocus)}},v={mounted(e,i){const t=i.value||{};console.log("Keyboard directive mounted with config:",t);const n=a=>{console.log("Key pressed:",a.key,"Config:",t);const o={Enter:"enter"," ":"space",Escape:"escape",ArrowUp:"arrowUp",ArrowDown:"arrowDown",ArrowLeft:"arrowLeft",ArrowRight:"arrowRight",Tab:"tab",Delete:"delete",Backspace:"backspace"};let r="";(a.ctrlKey||a.metaKey)&&(r+="ctrl+"),a.altKey&&(r+="alt+"),a.shiftKey&&(r+="shift+"),r+=a.key.toLowerCase();const s=o[a.key];if(s&&typeof t[s]=="function"){a.preventDefault(),t[s](a);return}if(t[r]&&typeof t[r]=="function"){a.preventDefault(),t[r](a);return}if(t[a.key]&&typeof t[a.key]=="function"){a.preventDefault(),t[a.key](a);return}if(t.arrows&&["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(a.key)&&typeof t.arrows=="function"){a.preventDefault(),t.arrows(a);return}if(t.handlers){if(t.handlers[a.key]&&typeof t.handlers[a.key]=="function"){a.preventDefault(),t.handlers[a.key](a);return}if(t.handlers[r]&&typeof t.handlers[r]=="function"){a.preventDefault(),t.handlers[r](a);return}}if(t.custom&&typeof t.custom=="function"){t.custom(a);return}t.enter===!0&&a.key==="Enter"&&!["INPUT","TEXTAREA","SELECT"].includes(a.target.tagName)&&(a.preventDefault(),e.click&&e.click()),t.space===!0&&a.key===" "&&["BUTTON","A"].includes(e.tagName)&&(a.preventDefault(),e.click&&e.click())};e.addEventListener("keydown",n),e.__keyboardHandler=n},unmounted(e){e.__keyboardHandler&&(e.removeEventListener("keydown",e.__keyboardHandler),delete e.__keyboardHandler)}},m={mounted(e,i){if(!i.value)return;const t=typeof i.value=="string"?{message:i.value,priority:"polite"}:i.value;t&&t.message&&d(t.message,t.priority||"polite")},updated(e,i){if(i.value!==i.oldValue&&i.value){const t=typeof i.value=="string"?{message:i.value,priority:"polite"}:i.value;t&&t.message&&d(t.message,t.priority||"polite")}}},_={mounted(e,i){const t=i.value,n=a=>{a.preventDefault();const o=document.querySelector(t);o&&(o.hasAttribute("tabindex")||o.setAttribute("tabindex","-1"),o.focus(),o.scrollIntoView({behavior:"smooth",block:"start"}))};e.addEventListener("click",n),e.__skipLinkHandler=n,e.setAttribute("role","link"),t&&e.setAttribute("aria-label",`Skip to ${t.replace("#","")}`)},unmounted(e){e.__skipLinkHandler&&(e.removeEventListener("click",e.__skipLinkHandler),delete e.__skipLinkHandler)}},k={mounted(e,i){b(e,i)},updated(e,i){b(e,i)},unmounted(e){delete e.__a11ySkipOriginalTabIndex,delete e.__a11ySkipOriginalAriaHidden,delete e.__a11ySkipChildrenState}};function b(e,i){if(i.value===void 0?!0:!!i.value){e.__a11ySkipOriginalTabIndex===void 0&&(e.__a11ySkipOriginalTabIndex=e.getAttribute("tabindex")),e.__a11ySkipOriginalAriaHidden===void 0&&(e.__a11ySkipOriginalAriaHidden=e.getAttribute("aria-hidden")),e.setAttribute("tabindex","-1"),e.setAttribute("aria-hidden","true"),e.setAttribute("data-a11y-skip","true");const n=e.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');e.__a11ySkipChildrenState||(e.__a11ySkipChildrenState=[]),n.forEach((a,o)=>{e.__a11ySkipChildrenState[o]||(e.__a11ySkipChildrenState[o]={tabIndex:a.getAttribute("tabindex"),disabled:a.disabled}),a.setAttribute("tabindex","-1"),"disabled"in a&&(a.disabled=!0)}),i.modifiers.noInteraction&&(e.style.pointerEvents="none"),i.modifiers.visual&&(e.style.opacity="0.5",e.style.filter="grayscale(100%)",e.style.pointerEvents="none")}else{e.__a11ySkipOriginalTabIndex!==void 0?e.__a11ySkipOriginalTabIndex===null?e.removeAttribute("tabindex"):e.setAttribute("tabindex",e.__a11ySkipOriginalTabIndex):e.removeAttribute("tabindex"),e.__a11ySkipOriginalAriaHidden!==void 0?e.__a11ySkipOriginalAriaHidden===null?e.removeAttribute("aria-hidden"):e.setAttribute("aria-hidden",e.__a11ySkipOriginalAriaHidden):e.removeAttribute("aria-hidden"),e.removeAttribute("data-a11y-skip");const n=e.querySelectorAll("a, button, input, select, textarea, [tabindex]");e.__a11ySkipChildrenState&&n.forEach((a,o)=>{const r=e.__a11ySkipChildrenState[o];r&&(r.tabIndex===null?a.removeAttribute("tabindex"):a.setAttribute("tabindex",r.tabIndex),"disabled"in a&&(a.disabled=r.disabled))}),i.modifiers.noInteraction&&(e.style.pointerEvents=""),i.modifiers.visual&&(e.style.opacity="",e.style.filter="",e.style.pointerEvents="")}}const h={mounted(e,i){const t=i.value||{},n={label:"aria-label",labelledby:"aria-labelledby",describedby:"aria-describedby",expanded:"aria-expanded",pressed:"aria-pressed",selected:"aria-selected",checked:"aria-checked",disabled:"aria-disabled",hidden:"aria-hidden",invalid:"aria-invalid",required:"aria-required",live:"aria-live",atomic:"aria-atomic",busy:"aria-busy",controls:"aria-controls",owns:"aria-owns",haspopup:"aria-haspopup",level:"aria-level",modal:"aria-modal",multiselectable:"aria-multiselectable",orientation:"aria-orientation",placeholder:"aria-placeholder",readonly:"aria-readonly",relevant:"aria-relevant",valuemax:"aria-valuemax",valuemin:"aria-valuemin",valuenow:"aria-valuenow",valuetext:"aria-valuetext"},a={};Object.entries(t).forEach(([o,r])=>{const s=n[o]||o;a[s]=r}),c(e,a),e.__ariaAttributes=Object.keys(a)},updated(e,i){if(JSON.stringify(i.value)!==JSON.stringify(i.oldValue)){e.__ariaAttributes&&e.__ariaAttributes.forEach(o=>e.removeAttribute(o));const t=i.value||{},n={label:"aria-label",labelledby:"aria-labelledby",describedby:"aria-describedby",expanded:"aria-expanded",pressed:"aria-pressed",selected:"aria-selected",checked:"aria-checked",disabled:"aria-disabled",hidden:"aria-hidden",invalid:"aria-invalid",required:"aria-required",live:"aria-live",atomic:"aria-atomic",busy:"aria-busy",controls:"aria-controls",owns:"aria-owns",haspopup:"aria-haspopup",level:"aria-level",modal:"aria-modal"},a={};Object.entries(t).forEach(([o,r])=>{const s=n[o]||o;a[s]=r}),c(e,a),e.__ariaAttributes=Object.keys(a)}},unmounted(e){e.__ariaAttributes&&(e.__ariaAttributes.forEach(i=>e.removeAttribute(i)),delete e.__ariaAttributes)}},A={"a11y-focus":y,"a11y-trap-focus":p,"a11y-keyboard":v,"a11y-announce":m,"a11y-skip-link":_,"a11y-skip":k,"a11y-aria":h};function g(e){Object.entries(A).forEach(([i,t])=>{e.directive(i,t)})}const O={install:g};exports.a11yDirectives=A;exports.announce=d;exports.announceDirective=m;exports.ariaDirective=h;exports.containsActiveElement=E;exports.default=O;exports.focusDirective=y;exports.generateId=T;exports.getFirstFocusable=w;exports.getFocusableElements=u;exports.getLastFocusable=x;exports.installA11yDirectives=g;exports.keyboardDirective=v;exports.removeAriaAttributes=S;exports.restoreFocus=F;exports.saveFocus=D;exports.setAriaAttributes=c;exports.skipDirective=k;exports.skipLinkDirective=_;exports.trapFocusDirective=p;
@@ -0,0 +1,315 @@
1
+ const p = {
2
+ mounted(e, i) {
3
+ const t = typeof i.value == "object" ? i.value : { delay: i.value || 0 }, n = t.delay || 0, a = t.select || !1;
4
+ setTimeout(() => {
5
+ e.focus && (e.focus(), a && (e.tagName === "INPUT" || e.tagName === "TEXTAREA") && e.select());
6
+ }, n);
7
+ }
8
+ };
9
+ function u(e) {
10
+ if (!e) return [];
11
+ const i = [
12
+ "a[href]",
13
+ "button:not([disabled])",
14
+ "textarea:not([disabled])",
15
+ 'input:not([disabled]):not([type="hidden"])',
16
+ "select:not([disabled])",
17
+ '[tabindex]:not([tabindex="-1"])',
18
+ '[contenteditable="true"]',
19
+ "audio[controls]",
20
+ "video[controls]"
21
+ ];
22
+ return Array.from(e.querySelectorAll(i.join(", "))).filter((n) => {
23
+ const a = window.getComputedStyle(n);
24
+ return n.offsetWidth > 0 && n.offsetHeight > 0 && a.visibility !== "hidden" && a.display !== "none";
25
+ });
26
+ }
27
+ function l(e, i) {
28
+ Object.entries(i).forEach(([t, n]) => {
29
+ n != null && e.setAttribute(t, n);
30
+ });
31
+ }
32
+ function S(e, i) {
33
+ i.forEach((t) => {
34
+ e.removeAttribute(t);
35
+ });
36
+ }
37
+ function f(e, i = "polite") {
38
+ const t = document.createElement("div");
39
+ t.setAttribute("role", "status"), t.setAttribute("aria-live", i), t.setAttribute("aria-atomic", "true"), t.style.position = "absolute", t.style.left = "-10000px", t.style.width = "1px", t.style.height = "1px", t.style.overflow = "hidden", document.body.appendChild(t), setTimeout(() => {
40
+ t.textContent = e;
41
+ }, 100), setTimeout(() => {
42
+ document.body.removeChild(t);
43
+ }, 3e3);
44
+ }
45
+ function w(e) {
46
+ const i = u(e);
47
+ return i.length > 0 ? i[0] : null;
48
+ }
49
+ function x(e) {
50
+ const i = u(e);
51
+ return i.length > 0 ? i[i.length - 1] : null;
52
+ }
53
+ function E(e) {
54
+ return e.contains(document.activeElement);
55
+ }
56
+ function D() {
57
+ return document.activeElement;
58
+ }
59
+ function T(e) {
60
+ e && e.focus && e.focus();
61
+ }
62
+ function O(e = "a11y") {
63
+ return `${e}-${Math.random().toString(36).substr(2, 9)}`;
64
+ }
65
+ const b = {
66
+ mounted(e, i) {
67
+ const n = (i.value || {}).autoFocus !== !1, a = document.activeElement;
68
+ e.__previousFocus = a, n && setTimeout(() => {
69
+ const r = u(e);
70
+ r.length > 0 && r[0].focus();
71
+ }, 100);
72
+ const o = (r) => {
73
+ if (r.key !== "Tab") return;
74
+ const s = u(e);
75
+ if (s.length === 0) return;
76
+ const d = s[0], c = s[s.length - 1];
77
+ r.shiftKey && document.activeElement === d ? (r.preventDefault(), c.focus()) : !r.shiftKey && document.activeElement === c && (r.preventDefault(), d.focus());
78
+ };
79
+ e.addEventListener("keydown", o), e.__handleKeydown = o;
80
+ },
81
+ unmounted(e) {
82
+ e.__handleKeydown && (e.removeEventListener("keydown", e.__handleKeydown), delete e.__handleKeydown), e.__previousFocus && (setTimeout(() => {
83
+ if (e.__previousFocus && document.body.contains(e.__previousFocus) && typeof e.__previousFocus.focus == "function")
84
+ try {
85
+ e.__previousFocus.focus();
86
+ } catch (i) {
87
+ console.warn("Could not restore focus:", i);
88
+ }
89
+ }, 0), delete e.__previousFocus);
90
+ }
91
+ }, v = {
92
+ mounted(e, i) {
93
+ const t = i.value || {};
94
+ console.log("Keyboard directive mounted with config:", t);
95
+ const n = (a) => {
96
+ console.log("Key pressed:", a.key, "Config:", t);
97
+ const o = {
98
+ Enter: "enter",
99
+ " ": "space",
100
+ Escape: "escape",
101
+ ArrowUp: "arrowUp",
102
+ ArrowDown: "arrowDown",
103
+ ArrowLeft: "arrowLeft",
104
+ ArrowRight: "arrowRight",
105
+ Tab: "tab",
106
+ Delete: "delete",
107
+ Backspace: "backspace"
108
+ };
109
+ let r = "";
110
+ (a.ctrlKey || a.metaKey) && (r += "ctrl+"), a.altKey && (r += "alt+"), a.shiftKey && (r += "shift+"), r += a.key.toLowerCase();
111
+ const s = o[a.key];
112
+ if (s && typeof t[s] == "function") {
113
+ a.preventDefault(), t[s](a);
114
+ return;
115
+ }
116
+ if (t[r] && typeof t[r] == "function") {
117
+ a.preventDefault(), t[r](a);
118
+ return;
119
+ }
120
+ if (t[a.key] && typeof t[a.key] == "function") {
121
+ a.preventDefault(), t[a.key](a);
122
+ return;
123
+ }
124
+ if (t.arrows && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(a.key) && typeof t.arrows == "function") {
125
+ a.preventDefault(), t.arrows(a);
126
+ return;
127
+ }
128
+ if (t.handlers) {
129
+ if (t.handlers[a.key] && typeof t.handlers[a.key] == "function") {
130
+ a.preventDefault(), t.handlers[a.key](a);
131
+ return;
132
+ }
133
+ if (t.handlers[r] && typeof t.handlers[r] == "function") {
134
+ a.preventDefault(), t.handlers[r](a);
135
+ return;
136
+ }
137
+ }
138
+ if (t.custom && typeof t.custom == "function") {
139
+ t.custom(a);
140
+ return;
141
+ }
142
+ t.enter === !0 && a.key === "Enter" && !["INPUT", "TEXTAREA", "SELECT"].includes(a.target.tagName) && (a.preventDefault(), e.click && e.click()), t.space === !0 && a.key === " " && ["BUTTON", "A"].includes(e.tagName) && (a.preventDefault(), e.click && e.click());
143
+ };
144
+ e.addEventListener("keydown", n), e.__keyboardHandler = n;
145
+ },
146
+ unmounted(e) {
147
+ e.__keyboardHandler && (e.removeEventListener("keydown", e.__keyboardHandler), delete e.__keyboardHandler);
148
+ }
149
+ }, m = {
150
+ mounted(e, i) {
151
+ if (!i.value) return;
152
+ const t = typeof i.value == "string" ? { message: i.value, priority: "polite" } : i.value;
153
+ t && t.message && f(t.message, t.priority || "polite");
154
+ },
155
+ updated(e, i) {
156
+ if (i.value !== i.oldValue && i.value) {
157
+ const t = typeof i.value == "string" ? { message: i.value, priority: "polite" } : i.value;
158
+ t && t.message && f(t.message, t.priority || "polite");
159
+ }
160
+ }
161
+ }, _ = {
162
+ mounted(e, i) {
163
+ const t = i.value, n = (a) => {
164
+ a.preventDefault();
165
+ const o = document.querySelector(t);
166
+ o && (o.hasAttribute("tabindex") || o.setAttribute("tabindex", "-1"), o.focus(), o.scrollIntoView({ behavior: "smooth", block: "start" }));
167
+ };
168
+ e.addEventListener("click", n), e.__skipLinkHandler = n, e.setAttribute("role", "link"), t && e.setAttribute("aria-label", `Skip to ${t.replace("#", "")}`);
169
+ },
170
+ unmounted(e) {
171
+ e.__skipLinkHandler && (e.removeEventListener("click", e.__skipLinkHandler), delete e.__skipLinkHandler);
172
+ }
173
+ }, k = {
174
+ mounted(e, i) {
175
+ y(e, i);
176
+ },
177
+ updated(e, i) {
178
+ y(e, i);
179
+ },
180
+ unmounted(e) {
181
+ delete e.__a11ySkipOriginalTabIndex, delete e.__a11ySkipOriginalAriaHidden, delete e.__a11ySkipChildrenState;
182
+ }
183
+ };
184
+ function y(e, i) {
185
+ if (i.value === void 0 ? !0 : !!i.value) {
186
+ e.__a11ySkipOriginalTabIndex === void 0 && (e.__a11ySkipOriginalTabIndex = e.getAttribute("tabindex")), e.__a11ySkipOriginalAriaHidden === void 0 && (e.__a11ySkipOriginalAriaHidden = e.getAttribute("aria-hidden")), e.setAttribute("tabindex", "-1"), e.setAttribute("aria-hidden", "true"), e.setAttribute("data-a11y-skip", "true");
187
+ const n = e.querySelectorAll(
188
+ 'a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
189
+ );
190
+ e.__a11ySkipChildrenState || (e.__a11ySkipChildrenState = []), n.forEach((a, o) => {
191
+ e.__a11ySkipChildrenState[o] || (e.__a11ySkipChildrenState[o] = {
192
+ tabIndex: a.getAttribute("tabindex"),
193
+ disabled: a.disabled
194
+ }), a.setAttribute("tabindex", "-1"), "disabled" in a && (a.disabled = !0);
195
+ }), i.modifiers.noInteraction && (e.style.pointerEvents = "none"), i.modifiers.visual && (e.style.opacity = "0.5", e.style.filter = "grayscale(100%)", e.style.pointerEvents = "none");
196
+ } else {
197
+ e.__a11ySkipOriginalTabIndex !== void 0 ? e.__a11ySkipOriginalTabIndex === null ? e.removeAttribute("tabindex") : e.setAttribute("tabindex", e.__a11ySkipOriginalTabIndex) : e.removeAttribute("tabindex"), e.__a11ySkipOriginalAriaHidden !== void 0 ? e.__a11ySkipOriginalAriaHidden === null ? e.removeAttribute("aria-hidden") : e.setAttribute("aria-hidden", e.__a11ySkipOriginalAriaHidden) : e.removeAttribute("aria-hidden"), e.removeAttribute("data-a11y-skip");
198
+ const n = e.querySelectorAll(
199
+ "a, button, input, select, textarea, [tabindex]"
200
+ );
201
+ e.__a11ySkipChildrenState && n.forEach((a, o) => {
202
+ const r = e.__a11ySkipChildrenState[o];
203
+ r && (r.tabIndex === null ? a.removeAttribute("tabindex") : a.setAttribute("tabindex", r.tabIndex), "disabled" in a && (a.disabled = r.disabled));
204
+ }), i.modifiers.noInteraction && (e.style.pointerEvents = ""), i.modifiers.visual && (e.style.opacity = "", e.style.filter = "", e.style.pointerEvents = "");
205
+ }
206
+ }
207
+ const h = {
208
+ mounted(e, i) {
209
+ const t = i.value || {}, n = {
210
+ label: "aria-label",
211
+ labelledby: "aria-labelledby",
212
+ describedby: "aria-describedby",
213
+ expanded: "aria-expanded",
214
+ pressed: "aria-pressed",
215
+ selected: "aria-selected",
216
+ checked: "aria-checked",
217
+ disabled: "aria-disabled",
218
+ hidden: "aria-hidden",
219
+ invalid: "aria-invalid",
220
+ required: "aria-required",
221
+ live: "aria-live",
222
+ atomic: "aria-atomic",
223
+ busy: "aria-busy",
224
+ controls: "aria-controls",
225
+ owns: "aria-owns",
226
+ haspopup: "aria-haspopup",
227
+ level: "aria-level",
228
+ modal: "aria-modal",
229
+ multiselectable: "aria-multiselectable",
230
+ orientation: "aria-orientation",
231
+ placeholder: "aria-placeholder",
232
+ readonly: "aria-readonly",
233
+ relevant: "aria-relevant",
234
+ valuemax: "aria-valuemax",
235
+ valuemin: "aria-valuemin",
236
+ valuenow: "aria-valuenow",
237
+ valuetext: "aria-valuetext"
238
+ }, a = {};
239
+ Object.entries(t).forEach(([o, r]) => {
240
+ const s = n[o] || o;
241
+ a[s] = r;
242
+ }), l(e, a), e.__ariaAttributes = Object.keys(a);
243
+ },
244
+ updated(e, i) {
245
+ if (JSON.stringify(i.value) !== JSON.stringify(i.oldValue)) {
246
+ e.__ariaAttributes && e.__ariaAttributes.forEach((o) => e.removeAttribute(o));
247
+ const t = i.value || {}, n = {
248
+ label: "aria-label",
249
+ labelledby: "aria-labelledby",
250
+ describedby: "aria-describedby",
251
+ expanded: "aria-expanded",
252
+ pressed: "aria-pressed",
253
+ selected: "aria-selected",
254
+ checked: "aria-checked",
255
+ disabled: "aria-disabled",
256
+ hidden: "aria-hidden",
257
+ invalid: "aria-invalid",
258
+ required: "aria-required",
259
+ live: "aria-live",
260
+ atomic: "aria-atomic",
261
+ busy: "aria-busy",
262
+ controls: "aria-controls",
263
+ owns: "aria-owns",
264
+ haspopup: "aria-haspopup",
265
+ level: "aria-level",
266
+ modal: "aria-modal"
267
+ }, a = {};
268
+ Object.entries(t).forEach(([o, r]) => {
269
+ const s = n[o] || o;
270
+ a[s] = r;
271
+ }), l(e, a), e.__ariaAttributes = Object.keys(a);
272
+ }
273
+ },
274
+ unmounted(e) {
275
+ e.__ariaAttributes && (e.__ariaAttributes.forEach((i) => e.removeAttribute(i)), delete e.__ariaAttributes);
276
+ }
277
+ }, A = {
278
+ "a11y-focus": p,
279
+ "a11y-trap-focus": b,
280
+ "a11y-keyboard": v,
281
+ "a11y-announce": m,
282
+ "a11y-skip-link": _,
283
+ "a11y-skip": k,
284
+ "a11y-aria": h
285
+ };
286
+ function g(e) {
287
+ Object.entries(A).forEach(([i, t]) => {
288
+ e.directive(i, t);
289
+ });
290
+ }
291
+ const F = {
292
+ install: g
293
+ };
294
+ export {
295
+ A as a11yDirectives,
296
+ f as announce,
297
+ m as announceDirective,
298
+ h as ariaDirective,
299
+ E as containsActiveElement,
300
+ F as default,
301
+ p as focusDirective,
302
+ O as generateId,
303
+ w as getFirstFocusable,
304
+ u as getFocusableElements,
305
+ x as getLastFocusable,
306
+ g as installA11yDirectives,
307
+ v as keyboardDirective,
308
+ S as removeAriaAttributes,
309
+ T as restoreFocus,
310
+ D as saveFocus,
311
+ l as setAriaAttributes,
312
+ k as skipDirective,
313
+ _ as skipLinkDirective,
314
+ b as trapFocusDirective
315
+ };
@@ -0,0 +1 @@
1
+ (function(r,d){typeof exports=="object"&&typeof module<"u"?d(exports):typeof define=="function"&&define.amd?define(["exports"],d):(r=typeof globalThis<"u"?globalThis:r||self,d(r.VueA11yDirectives={}))})(this,function(r){"use strict";const d={mounted(e,a){const t=typeof a.value=="object"?a.value:{delay:a.value||0},o=t.delay||0,i=t.select||!1;setTimeout(()=>{e.focus&&(e.focus(),i&&(e.tagName==="INPUT"||e.tagName==="TEXTAREA")&&e.select())},o)}};function c(e){if(!e)return[];const a=["a[href]","button:not([disabled])","textarea:not([disabled])",'input:not([disabled]):not([type="hidden"])',"select:not([disabled])",'[tabindex]:not([tabindex="-1"])','[contenteditable="true"]',"audio[controls]","video[controls]"];return Array.from(e.querySelectorAll(a.join(", "))).filter(o=>{const i=window.getComputedStyle(o);return o.offsetWidth>0&&o.offsetHeight>0&&i.visibility!=="hidden"&&i.display!=="none"})}function l(e,a){Object.entries(a).forEach(([t,o])=>{o!=null&&e.setAttribute(t,o)})}function w(e,a){a.forEach(t=>{e.removeAttribute(t)})}function f(e,a="polite"){const t=document.createElement("div");t.setAttribute("role","status"),t.setAttribute("aria-live",a),t.setAttribute("aria-atomic","true"),t.style.position="absolute",t.style.left="-10000px",t.style.width="1px",t.style.height="1px",t.style.overflow="hidden",document.body.appendChild(t),setTimeout(()=>{t.textContent=e},100),setTimeout(()=>{document.body.removeChild(t)},3e3)}function E(e){const a=c(e);return a.length>0?a[0]:null}function D(e){const a=c(e);return a.length>0?a[a.length-1]:null}function F(e){return e.contains(document.activeElement)}function T(){return document.activeElement}function x(e){e&&e.focus&&e.focus()}function O(e="a11y"){return`${e}-${Math.random().toString(36).substr(2,9)}`}const y={mounted(e,a){const o=(a.value||{}).autoFocus!==!1,i=document.activeElement;e.__previousFocus=i,o&&setTimeout(()=>{const n=c(e);n.length>0&&n[0].focus()},100);const s=n=>{if(n.key!=="Tab")return;const u=c(e);if(u.length===0)return;const g=u[0],S=u[u.length-1];n.shiftKey&&document.activeElement===g?(n.preventDefault(),S.focus()):!n.shiftKey&&document.activeElement===S&&(n.preventDefault(),g.focus())};e.addEventListener("keydown",s),e.__handleKeydown=s},unmounted(e){e.__handleKeydown&&(e.removeEventListener("keydown",e.__handleKeydown),delete e.__handleKeydown),e.__previousFocus&&(setTimeout(()=>{if(e.__previousFocus&&document.body.contains(e.__previousFocus)&&typeof e.__previousFocus.focus=="function")try{e.__previousFocus.focus()}catch(a){console.warn("Could not restore focus:",a)}},0),delete e.__previousFocus)}},b={mounted(e,a){const t=a.value||{};console.log("Keyboard directive mounted with config:",t);const o=i=>{console.log("Key pressed:",i.key,"Config:",t);const s={Enter:"enter"," ":"space",Escape:"escape",ArrowUp:"arrowUp",ArrowDown:"arrowDown",ArrowLeft:"arrowLeft",ArrowRight:"arrowRight",Tab:"tab",Delete:"delete",Backspace:"backspace"};let n="";(i.ctrlKey||i.metaKey)&&(n+="ctrl+"),i.altKey&&(n+="alt+"),i.shiftKey&&(n+="shift+"),n+=i.key.toLowerCase();const u=s[i.key];if(u&&typeof t[u]=="function"){i.preventDefault(),t[u](i);return}if(t[n]&&typeof t[n]=="function"){i.preventDefault(),t[n](i);return}if(t[i.key]&&typeof t[i.key]=="function"){i.preventDefault(),t[i.key](i);return}if(t.arrows&&["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(i.key)&&typeof t.arrows=="function"){i.preventDefault(),t.arrows(i);return}if(t.handlers){if(t.handlers[i.key]&&typeof t.handlers[i.key]=="function"){i.preventDefault(),t.handlers[i.key](i);return}if(t.handlers[n]&&typeof t.handlers[n]=="function"){i.preventDefault(),t.handlers[n](i);return}}if(t.custom&&typeof t.custom=="function"){t.custom(i);return}t.enter===!0&&i.key==="Enter"&&!["INPUT","TEXTAREA","SELECT"].includes(i.target.tagName)&&(i.preventDefault(),e.click&&e.click()),t.space===!0&&i.key===" "&&["BUTTON","A"].includes(e.tagName)&&(i.preventDefault(),e.click&&e.click())};e.addEventListener("keydown",o),e.__keyboardHandler=o},unmounted(e){e.__keyboardHandler&&(e.removeEventListener("keydown",e.__keyboardHandler),delete e.__keyboardHandler)}},p={mounted(e,a){if(!a.value)return;const t=typeof a.value=="string"?{message:a.value,priority:"polite"}:a.value;t&&t.message&&f(t.message,t.priority||"polite")},updated(e,a){if(a.value!==a.oldValue&&a.value){const t=typeof a.value=="string"?{message:a.value,priority:"polite"}:a.value;t&&t.message&&f(t.message,t.priority||"polite")}}},v={mounted(e,a){const t=a.value,o=i=>{i.preventDefault();const s=document.querySelector(t);s&&(s.hasAttribute("tabindex")||s.setAttribute("tabindex","-1"),s.focus(),s.scrollIntoView({behavior:"smooth",block:"start"}))};e.addEventListener("click",o),e.__skipLinkHandler=o,e.setAttribute("role","link"),t&&e.setAttribute("aria-label",`Skip to ${t.replace("#","")}`)},unmounted(e){e.__skipLinkHandler&&(e.removeEventListener("click",e.__skipLinkHandler),delete e.__skipLinkHandler)}},m={mounted(e,a){_(e,a)},updated(e,a){_(e,a)},unmounted(e){delete e.__a11ySkipOriginalTabIndex,delete e.__a11ySkipOriginalAriaHidden,delete e.__a11ySkipChildrenState}};function _(e,a){if(a.value===void 0?!0:!!a.value){e.__a11ySkipOriginalTabIndex===void 0&&(e.__a11ySkipOriginalTabIndex=e.getAttribute("tabindex")),e.__a11ySkipOriginalAriaHidden===void 0&&(e.__a11ySkipOriginalAriaHidden=e.getAttribute("aria-hidden")),e.setAttribute("tabindex","-1"),e.setAttribute("aria-hidden","true"),e.setAttribute("data-a11y-skip","true");const o=e.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');e.__a11ySkipChildrenState||(e.__a11ySkipChildrenState=[]),o.forEach((i,s)=>{e.__a11ySkipChildrenState[s]||(e.__a11ySkipChildrenState[s]={tabIndex:i.getAttribute("tabindex"),disabled:i.disabled}),i.setAttribute("tabindex","-1"),"disabled"in i&&(i.disabled=!0)}),a.modifiers.noInteraction&&(e.style.pointerEvents="none"),a.modifiers.visual&&(e.style.opacity="0.5",e.style.filter="grayscale(100%)",e.style.pointerEvents="none")}else{e.__a11ySkipOriginalTabIndex!==void 0?e.__a11ySkipOriginalTabIndex===null?e.removeAttribute("tabindex"):e.setAttribute("tabindex",e.__a11ySkipOriginalTabIndex):e.removeAttribute("tabindex"),e.__a11ySkipOriginalAriaHidden!==void 0?e.__a11ySkipOriginalAriaHidden===null?e.removeAttribute("aria-hidden"):e.setAttribute("aria-hidden",e.__a11ySkipOriginalAriaHidden):e.removeAttribute("aria-hidden"),e.removeAttribute("data-a11y-skip");const o=e.querySelectorAll("a, button, input, select, textarea, [tabindex]");e.__a11ySkipChildrenState&&o.forEach((i,s)=>{const n=e.__a11ySkipChildrenState[s];n&&(n.tabIndex===null?i.removeAttribute("tabindex"):i.setAttribute("tabindex",n.tabIndex),"disabled"in i&&(i.disabled=n.disabled))}),a.modifiers.noInteraction&&(e.style.pointerEvents=""),a.modifiers.visual&&(e.style.opacity="",e.style.filter="",e.style.pointerEvents="")}}const k={mounted(e,a){const t=a.value||{},o={label:"aria-label",labelledby:"aria-labelledby",describedby:"aria-describedby",expanded:"aria-expanded",pressed:"aria-pressed",selected:"aria-selected",checked:"aria-checked",disabled:"aria-disabled",hidden:"aria-hidden",invalid:"aria-invalid",required:"aria-required",live:"aria-live",atomic:"aria-atomic",busy:"aria-busy",controls:"aria-controls",owns:"aria-owns",haspopup:"aria-haspopup",level:"aria-level",modal:"aria-modal",multiselectable:"aria-multiselectable",orientation:"aria-orientation",placeholder:"aria-placeholder",readonly:"aria-readonly",relevant:"aria-relevant",valuemax:"aria-valuemax",valuemin:"aria-valuemin",valuenow:"aria-valuenow",valuetext:"aria-valuetext"},i={};Object.entries(t).forEach(([s,n])=>{const u=o[s]||s;i[u]=n}),l(e,i),e.__ariaAttributes=Object.keys(i)},updated(e,a){if(JSON.stringify(a.value)!==JSON.stringify(a.oldValue)){e.__ariaAttributes&&e.__ariaAttributes.forEach(s=>e.removeAttribute(s));const t=a.value||{},o={label:"aria-label",labelledby:"aria-labelledby",describedby:"aria-describedby",expanded:"aria-expanded",pressed:"aria-pressed",selected:"aria-selected",checked:"aria-checked",disabled:"aria-disabled",hidden:"aria-hidden",invalid:"aria-invalid",required:"aria-required",live:"aria-live",atomic:"aria-atomic",busy:"aria-busy",controls:"aria-controls",owns:"aria-owns",haspopup:"aria-haspopup",level:"aria-level",modal:"aria-modal"},i={};Object.entries(t).forEach(([s,n])=>{const u=o[s]||s;i[u]=n}),l(e,i),e.__ariaAttributes=Object.keys(i)}},unmounted(e){e.__ariaAttributes&&(e.__ariaAttributes.forEach(a=>e.removeAttribute(a)),delete e.__ariaAttributes)}},h={"a11y-focus":d,"a11y-trap-focus":y,"a11y-keyboard":b,"a11y-announce":p,"a11y-skip-link":v,"a11y-skip":m,"a11y-aria":k};function A(e){Object.entries(h).forEach(([a,t])=>{e.directive(a,t)})}const L={install:A};r.a11yDirectives=h,r.announce=f,r.announceDirective=p,r.ariaDirective=k,r.containsActiveElement=F,r.default=L,r.focusDirective=d,r.generateId=O,r.getFirstFocusable=E,r.getFocusableElements=c,r.getLastFocusable=D,r.installA11yDirectives=A,r.keyboardDirective=b,r.removeAriaAttributes=w,r.restoreFocus=x,r.saveFocus=T,r.setAriaAttributes=l,r.skipDirective=m,r.skipLinkDirective=v,r.trapFocusDirective=y,Object.defineProperties(r,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "vue-a11y-directives",
3
+ "version": "1.0.0",
4
+ "description": "A comprehensive set of Vue 3 directives for building accessible web applications with WCAG compliance",
5
+ "main": "dist/vue-a11y-directives.cjs.js",
6
+ "module": "dist/vue-a11y-directives.esm.js",
7
+ "unpkg": "dist/vue-a11y-directives.min.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "src"
12
+ ],
13
+ "keywords": [
14
+ "vue",
15
+ "vue3",
16
+ "accessibility",
17
+ "a11y",
18
+ "directives",
19
+ "wcag",
20
+ "aria",
21
+ "focus-management",
22
+ "keyboard-navigation",
23
+ "screen-reader"
24
+ ],
25
+ "author": "Shkelqim Emerllahu",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/shkelqimemerllahu/vue-a11y-directives"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/shkelqimemerllahu/vue-a11y-directives/issues"
33
+ },
34
+ "homepage": "https://github.com/shkelqimemerllahu/vue-a11y-directives#readme",
35
+ "scripts": {
36
+ "dev": "vite",
37
+ "build": "vite build",
38
+ "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist",
39
+ "build:docs": "cd docs && npm run build",
40
+ "preview": "vite preview",
41
+ "docs:dev": "cd docs && npm run dev",
42
+ "lint": "eslint src --ext .js,.vue",
43
+ "prepublishOnly": "vite build"
44
+ },
45
+ "peerDependencies": {
46
+ "vue": "^3.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@vitejs/plugin-vue": "^5.0.0",
50
+ "vite": "^5.0.0",
51
+ "vue": "^3.4.0"
52
+ },
53
+ "exports": {
54
+ ".": {
55
+ "import": "./dist/vue-a11y-directives.esm.js",
56
+ "require": "./dist/vue-a11y-directives.cjs.js"
57
+ },
58
+ "./dist/style.css": "./dist/style.css"
59
+ }
60
+ }