From c3cbe1cf8eb02b09db0d08c7c3a5adee8b298d97 Mon Sep 17 00:00:00 2001 From: Dan Popescu Date: Thu, 1 Jan 1970 00:00:00 +0000 Subject: [PATCH] gitpkg --- LICENSE | 21 + README.md | 498 ++++++++++++++++ dist/index.es.js | 340 +++++++++++ dist/index.es.js.map | 1 + dist/index.umd.js | 2 + dist/index.umd.js.map | 1 + dist/styles/index.css | 96 ++++ dist/styles/index.sass | 107 ++++ dist/types/index.d.ts | 39 ++ dist/web-types/index.json | 75 +++ package.json | 113 ++++ src/App.vue | 661 ++++++++++++++++++++++ src/assets/logo.png | Bin 0 -> 57076 bytes src/directives/keyboard-trap/directive.js | 648 +++++++++++++++++++++ src/directives/keyboard-trap/helpers.js | 101 ++++ src/directives/keyboard-trap/index.js | 17 + src/directives/keyboard-trap/options.js | 106 ++++ src/exports.js | 13 + src/main.js | 17 + src/public/styles/index.css | 96 ++++ src/public/styles/index.sass | 107 ++++ src/public/types/index.d.ts | 39 ++ src/public/web-types/index.json | 75 +++ 23 files changed, 3173 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 dist/index.es.js create mode 100644 dist/index.es.js.map create mode 100644 dist/index.umd.js create mode 100644 dist/index.umd.js.map create mode 100644 dist/styles/index.css create mode 100644 dist/styles/index.sass create mode 100644 dist/types/index.d.ts create mode 100644 dist/web-types/index.json create mode 100644 package.json create mode 100644 src/App.vue create mode 100644 src/assets/logo.png create mode 100644 src/directives/keyboard-trap/directive.js create mode 100644 src/directives/keyboard-trap/helpers.js create mode 100644 src/directives/keyboard-trap/index.js create mode 100644 src/directives/keyboard-trap/options.js create mode 100644 src/exports.js create mode 100644 src/main.js create mode 100644 src/public/styles/index.css create mode 100644 src/public/styles/index.sass create mode 100644 src/public/types/index.d.ts create mode 100644 src/public/web-types/index.json diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c3f2fbc --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-present Popescu Dan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a51d47 --- /dev/null +++ b/README.md @@ -0,0 +1,498 @@ +# VueKeyboardTrap (vue-keyboard-trap) + +[![License: MIT](https://img.shields.io/github/license/pdanpdan/vue-keyboard-trap?style=for-the-badge)](https://opensource.org/licenses/MIT)   +[![minzip](https://img.shields.io/bundlephobia/minzip/@pdanpdan/vue-keyboard-trap/latest?style=for-the-badge)](https://bundlephobia.com/result?p=@pdanpdan/vue-keyboard-trap)   +![github release](https://img.shields.io/github/v/tag/pdanpdan/vue-keyboard-trap?sort=semver&style=for-the-badge)   +![jsdelivr hits](https://img.shields.io/jsdelivr/gh/hm/pdanpdan/vue-keyboard-trap?style=for-the-badge)   +![npm release](https://img.shields.io/npm/v/@pdanpdan/vue-keyboard-trap?style=for-the-badge)   +![npm downloads](https://img.shields.io/npm/dm/@pdanpdan/vue-keyboard-trap?style=for-the-badge) + +## Project description + +Vue directive and composable for keyboard navigation - roving movement and trapping inside container. + +Works both for Vue3 and Vue2, as a directive (`v-kbd-trap`) or as a composable (`useKeyboardTrap`). + +[Demo codepen](https://codepen.io/pdanpdan/pen/MWrzLdM) + +[Docs and examples](https://pdanpdan.github.io/vue-keyboard-trap/) + +[Source code, Issues, Discussions](https://github.com/pdanpdan/vue-keyboard-trap) + +## Install + +```bash +pnpm add @pdanpdan/vue-keyboard-trap +``` +or +```bash +yarn add @pdanpdan/vue-keyboard-trap +``` +or +```bash +npm install @pdanpdan/vue-keyboard-trap +``` + +## Playground + +[Demo codepen](https://codepen.io/pdanpdan/pen/MWrzLdM) + +## Usage + +### Usage as ESM + +#### As composable (both Vue3 and Vue2) + +```html + + + +``` + +#### As plugin on Vue3 - directive + +```javascript +import { createApp } from 'vue'; +import { VueKeyboardTrapDirectivePlugin } from '@pdanpdan/vue-keyboard-trap'; +import App from './App.vue'; + +const app = createApp(App); + +app.use(VueKeyboardTrapDirectivePlugin, { + // ...options if required +}); + +app.mount('#app'); +``` + +#### As plugin on Vue2 - directive + +```javascript +import Vue from 'vue'; +import { VueKeyboardTrapDirectivePlugin } from '@pdanpdan/vue-keyboard-trap'; +import App from './App.vue'; + +Vue.use(VueKeyboardTrapDirectivePlugin, { + // ...options if required +}); + +new Vue({ + el: '#app', +}); +``` + +#### Included in specific components (Vue3 script setup) - directive + +```html + +``` + +#### Included in specific components (Vue3 script) - directive + +```html + +``` + +#### Included in specific components (Vue2) - directive + +```html + +``` + +#### User hint styles (cosmetic) + +The directive does not require any CSS styles to work, but for cosmetic purposes (as user hints) some example styles are provided in `dist/styles/index.sass`. + +in Javascript +```javascript +import '@pdanpdan/vue-keyboard-trap/styles'; +``` + +or in SASS +```sass +@import '@pdanpdan/vue-keyboard-trap/styles' +``` + +or (if the `/styles` export is not used by your bundler) + +in Javascript +```javascript +import '@pdanpdan/vue-keyboard-trap/dist/styles/index.sass'; +``` + +or in SASS +```sass +@import '@pdanpdan/vue-keyboard-trap/dist/styles/index.sass' +``` + +### Usage as UMD + +Load the javascript from [https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/index.umd.js](https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/index.umd.js). + +It will expose a global object `VueKeyboardTrap` with `VueKeyboardTrapDirectivePlugin` and `VueKeyboardTrapDirectiveFactory` keys. + +In order to work it requires that `VueDemi` is already loaded on the page. You can do it like this: + +```html + + + + +``` + +#### As composable (both Vue3 and Vue2) + +```javascript +const { ref } = Vue; + +const { useKeyboardTrapFactory } = VueKeyboardTrap; +const useKeyboardTrap = useKeyboardTrapFactory({ + // ...options if required +}); + +const elRef = ref(null); +useKeyboardTrap( + // element (reactive) + elRef, + // modifiers (optional, reactive, default all modifiers are false) + { + roving: true, + }, + // active (optional, reactive, default true) + true +); +``` + +#### As plugin on Vue3 - directive + +```javascript +const { createApp } = Vue; +const { VueKeyboardTrapDirectivePlugin } = VueKeyboardTrap; + +const app = createApp({}); + +app.use(VueKeyboardTrapDirectivePlugin, { + // ...options if required +}); + +app.mount('#app'); +``` + +#### As plugin on Vue2 - directive + +```javascript +const { VueKeyboardTrapDirectivePlugin } = VueKeyboardTrap; + +Vue.use(VueKeyboardTrapDirectivePlugin, { + // ...options if required +}); + +new Vue({ + el: '#app', +}); +``` + +#### As directive on Vue3 - directive + +```javascript +const { createApp } = Vue; +const { VueKeyboardTrapDirectiveFactory } = VueKeyboardTrap; + +const app = createApp({}); + +const { name, directive } = VueKeyboardTrapDirectiveFactory({ + // ...options if required +}); + +app.directive(name, directive); + +app.mount('#app'); +``` + +#### As directive on Vue2 - directive + +```javascript +const { VueKeyboardTrapDirectiveFactory } = VueKeyboardTrap; + +const { name, directive } = VueKeyboardTrapDirectiveFactory({ + // ...options if required +}); + +Vue.directive(name, directive); +``` + +#### User hint styles (cosmetic) + +If you want you can access the CSS cosmetic style (user hints) from [https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/styles/index.css](https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/styles/index.css). + +### Directive configuration options + +| Option | Description | Default | +|--------|-------------|:-------:| +| `name` | snake-case name of the directive (without `v-` prefix) | `kbd-trap` | +| `datasetName` | camelCase name of the `data-attribute` to be set on element when trap is enabled | `v${PascalCase from name}` | +| `focusableSelector` | CSS selector for focusable elements | [see here](#default-focusableselector) | +| `rovingSkipSelector` | CSS selector for elements that should not respond to roving key navigation (input, textarea, ...) | [see here](#default-rovingskipselector) | +| `gridSkipSelector` | CSS selector that will be applied in .roving.grid mode to exclude elements - must be a series of `:not()` selectors | [see here](#default-gridskipselector) | +| `autofocusSelector` | CSS selector for the elements that should be autofocused | [see here](#default-autofocusselector) | +| `trapTabIndex` | tabIndex value to be used when trap element has a tabIndex of -1 and has no `tabindex` attribute | -9999 | + +#### Default `focusableSelector`: + +```css +:focus, +a[href]:not([tabindex^="-"]), +area[href]:not([tabindex^="-"]), +video[controls]:not([tabindex^="-"]), +audio[controls]:not([tabindex^="-"]), +iframe:not([tabindex^="-"]), +[tabindex]:not(slot):not([tabindex^="-"]), +[contenteditable]:not([contenteditable="false"]):not([tabindex^="-"]), +details > summary:first-of-type:not([tabindex^="-"]), +input:not([type="hidden"]):not(fieldset[disabled] input):not([disabled]):not([tabindex^="-"]), +select:not(fieldset[disabled] input):not([disabled]):not([tabindex^="-"]), +textarea:not(fieldset[disabled] input):not([disabled]):not([tabindex^="-"]), +button:not(fieldset[disabled] input):not([disabled]):not([tabindex^="-"]), +fieldset[disabled]:not(fieldset[disabled] fieldset) > legend input:not([type="hidden"]):not([disabled]):not([tabindex^="-"]), +fieldset[disabled]:not(fieldset[disabled] fieldset) > legend select:not([disabled]):not([tabindex^="-"]), +fieldset[disabled]:not(fieldset[disabled] fieldset) > legend textarea:not([disabled]):not([tabindex^="-"]), +fieldset[disabled]:not(fieldset[disabled] fieldset) > legend button:not([disabled]):not([tabindex^="-"]), +[class*="focusable"]:not([disabled]):not([tabindex^="-"]) +``` + +By default `a` tags without href are not focusable - add a `tabindex="0"` attribute on them to make them focusable. +This can be done for all other elements if you want them to be focusable. + +#### Default `rovingSkipSelector`: + +```css +input:not([disabled]):not([type="button"]):not([type="checkbox"]):not([type="file"]):not([type="image"]):not([type="radio"]):not([type="reset"]):not([type="submit"]), +select:not([disabled]), +select:not([disabled]) *, +textarea:not([disabled]), +[contenteditable]:not([contenteditable="false"]), +[contenteditable]:not([contenteditable="false"]) * +``` + +#### Default `gridSkipSelector`: + +```css +:not([disabled]), +:not([tabindex^="-"]) +``` + +#### Default `autofocusSelector`: + +```css +[autofocus]:not([disabled]):not([autofocus="false"]), +[data-autofocus]:not([disabled]):not([data-autofocus="false"]) +``` + +### Dynamic enable/disable + +Use the value of the directive (boolean) to enable/disable it. + +```html +
+``` + +The modifiers are reactive so if you use render functions you can dynamically change the behaviour. + +### Directive modifiers + +| Modifier | Description | +|----------|-------------| +| `.autofocus` | autofocuses the first element that matches [autofocusSelector](#default-autofocusselector) or (if no such element is found) the first focusable child element **when the directive is mounted or enabled** (**only if it not covered by another element**) | +| `.roving` or `.roving.vertical.horizontal` | allow roving navigation (`Home`, `End`, `ArrowKeys`) | +| `.roving.vertical` | allow roving navigation (`Home`, `End`, `ArrowUp`, `ArrowDown`) | +| `.roving.horizontal` | allow roving navigation (`Home`, `End`, `ArrowLeft`, `ArrowRight`) | +| `.roving.grid` | allow roving navigation (`Home`, `End`, `ArrowKeys`) using dataset attrs on elements `[data-${camelCase from datasetName}-(row/col)]`; `[data-${camelCase from datasetName}-(row/col)~="*"]` is a catchall | +| `.roving` used on an element with `[role="grid"]` | allow roving navigation (`Home`, `End`, `ArrowKeys`) using role attrs on elements `[role="row/gridcell"]` | +| `.roving.tabinside` | `Tab` key navigates to next/prev element inside trap (by default `Tab` key navigates to next/prev element outside trap in roving mode) | +| `.escrefocus` | refocus element that was in focus before activating the trap on `Esc` | +| `.escexits` | refocus a parent trap on `Esc` (has priority over `.escrefocus`) | +| `.indexorder` used without `.grid` modifier and on elements without `[role="grid"]` | force usage of order in `tabindex` (`tabindex` in ascending order and then DOM order) | + +## Keyboard navigation + +- `TAB` / `SHIFT`+`TAB` key + - moves to next / previous focusable element inside the trap group (moves from last one to first one or from first one to last one when no more focusable elements are available in the group) + - if `.roving` modifier is used moves to next / previous trap group or focusable element outside the current trap group + - if `.roving.tabinside` modifiers are used then move inside the trap group + - if `.indexorder` modifier is used without `.grid` and on elements without `[role="grid"]` - the order of tabindex will be used +- `ESC` key + - disables / enables the current tab group + - if `.escexits` modifier is used then refocus the last active focusable element in a parent trap group + - if `.escrefocus` modifier is used then refocus the last focusable element that was active before the current trap group got focus + - if `.escexits` or `.escrefocus` are used then press `SHIFT + ESC` to disable / enable the current tab group +- `HOME` / `END` when `.roving` modifier is used + - move to first / last focusable element in the current trap group +- `ARROW_KEYS` when `.roving` modifier is used (`.roving.horizontal.vertical` is the same as `.roving`) + - if only `.horizontal` modifier is used then only `ARROW_LEFT` / `ARROW_RIGHT` keys can be used + - if only `.vertical` modifier is used then only `ARROW_UP` / `ARROW_DOWN` keys can be used + - `ARROW_LEFT` / `ARROW_UP` move to the previous focusable element inside the trap group + - `ARROW_RIGHT` / `ARROW_DOWN` move to the next focusable element inside the trap group + - if `.indexorder` modifier is used without `.grid` and on elements without `[role="grid"]` - the order of tabindex will be used +- `ARROW_KEYS` when `.roving.grid` modifiers are used or `.roving` modifier on a trap element with [role="grid"] + - move in the grid inside the current trap group + +### Keyboard navigation inside `.roving.grid` trap groups + +In order to specify the navigation pattern you must use 2 dataset attributes on the focusable elements inside the `.roving` trap group: + +- `data-v-kbd-trap-row` specifies the numeric identifier of the row the element belongs to (numbers need not be consecutive, but their natural order determines the navigation order) +- `data-v-kbd-trap-col` specifies the numeric identifier of the column the element belongs to (numbers need not be consecutive, but their natural order determines the navigation order) + +Any or both attributes can have a value of `*` that means that it is an alement that can be focused from elements having any coresponding (row or col) attribute. + +#### Navigation rules + +- the first focusable element on the row / col (based on direction of movement) is focused +- an element with `*` for row or col is considered to belong to any row / col + +### Keyboard navigation inside `.roving` trap groups with `[role="grid"]` + +In order to specify the navigation pattern you must use role attributes `[role="row"]` and `[role="gridcell"]`. + +All focusable element must have `[role="gridcell"]` and must be inside `[role="row"]` elements inside `[role="grid"]` trap element. + +The `gridcell`s will be considered inline-start aligned in every row. + +#### Navigation rules + +- the first focusable element on the row / col (based on direction of movement) is focused + +### RTL / LTR + +The directive checks the closest parent DOM Element of the active element that has a `[dir="rtl"]` or `[dir="ltr`]` attribute. + +If the direction is RTL the `ARROW_LEFT` and `ARROW_RIGHT` keys move in reverse (according to document order of the focusable elements) but consistent to the way the elements are ordered on screen. + +## CSS (visual hints for users) + +The directive does not require any styles, but it might help the users to have visual hints for navigation. + +A default style is provided as SASS in `dist/styles/index.sass` (can be imported as `import '@pdapdan/vue-keyboard-trap/styles'`, as `import '@pdapdan/vue-keyboard-trap/dist/styles/index.sass'` (if the bundler does not use the `/styles` export) or included from [https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/styles/index.sass](https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/styles/index.sass)). + +The default style is also provided as CSS in `dist/styles/index.css` (can be imported as `import '@pdapdan/vue-keyboard-trap/dist/styles/index.css'` or included from [https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/styles/index.css](https://cdn.jsdelivr.net/gh/pdanpdan/vue-keyboard-trap/dist/styles/index.css)). + +There are some CSS variables that can be used to customize the aspect of the hints: + +| Variable | Role | Default | +|----------|------|:-------:| +| `--color-v-kbd-trap-enabled` | the text color when directive is enabled | `#c33` | +| `--color-v-kbd-trap-disabled` | the text color when directive is disabled | `#999` | +| `--color-v-kbd-trap-background` | the background color of the hint area | `#eeee` | +| `--text-v-kbd-trap-separator` | separator between elements | `/` | +| `--text-v-kbd-trap-enabled` | indicator for enabled but not active trap | `Trap` | +| `--text-v-kbd-trap-esc` | indicator for `Esc` key active | `Esc` | +| `--text-v-kbd-trap-esc-refocus` | indicator for `Esc` key active when it refocuses | `Esc\2949` / `Esc⥉` | +| `--text-v-kbd-trap-esc-exits` | indicator for `Esc` key active when it exits trap | `Esc\2923` / `Esc⤣` | +| `--text-v-kbd-trap-tab` | indicator for `Tab` key active inside trap | `Tab` | +| `--text-v-kbd-trap-tab-exits` | indicator for `Tab` key active when it exits trap | `Tab\21C5` / `Tab⇅` | +| `--text-v-kbd-trap-grid` | indicator for grid mode active | `\229E` / `⊞` | +| `--text-v-kbd-trap-arrows-all` | indicator for move keys active in roving mode | `\2962\2963\2965\2964` / `⥢⥣⥥⥤` | +| `--text-v-kbd-trap-arrows-horizontal` | indicator for move keys active in roving mode horizontal | `\2962\2964` / `⥢⥤` | +| `--text-v-kbd-trap-arrows-vertical` | indicator for move keys active in roving mode vertical | `\2963\2965` / `⥣⥥` | + +In the default style the hint is positioned on the top-right corner of the trap group. + +<<< @/../src/public/styles/index.sass + +## Development + +### Install the dependencies + +```bash +pnpm i +``` + +### Start development mode (hot-code reloading, error reporting, etc.) + +```bash +pnpm dev +``` + +### Lint the files + +```bash +pnpm lint +``` + +### Build for production + +```bash +pnpm build +``` + +## Source code, issues, bug reports, feature requests + +[Vue Keyboard Trap (vue-keyboard-trap)](https://github.com/pdanpdan/vue-keyboard-trap) + +## Author + +* Name: Dan Popescu (PDan) +* Email: [pdan.popescu@gmail.com](mailto:pdan.popescu@gmail.com) +* Website: https://github.com/pdanpdan/ +* Github: [@pdanpdan](https://github.com/pdanpdan) + +## License + +Copyright © 2022-present [Dan Popescu](https://github.com/pdanpdan). + +This application is distributed under [![License: MIT](https://img.shields.io/github/license/pdanpdan/vue-keyboard-trap?style=for-the-badge)](https://opensource.org/licenses/MIT), see LICENSE for more information. diff --git a/dist/index.es.js b/dist/index.es.js new file mode 100644 index 0000000..37b2833 --- /dev/null +++ b/dist/index.es.js @@ -0,0 +1,340 @@ +import { isVue3 as j, markRaw as V, computed as W, watch as H, getCurrentScope as B, onScopeDispose as G, unref as J } from "vue-demi"; +function O(t) { + const e = { + name: "kbd-trap", + focusableSelector: [":focus"].concat( + [ + "a[href]", + "area[href]", + "audio[controls]", + "video[controls]", + "iframe", + "[tabindex]:not(slot)", + '[contenteditable]:not([contenteditable="false"])', + "details > summary:first-of-type" + ].map((a) => `${a}:not([tabindex^="-"])`) + ).concat( + [ + 'input:not([type="hidden"]):not(fieldset[disabled] input)', + "select:not(fieldset[disabled] select)", + "textarea:not(fieldset[disabled] textarea)", + "button:not(fieldset[disabled] button)", + '[class*="focusable"]' + ].map((a) => `${a}:not([disabled]):not([tabindex^="-"])`) + ).concat( + [ + 'input:not([type="hidden"])', + "select", + "textarea", + "button" + ].map((a) => `fieldset[disabled]:not(fieldset[disabled] fieldset) > legend ${a}:not([disabled]):not([tabindex^="-"])`) + ).join(","), + rovingSkipSelector: [ + 'input:not([disabled]):not([type="button"]):not([type="checkbox"]):not([type="file"]):not([type="image"]):not([type="radio"]):not([type="reset"]):not([type="submit"])', + "select:not([disabled])", + "select:not([disabled]) *", + "textarea:not([disabled])", + '[contenteditable]:not([contenteditable="false"])', + '[contenteditable]:not([contenteditable="false"]) *' + ].join(","), + gridSkipSelector: [ + ":not([disabled])", + ':not([tabindex^="-"])' + ].join(""), + autofocusSelector: [ + '[autofocus]:not([autofocus="false"])', + '[data-autofocus]:not([data-autofocus="false"])' + ].map((a) => `${a}:not([disabled])`).join(","), + trapTabIndex: -9999, + ...t + }, n = e.name.toLocaleLowerCase().split(/[^a-z0-9]+/).filter((a) => a.length > 0).map((a) => `${a[0].toLocaleUpperCase()}${a.slice(1)}`).join(""); + if (e.datasetName === void 0 && (e.datasetName = `v${n}`), e.datasetNameActive = `${e.datasetName}Active`, e.datasetNamePreventRefocus = `${e.datasetName}PreventRefocus`, typeof window > "u") + return e; + const i = document.createElement("span"); + i.dataset[e.datasetName] = ""; + const r = i.getAttributeNames()[0]; + return e.datasetNameSelector = `[${r}]`, e.datasetNameSelectorRovingHorizontal = `[${r}~="roving"][${r}~="horizontal"],[${r}~="roving"]:not([${r}~="vertical"])`, e.datasetNameSelectorRovingVertical = `[${r}~="roving"][${r}~="vertical"],[${r}~="roving"]:not([${r}~="horizontal"])`, e.datasetNameRow = `${e.datasetName}Row`, e.datasetNameRowSelector = (a) => `:focus,[${r}-row~="${a}"]${e.gridSkipSelector},[${r}-row~="*"]${e.gridSkipSelector}`, e.datasetNameCol = `${e.datasetName}Col`, e.datasetNameColSelector = (a) => `:focus,[${r}-col~="${a}"]${e.gridSkipSelector},[${r}-col~="*"]${e.gridSkipSelector}`, e; +} +function Q() { + return !0; +} +function P(t, e = !1) { + if (t.closest("dialog") != null) + return !0; + const { + left: n, + right: i, + top: r, + bottom: a + } = t.getBoundingClientRect(); + if (n === i && r === a) + return !0; + const o = [ + [n, r], + [n, (r + a) / 2], + [n, a], + [(n + i) / 2, r], + [(n + i) / 2, (r + a) / 2], + [(n + i) / 2, a], + [i, r], + [i, (r + a) / 2], + [i, a] + ]; + let s = !1; + for (let p = 0; p < 9; p += 1) { + const b = document.elementFromPoint(...o[p]); + if (t.contains(b) === !0) + return !0; + b != null && (s = !0); + } + if (e === !0 || typeof t.scrollIntoView != "function") + return !s; + const u = []; + let l = t.parentElement; + for (; l != null; ) + u.push([l, l.scrollLeft, l.scrollTop]), l = l.parentElement; + t.scrollIntoView(); + const T = P(t, !0); + for (let p = u.length - 1; p >= 0; p -= 1) { + const [b, m, g] = u[p]; + b.scrollLeft = m, b.scrollTop = g; + } + return T; +} +let L; +function w(t, e = Q) { + return t == null || typeof t.focus != "function" || e(t) !== !0 ? !1 : (L = t, t.focus(), [L, t].includes(document.activeElement) || document.activeElement != null && [L, t].includes(document.activeElement.__focusTargetPlaceholder)); +} +const X = /(\d+)/; +function R(t) { + const e = X.exec(t); + return e == null ? "" : e[1]; +} +function D(t, e) { + const n = (t && t !== e && t.parentElement || e).closest('[dir="rtl"],[dir="ltr"]'); + return n && n.matches('[dir="rtl"]'); +} +let v = null; +function S(t, e) { + v !== t && (t != null && (t.dataset[e.datasetNameActive] = "", t.__vKbdTrapActiveClean = () => { + delete t.dataset[e.datasetNameActive], t.__vKbdTrapActiveClean = void 0; + }), v != null && typeof v.__vKbdTrapActiveClean == "function" && v.__vKbdTrapActiveClean(), v = t); +} +function F(t) { + const e = (t || {}).__vKbdTrap; + return e === Object(e) ? e : null; +} +function q(t, e, n, i) { + e === !0 ? (delete t.dataset[i.datasetName], t.tabIndex === i.trapTabIndex && t.removeAttribute("tabindex")) : (t.dataset[i.datasetName] = Object.keys(n.modifiers).filter((r) => n.modifiers[r] === !0).join(" "), t.tabIndex < 0 && t.getAttribute("tabindex") == null && t.matches("dialog") === !1 && t.matches("[popover]") === !1 && (t.tabIndex = i.trapTabIndex)); +} +function Y(t, e, n, i) { + const r = { + disable: n === !1, + modifiers: i, + focusTarget: null, + relatedFocusTarget: null, + bind() { + e.__vKbdTrap = r, e.addEventListener("keydown", r.trap), e.addEventListener("focusin", r.activate), e.addEventListener("focusout", r.deactivate), e.addEventListener("pointerdown", r.overwriteFocusTarget, { passive: !0 }), r.disable === !1 && q(e, r.disable, r, t); + }, + unbind() { + delete e.__vKbdTrap, e.removeEventListener("keydown", r.trap), e.removeEventListener("focusin", r.activate), e.removeEventListener("focusout", r.deactivate), e.removeEventListener("pointerdown", r.overwriteFocusTarget), q(e, !0, r, t); + }, + activate(a) { + if (r.disable === !0 || a.__vKbdTrap === !0) + return; + a.__vKbdTrap = !0; + const o = a.relatedTarget; + o != null && o !== document.body && o.closest(t.datasetNameSelector) !== e && o.tabIndex !== t.trapTabIndex && (r.relatedFocusTarget = o), v !== e && (o == null || o.closest(t.datasetNameSelector) !== e) && (S(e, t), (o == null || o.dataset[t.datasetNamePreventRefocus] === void 0 || e.contains(o) === !1) && r.refocus(r.modifiers.roving !== !0)); + }, + deactivate(a) { + if (r.disable === !0 || a.__vKbdTrap === !0) + return; + a.__vKbdTrap = !0; + const o = a.relatedTarget; + v === e && (o == null || o.closest(t.datasetNameSelector) !== e) && (r.focusTarget = a.target, o == null && r.relatedFocusTarget && w(r.relatedFocusTarget), S(null, t)); + }, + trap(a) { + if (r.disable === !0 || a.__vKbdTrap === !0) + return; + const { code: o, shiftKey: s } = a, { activeElement: u } = document; + if (o === "Escape") { + if (a.__vKbdTrap = !0, v === e) { + if (r.focusTarget = u, s === !0) + a.preventDefault(); + else { + if (r.modifiers.escexits === !0) { + S(e.parentElement == null ? null : e.parentElement.closest(t.datasetNameSelector), t); + const c = F(v); + c?.refocus(); + return; + } + if (r.modifiers.escrefocus === !0 && w(r.relatedFocusTarget) === !0) + return; + } + const d = e.parentElement && e.parentElement.closest(t.datasetNameSelector); + S(d || null, t); + } else + S(e, t); + return; + } + if (v !== e) + return; + a.__vKbdTrap = !0; + let l = 0, T = (d) => d, p = !1, b = !1; + if (r.modifiers.roving === !0) { + const d = u.matches(t.rovingSkipSelector); + if (o !== "Tab" && d === !0) + return; + if (o === "Tab") + d === !1 && r.modifiers.tabinside !== !0 ? (p = e.parentElement.closest(t.datasetNameSelector), p != null && (a.__vKbdTrap = void 0), s === !0 ? (l = 1, T = (c, f) => f) : (l = -1, T = () => 0)) : l = s === !0 ? -1 : 1; + else if (o === "Home") + l = 1, T = (c, f) => f; + else if (o === "End") + l = -1, T = () => 0; + else if (e.parentElement != null && (r.modifiers.vertical === !0 && r.modifiers.horizontal !== !0 && (o === "ArrowLeft" || o === "ArrowRight") || r.modifiers.horizontal === !0 && r.modifiers.vertical !== !0 && (o === "ArrowUp" || o === "ArrowDown"))) { + const c = e.parentElement.closest( + r.modifiers.vertical === !0 ? t.datasetNameSelectorRovingHorizontal : t.datasetNameSelectorRovingVertical + ); + c != null && (p = c, a.__vKbdTrap = void 0, o === (D(u, e) === !0 ? "ArrowRight" : "ArrowLeft") || o === "ArrowUp" ? (l = 1, T = (f, h) => h) : (l = -1, T = () => 0)); + } else + (r.modifiers.vertical === !0 || r.modifiers.horizontal !== !0) && (o === "ArrowUp" ? (l = -1, b = "v") : o === "ArrowDown" && (l = 1, b = "v")), (r.modifiers.vertical !== !0 || r.modifiers.horizontal === !0) && (o === "ArrowLeft" ? (l = -1, b = "h") : o === "ArrowRight" && (l = 1, b = "h"), l !== 0 && b === "h" && D(u, e) === !0 && (l *= -1)); + } else o === "Tab" && (l = s === !0 ? -1 : 1); + if (l === 0) + return; + p === !1 ? a.preventDefault() : (r.focusTarget = u, r.focusTarget.dataset[t.datasetNamePreventRefocus] = "", requestAnimationFrame(() => { + r.focusTarget && delete r.focusTarget.dataset[t.datasetNamePreventRefocus]; + })); + let m = []; + if (b !== !1) { + let d; + if (r.modifiers.grid === !0) { + const c = R(u.dataset[t.datasetNameRow]), f = R(u.dataset[t.datasetNameCol]), h = b === "v" ? t.datasetNameColSelector(f) : t.datasetNameRowSelector(c); + m = Array.from(e.querySelectorAll(h)), d = new WeakMap( + m.map((_) => { + const N = R(_.dataset[t.datasetNameRow]), y = R(_.dataset[t.datasetNameCol]); + let A; + return b === "v" ? (N !== c || y === f) && (A = 1e3 * N + 1 * y) : (y !== f || N === c) && (A = 1e3 * y + 1 * N), [_, A]; + }) + ); + } else if (e.matches('[role="grid"]') === !0 && u.matches('[role="row"] [role="gridcell"]')) { + const c = Array.from(e.querySelectorAll('[role="row"]')), f = /* @__PURE__ */ new WeakMap(), h = c.map((C, K) => { + const $ = Array.from(C.querySelectorAll('[role="gridcell"]')); + return $.forEach((E, U) => { + f.set(E, [K + 1, U + 1]); + }), $; + }), _ = u.closest('[role="row"]'), N = c.indexOf(_) + 1, y = h[N - 1].indexOf(u) + 1, { focusableSelector: A } = t; + m = Array.from(e.querySelectorAll(A)), d = new WeakMap( + m.map((C) => { + const [K, $] = f.get(C) || [null, null]; + let E; + return b === "v" ? $ === y && (E = 1 * K) : K === N && (E = 1 * $), [C, E]; + }) + ); + } + d != null && p == null && (m = m.filter((c) => d.get(c) !== void 0), m.sort((c, f) => d.get(c) - d.get(f))); + } + if (m.length === 0) { + const { focusableSelector: d } = t; + if (m = Array.from(e.querySelectorAll(d)), i.indexorder === !0 && p == null) { + const c = new WeakMap( + m.map((f) => [f, Math.max(f.tabIndex || 0, 0)]) + ); + m.sort((f, h) => c.get(f) - c.get(h)); + } + e.matches(d) && m.unshift(e); + } + const g = m.length - 1; + let x = T(m.indexOf(u), g); + for (let d = 0; d < g; d += 1) + if (x += l, x < 0 ? x = g : x > g && (x = 0), w(m[x]) === !0) { + p !== !1 && S(p, t); + return; + } + }, + overwriteFocusTarget(a) { + r.disable === !1 && a.__vKbdTrap !== !0 && (a.__vKbdTrap = !0, r.focusTarget = a.target); + }, + refocus(a) { + if (r.disable === !1 && v === e && r.focusTarget) { + let o = r.focusTarget.closest(t.datasetNameSelector); + for (; o && o !== e; ) { + const s = F(o); + if (s !== null && s.disable === !1 && s.focusTarget) + return S(o, t), s.refocus(a !== void 0 ? s.modifiers.roving !== !0 : void 0); + o = o.parentElement && o.parentElement.closest(t.datasetNameSelector); + } + return r.focusTarget.tabIndex === t.trapTabIndex || r.focusTarget.matches("dialog") === !0 || r.focusTarget.matches("[popover]") === !0 ? r.modifiers.autofocus === !0 && w(e.querySelector(t.autofocusSelector)) === !0 || w(e.querySelector(t.focusableSelector)) === !0 || w(r.focusTarget) === !0 : a === !0 ? !1 : w(r.focusTarget) === !0 || w(e.querySelector(t.focusableSelector)) === !0; + } + return !1; + }, + autofocus() { + S(e, t), r.disable === !1 && w(e.querySelector(t.autofocusSelector), P) === !1 && w(e.querySelector(t.focusableSelector), P); + } + }; + return r; +} +function z(t, e, n, i) { + const r = Y(t, e, n, i); + r.bind(), i.autofocus === !0 && r.autofocus(); +} +function M(t, e, n, i, r) { + const a = i === !1; + e.modifiers = r, q(n, a, e, t), v === n && (a === !0 ? S(null, t) : n.dataset[t.datasetNameActive] = ""), e.disable !== a && (e.disable = a, r.autofocus === !0 ? e.autofocus() : a === !1 && v !== n && n.contains(document.activeElement) === !0 && S(n, t)); +} +function k(t, e) { + const n = F(e); + n !== null && n.unbind(), v === e && (n.relatedFocusTarget && w(n.relatedFocusTarget), S(null, t)); +} +function Z(t) { + const e = O(t), n = (a, { value: o, modifiers: s }) => z(e, a, o, s), i = (a, { value: o, modifiers: s }) => { + const u = F(a); + u !== null ? M(e, u, a, o, s) : j ? n(a, { value: o, modifiers: s }) : v === a && S(null, e); + }, r = (a) => k(e, a); + return j ? V({ + name: e.name, + directive: { + mounted: n, + updated: i, + unmounted: r, + getSSRProps() { + } + } + }) : { + name: e.name, + directive: { + bind: n, + update: i, + unbind: r + } + }; +} +function I(t) { + return typeof t == "function" ? t() : J(t); +} +function te(t) { + const e = O(t); + return (n, i = {}, r = !0) => { + const a = W(() => { + const s = I(n); + return s == null ? null : V("$el" in s ? s.$el : s); + }), o = H(() => [a.value, I(r), I(i)], ([s, u, l], [T] = []) => { + s == null && T == null || (T == null && s != null ? z(e, s, u, l) : s == null ? k(e, s) : s !== T ? (k(e, T), z(e, s, u, l)) : M(e, F(s), s, u, l)); + }, { flush: "sync", deep: !0, immediate: !0 }); + B() && G(() => { + o(), a.value != null && k(e, a.value); + }); + }; +} +const re = { + install(t, e) { + const { name: n, directive: i } = Z(e); + t.directive(n, i); + } +}; +export { + Z as VueKeyboardTrapDirectiveFactory, + re as VueKeyboardTrapDirectivePlugin, + re as default, + te as useKeyboardTrapFactory +}; +//# sourceMappingURL=index.es.js.map diff --git a/dist/index.es.js.map b/dist/index.es.js.map new file mode 100644 index 0000000..4599a0b --- /dev/null +++ b/dist/index.es.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.es.js","sources":["../src/directives/keyboard-trap/options.js","../src/directives/keyboard-trap/helpers.js","../src/directives/keyboard-trap/directive.js","../src/directives/keyboard-trap/index.js"],"sourcesContent":null,"names":["createConfig","options","config","s","pascalName","t","dsEl","datasetNameSnake","i","defaultFocusCheckFn","visibleFocusCheckFn","el","scrolled","left","right","top","bottom","posList","elAtPosFound","elAtPos","scrollPos","parent","visible","scrollEl","scrollLeft","scrollTop","focusTargetEl","focus","checkFn","reNumber","extractNumber","val","match","dirIsRtl","activeElement","currentTrapEl","dirEl","activeTrapEl","setActiveTrapEl","newEl","getCtx","ctx","setAttributes","disable","key","createCtx","value","modifiers","ev","oldFocusedElement","newFocusedElement","code","shiftKey","newCtx","trapEl","step","indexSelector","rovingExit","rovingDirection","rovingSkipSelector","_","iMax","parentTrap","focusableList","focusableMap","row","col","focusableSelector","o","r","c","rows","elToRowCol","rowsCells","rIndex","cols","cIndex","curRow","el1","el2","tabindexOrder","focusableIndexLast","focusableIndex","onlyIfTrapEl","bindFn","updateFn","unbindFn","directiveFactory","mounted","updated","isVue3","unmounted","markRaw","toValue","maybeRef","unref","composableFactory","maybeElementOrComponentRefOrComputed","modifiersRefOrComputed","activeRefOrComputed","elComputed","computed","elOrComponent","unwatch","watch","oldEl","getCurrentScope","onScopeDispose","VueKeyboardTrapDirectivePlugin","app","name","directive"],"mappings":"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"} \ No newline at end of file diff --git a/dist/index.umd.js b/dist/index.umd.js new file mode 100644 index 0000000..58da7d9 --- /dev/null +++ b/dist/index.umd.js @@ -0,0 +1,2 @@ +(function(y,S){typeof exports=="object"&&typeof module<"u"?S(exports,require("vue-demi")):typeof define=="function"&&define.amd?define(["exports","vue-demi"],S):(y=typeof globalThis<"u"?globalThis:y||self,S(y.VueKeyboardTrap={},y.VueDemi))})(this,function(y,S){"use strict";function M(t){const e={name:"kbd-trap",focusableSelector:[":focus"].concat(["a[href]","area[href]","audio[controls]","video[controls]","iframe","[tabindex]:not(slot)",'[contenteditable]:not([contenteditable="false"])',"details > summary:first-of-type"].map(a=>`${a}:not([tabindex^="-"])`)).concat(['input:not([type="hidden"]):not(fieldset[disabled] input)',"select:not(fieldset[disabled] select)","textarea:not(fieldset[disabled] textarea)","button:not(fieldset[disabled] button)",'[class*="focusable"]'].map(a=>`${a}:not([disabled]):not([tabindex^="-"])`)).concat(['input:not([type="hidden"])',"select","textarea","button"].map(a=>`fieldset[disabled]:not(fieldset[disabled] fieldset) > legend ${a}:not([disabled]):not([tabindex^="-"])`)).join(","),rovingSkipSelector:['input:not([disabled]):not([type="button"]):not([type="checkbox"]):not([type="file"]):not([type="image"]):not([type="radio"]):not([type="reset"]):not([type="submit"])',"select:not([disabled])","select:not([disabled]) *","textarea:not([disabled])",'[contenteditable]:not([contenteditable="false"])','[contenteditable]:not([contenteditable="false"]) *'].join(","),gridSkipSelector:[":not([disabled])",':not([tabindex^="-"])'].join(""),autofocusSelector:['[autofocus]:not([autofocus="false"])','[data-autofocus]:not([data-autofocus="false"])'].map(a=>`${a}:not([disabled])`).join(","),trapTabIndex:-9999,...t},n=e.name.toLocaleLowerCase().split(/[^a-z0-9]+/).filter(a=>a.length>0).map(a=>`${a[0].toLocaleUpperCase()}${a.slice(1)}`).join("");if(e.datasetName===void 0&&(e.datasetName=`v${n}`),e.datasetNameActive=`${e.datasetName}Active`,e.datasetNamePreventRefocus=`${e.datasetName}PreventRefocus`,typeof window>"u")return e;const i=document.createElement("span");i.dataset[e.datasetName]="";const r=i.getAttributeNames()[0];return e.datasetNameSelector=`[${r}]`,e.datasetNameSelectorRovingHorizontal=`[${r}~="roving"][${r}~="horizontal"],[${r}~="roving"]:not([${r}~="vertical"])`,e.datasetNameSelectorRovingVertical=`[${r}~="roving"][${r}~="vertical"],[${r}~="roving"]:not([${r}~="horizontal"])`,e.datasetNameRow=`${e.datasetName}Row`,e.datasetNameRowSelector=a=>`:focus,[${r}-row~="${a}"]${e.gridSkipSelector},[${r}-row~="*"]${e.gridSkipSelector}`,e.datasetNameCol=`${e.datasetName}Col`,e.datasetNameColSelector=a=>`:focus,[${r}-col~="${a}"]${e.gridSkipSelector},[${r}-col~="*"]${e.gridSkipSelector}`,e}function D(){return!0}function P(t,e=!1){if(t.closest("dialog")!=null)return!0;const{left:n,right:i,top:r,bottom:a}=t.getBoundingClientRect();if(n===i&&r===a)return!0;const o=[[n,r],[n,(r+a)/2],[n,a],[(n+i)/2,r],[(n+i)/2,(r+a)/2],[(n+i)/2,a],[i,r],[i,(r+a)/2],[i,a]];let s=!1;for(let p=0;p<9;p+=1){const v=document.elementFromPoint(...o[p]);if(t.contains(v)===!0)return!0;v!=null&&(s=!0)}if(e===!0||typeof t.scrollIntoView!="function")return!s;const u=[];let l=t.parentElement;for(;l!=null;)u.push([l,l.scrollLeft,l.scrollTop]),l=l.parentElement;t.scrollIntoView();const T=P(t,!0);for(let p=u.length-1;p>=0;p-=1){const[v,m,_]=u[p];v.scrollLeft=m,v.scrollTop=_}return T}let q;function h(t,e=D){return t==null||typeof t.focus!="function"||e(t)!==!0?!1:(q=t,t.focus(),[q,t].includes(document.activeElement)||document.activeElement!=null&&[q,t].includes(document.activeElement.__focusTargetPlaceholder))}const B=/(\d+)/;function R(t){const e=B.exec(t);return e==null?"":e[1]}function O(t,e){const n=(t&&t!==e&&t.parentElement||e).closest('[dir="rtl"],[dir="ltr"]');return n&&n.matches('[dir="rtl"]')}let b=null;function w(t,e){b!==t&&(t!=null&&(t.dataset[e.datasetNameActive]="",t.__vKbdTrapActiveClean=()=>{delete t.dataset[e.datasetNameActive],t.__vKbdTrapActiveClean=void 0}),b!=null&&typeof b.__vKbdTrapActiveClean=="function"&&b.__vKbdTrapActiveClean(),b=t)}function $(t){const e=(t||{}).__vKbdTrap;return e===Object(e)?e:null}function V(t,e,n,i){e===!0?(delete t.dataset[i.datasetName],t.tabIndex===i.trapTabIndex&&t.removeAttribute("tabindex")):(t.dataset[i.datasetName]=Object.keys(n.modifiers).filter(r=>n.modifiers[r]===!0).join(" "),t.tabIndex<0&&t.getAttribute("tabindex")==null&&t.matches("dialog")===!1&&t.matches("[popover]")===!1&&(t.tabIndex=i.trapTabIndex))}function G(t,e,n,i){const r={disable:n===!1,modifiers:i,focusTarget:null,relatedFocusTarget:null,bind(){e.__vKbdTrap=r,e.addEventListener("keydown",r.trap),e.addEventListener("focusin",r.activate),e.addEventListener("focusout",r.deactivate),e.addEventListener("pointerdown",r.overwriteFocusTarget,{passive:!0}),r.disable===!1&&V(e,r.disable,r,t)},unbind(){delete e.__vKbdTrap,e.removeEventListener("keydown",r.trap),e.removeEventListener("focusin",r.activate),e.removeEventListener("focusout",r.deactivate),e.removeEventListener("pointerdown",r.overwriteFocusTarget),V(e,!0,r,t)},activate(a){if(r.disable===!0||a.__vKbdTrap===!0)return;a.__vKbdTrap=!0;const o=a.relatedTarget;o!=null&&o!==document.body&&o.closest(t.datasetNameSelector)!==e&&o.tabIndex!==t.trapTabIndex&&(r.relatedFocusTarget=o),b!==e&&(o==null||o.closest(t.datasetNameSelector)!==e)&&(w(e,t),(o==null||o.dataset[t.datasetNamePreventRefocus]===void 0||e.contains(o)===!1)&&r.refocus(r.modifiers.roving!==!0))},deactivate(a){if(r.disable===!0||a.__vKbdTrap===!0)return;a.__vKbdTrap=!0;const o=a.relatedTarget;b===e&&(o==null||o.closest(t.datasetNameSelector)!==e)&&(r.focusTarget=a.target,o==null&&r.relatedFocusTarget&&h(r.relatedFocusTarget),w(null,t))},trap(a){if(r.disable===!0||a.__vKbdTrap===!0)return;const{code:o,shiftKey:s}=a,{activeElement:u}=document;if(o==="Escape"){if(a.__vKbdTrap=!0,b===e){if(r.focusTarget=u,s===!0)a.preventDefault();else{if(r.modifiers.escexits===!0){w(e.parentElement==null?null:e.parentElement.closest(t.datasetNameSelector),t);const d=$(b);d?.refocus();return}if(r.modifiers.escrefocus===!0&&h(r.relatedFocusTarget)===!0)return}const c=e.parentElement&&e.parentElement.closest(t.datasetNameSelector);w(c||null,t)}else w(e,t);return}if(b!==e)return;a.__vKbdTrap=!0;let l=0,T=c=>c,p=!1,v=!1;if(r.modifiers.roving===!0){const c=u.matches(t.rovingSkipSelector);if(o!=="Tab"&&c===!0)return;if(o==="Tab")c===!1&&r.modifiers.tabinside!==!0?(p=e.parentElement.closest(t.datasetNameSelector),p!=null&&(a.__vKbdTrap=void 0),s===!0?(l=1,T=(d,f)=>f):(l=-1,T=()=>0)):l=s===!0?-1:1;else if(o==="Home")l=1,T=(d,f)=>f;else if(o==="End")l=-1,T=()=>0;else if(e.parentElement!=null&&(r.modifiers.vertical===!0&&r.modifiers.horizontal!==!0&&(o==="ArrowLeft"||o==="ArrowRight")||r.modifiers.horizontal===!0&&r.modifiers.vertical!==!0&&(o==="ArrowUp"||o==="ArrowDown"))){const d=e.parentElement.closest(r.modifiers.vertical===!0?t.datasetNameSelectorRovingHorizontal:t.datasetNameSelectorRovingVertical);d!=null&&(p=d,a.__vKbdTrap=void 0,o===(O(u,e)===!0?"ArrowRight":"ArrowLeft")||o==="ArrowUp"?(l=1,T=(f,g)=>g):(l=-1,T=()=>0))}else(r.modifiers.vertical===!0||r.modifiers.horizontal!==!0)&&(o==="ArrowUp"?(l=-1,v="v"):o==="ArrowDown"&&(l=1,v="v")),(r.modifiers.vertical!==!0||r.modifiers.horizontal===!0)&&(o==="ArrowLeft"?(l=-1,v="h"):o==="ArrowRight"&&(l=1,v="h"),l!==0&&v==="h"&&O(u,e)===!0&&(l*=-1))}else o==="Tab"&&(l=s===!0?-1:1);if(l===0)return;p===!1?a.preventDefault():(r.focusTarget=u,r.focusTarget.dataset[t.datasetNamePreventRefocus]="",requestAnimationFrame(()=>{r.focusTarget&&delete r.focusTarget.dataset[t.datasetNamePreventRefocus]}));let m=[];if(v!==!1){let c;if(r.modifiers.grid===!0){const d=R(u.dataset[t.datasetNameRow]),f=R(u.dataset[t.datasetNameCol]),g=v==="v"?t.datasetNameColSelector(f):t.datasetNameRowSelector(d);m=Array.from(e.querySelectorAll(g)),c=new WeakMap(m.map(E=>{const N=R(E.dataset[t.datasetNameRow]),A=R(E.dataset[t.datasetNameCol]);let F;return v==="v"?(N!==d||A===f)&&(F=1e3*N+1*A):(A!==f||N===d)&&(F=1e3*A+1*N),[E,F]}))}else if(e.matches('[role="grid"]')===!0&&u.matches('[role="row"] [role="gridcell"]')){const d=Array.from(e.querySelectorAll('[role="row"]')),f=new WeakMap,g=d.map((L,I)=>{const C=Array.from(L.querySelectorAll('[role="gridcell"]'));return C.forEach((K,Q)=>{f.set(K,[I+1,Q+1])}),C}),E=u.closest('[role="row"]'),N=d.indexOf(E)+1,A=g[N-1].indexOf(u)+1,{focusableSelector:F}=t;m=Array.from(e.querySelectorAll(F)),c=new WeakMap(m.map(L=>{const[I,C]=f.get(L)||[null,null];let K;return v==="v"?C===A&&(K=1*I):I===N&&(K=1*C),[L,K]}))}c!=null&&p==null&&(m=m.filter(d=>c.get(d)!==void 0),m.sort((d,f)=>c.get(d)-c.get(f)))}if(m.length===0){const{focusableSelector:c}=t;if(m=Array.from(e.querySelectorAll(c)),i.indexorder===!0&&p==null){const d=new WeakMap(m.map(f=>[f,Math.max(f.tabIndex||0,0)]));m.sort((f,g)=>d.get(f)-d.get(g))}e.matches(c)&&m.unshift(e)}const _=m.length-1;let x=T(m.indexOf(u),_);for(let c=0;c<_;c+=1)if(x+=l,x<0?x=_:x>_&&(x=0),h(m[x])===!0){p!==!1&&w(p,t);return}},overwriteFocusTarget(a){r.disable===!1&&a.__vKbdTrap!==!0&&(a.__vKbdTrap=!0,r.focusTarget=a.target)},refocus(a){if(r.disable===!1&&b===e&&r.focusTarget){let o=r.focusTarget.closest(t.datasetNameSelector);for(;o&&o!==e;){const s=$(o);if(s!==null&&s.disable===!1&&s.focusTarget)return w(o,t),s.refocus(a!==void 0?s.modifiers.roving!==!0:void 0);o=o.parentElement&&o.parentElement.closest(t.datasetNameSelector)}return r.focusTarget.tabIndex===t.trapTabIndex||r.focusTarget.matches("dialog")===!0||r.focusTarget.matches("[popover]")===!0?r.modifiers.autofocus===!0&&h(e.querySelector(t.autofocusSelector))===!0||h(e.querySelector(t.focusableSelector))===!0||h(r.focusTarget)===!0:a===!0?!1:h(r.focusTarget)===!0||h(e.querySelector(t.focusableSelector))===!0}return!1},autofocus(){w(e,t),r.disable===!1&&h(e.querySelector(t.autofocusSelector),P)===!1&&h(e.querySelector(t.focusableSelector),P)}};return r}function j(t,e,n,i){const r=G(t,e,n,i);r.bind(),i.autofocus===!0&&r.autofocus()}function U(t,e,n,i,r){const a=i===!1;e.modifiers=r,V(n,a,e,t),b===n&&(a===!0?w(null,t):n.dataset[t.datasetNameActive]=""),e.disable!==a&&(e.disable=a,r.autofocus===!0?e.autofocus():a===!1&&b!==n&&n.contains(document.activeElement)===!0&&w(n,t))}function k(t,e){const n=$(e);n!==null&&n.unbind(),b===e&&(n.relatedFocusTarget&&h(n.relatedFocusTarget),w(null,t))}function W(t){const e=M(t),n=(a,{value:o,modifiers:s})=>j(e,a,o,s),i=(a,{value:o,modifiers:s})=>{const u=$(a);u!==null?U(e,u,a,o,s):S.isVue3?n(a,{value:o,modifiers:s}):b===a&&w(null,e)},r=a=>k(e,a);return S.isVue3?S.markRaw({name:e.name,directive:{mounted:n,updated:i,unmounted:r,getSSRProps(){}}}):{name:e.name,directive:{bind:n,update:i,unbind:r}}}function z(t){return typeof t=="function"?t():S.unref(t)}function J(t){const e=M(t);return(n,i={},r=!0)=>{const a=S.computed(()=>{const s=z(n);return s==null?null:S.markRaw("$el"in s?s.$el:s)}),o=S.watch(()=>[a.value,z(r),z(i)],([s,u,l],[T]=[])=>{s==null&&T==null||(T==null&&s!=null?j(e,s,u,l):s==null?k(e,s):s!==T?(k(e,T),j(e,s,u,l)):U(e,$(s),s,u,l))},{flush:"sync",deep:!0,immediate:!0});S.getCurrentScope()&&S.onScopeDispose(()=>{o(),a.value!=null&&k(e,a.value)})}}const H={install(t,e){const{name:n,directive:i}=W(e);t.directive(n,i)}};y.VueKeyboardTrapDirectiveFactory=W,y.VueKeyboardTrapDirectivePlugin=H,y.default=H,y.useKeyboardTrapFactory=J,Object.defineProperties(y,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); +//# sourceMappingURL=index.umd.js.map diff --git a/dist/index.umd.js.map b/dist/index.umd.js.map new file mode 100644 index 0000000..7cb14b8 --- /dev/null +++ b/dist/index.umd.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.umd.js","sources":["../src/directives/keyboard-trap/options.js","../src/directives/keyboard-trap/helpers.js","../src/directives/keyboard-trap/directive.js","../src/directives/keyboard-trap/index.js"],"sourcesContent":null,"names":["createConfig","options","config","s","pascalName","t","dsEl","datasetNameSnake","i","defaultFocusCheckFn","visibleFocusCheckFn","el","scrolled","left","right","top","bottom","posList","elAtPosFound","elAtPos","scrollPos","parent","visible","scrollEl","scrollLeft","scrollTop","focusTargetEl","focus","checkFn","reNumber","extractNumber","val","match","dirIsRtl","activeElement","currentTrapEl","dirEl","activeTrapEl","setActiveTrapEl","newEl","getCtx","ctx","setAttributes","disable","key","createCtx","value","modifiers","ev","oldFocusedElement","newFocusedElement","code","shiftKey","newCtx","trapEl","step","indexSelector","rovingExit","rovingDirection","rovingSkipSelector","_","iMax","parentTrap","focusableList","focusableMap","row","col","focusableSelector","o","r","c","rows","elToRowCol","rowsCells","rIndex","cols","cIndex","curRow","el1","el2","tabindexOrder","focusableIndexLast","focusableIndex","onlyIfTrapEl","bindFn","updateFn","unbindFn","directiveFactory","mounted","updated","isVue3","unmounted","markRaw","toValue","maybeRef","unref","composableFactory","maybeElementOrComponentRefOrComputed","modifiersRefOrComputed","activeRefOrComputed","elComputed","computed","elOrComponent","unwatch","watch","oldEl","getCurrentScope","onScopeDispose","VueKeyboardTrapDirectivePlugin","app","name","directive"],"mappings":"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"} \ No newline at end of file diff --git a/dist/styles/index.css b/dist/styles/index.css new file mode 100644 index 0000000..470f73d --- /dev/null +++ b/dist/styles/index.css @@ -0,0 +1,96 @@ +@charset "UTF-8"; + +[data-v-kbd-trap] { + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "Trap"); + --v-kbd-trap-esc: ""; + --v-kbd-trap-tab: ""; + --v-kbd-trap-roving: ""; +} + +[data-v-kbd-trap]:where(:has(:focus-visible)) { + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "Trap") var(--text-v-kbd-trap-separator, "/"); + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "Esc"); +} + +[data-v-kbd-trap]:where(:has(:focus-visible)):after { + content: var(--v-kbd-trap, "") var(--v-kbd-trap-esc, "") var(--v-kbd-trap-tab, "") var(--v-kbd-trap-roving, ""); + pointer-events: none; + position: absolute; + top: 2px; + right: 2px; + font: italic small-caps bold 14px monospace; + line-height: 1em; + padding: 4px; + background-color: var(--color-v-kbd-trap-background, rgba(238, 238, 238, 0.9333333333)); + border-radius: 2px; + z-index: 1; +} + +[data-v-kbd-trap]:where([tabindex="-9999"], dialog, [popover]) { + outline: none; +} + +[data-v-kbd-trap]:after { + color: var(--color-v-kbd-trap-disabled, #999); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):after { + color: var(--color-v-kbd-trap-enabled, #c33); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]) { + --v-kbd-trap: ""; + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "Esc"); + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab, "Tab"); + --v-kbd-trap-roving: ""; +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]) { + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab-exits, "Tab⇅"); + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-all, "⥢⥣⥥⥤"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=tabinside]) { + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab, "Tab"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=vertical]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-vertical, "⥣⥥"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=horizontal]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-horizontal, "⥢⥤"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=grid], [role=grid]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-grid, "⊞"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):has(input:not([disabled]):not([type=button]):not([type=checkbox]):not([type=file]):not([type=image]):not([type=radio]):not([type=reset]):not([type=submit]):focus-visible, select:not([disabled]):focus-visible, select:not([disabled]) *:focus-visible, textarea:not([disabled]):focus-visible, [contenteditable]:not([contenteditable=false]):focus-visible, [contenteditable]:not([contenteditable=false]) *:focus-visible) { + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab, "Tab"); + --v-kbd-trap-roving: ""; +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=escrefocus]) { + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-refocus, "Esc⥉"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=escexits]) { + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-exits, "Esc⤣"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-horizontal, "⥢⥤"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):after { + color: var(--color-v-kbd-trap-enabled, #c33); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-vertical, "⥣⥥"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):after { + color: var(--color-v-kbd-trap-enabled, #c33); +} diff --git a/dist/styles/index.sass b/dist/styles/index.sass new file mode 100644 index 0000000..1f3480a --- /dev/null +++ b/dist/styles/index.sass @@ -0,0 +1,107 @@ +@charset "UTF-8" + +$ColorVKeyboardTrapEnabled: #c33 !default +$ColorVKeyboardTrapDisabled: #999 !default +$ColorVKeyboardTrapBackground: #eeee !default + +$TextVKeyboardTrapSeparator: "/" !default +$TextVKeyboardTrapEnabled: "Trap" !default +$TextVKeyboardTrapEsc: "Esc" !default +$TextVKeyboardTrapEscRefocus: "Esc\2949" !default +$TextVKeyboardTrapEscExits: "Esc\2923" !default +$TextVKeyboardTrapTab: "Tab" !default +$TextVKeyboardTrapTabExits: "Tab\21C5" !default +$TextVKeyboardTrapGrid: "\229E" !default +$TextVKeyboardTrapArrowsAll: "\2962\2963\2965\2964" !default +$TextVKeyboardTrapArrowsHorizontal: "\2962\2964" !default +$TextVKeyboardTrapArrowsVertical: "\2963\2965" !default + +// :root +// --color-v-kbd-trap-enabled: #c33 +// --color-v-kbd-trap-disabled: #999 +// --color-v-kbd-trap-background: #eeee +// --text-v-kbd-trap-separator: "/" +// --text-v-kbd-trap-enabled: "Trap" +// --text-v-kbd-trap-esc: "Esc" +// --text-v-kbd-trap-esc-refocus: "Esc\2949" +// --text-v-kbd-trap-esc-exits: "Esc\2923" +// --text-v-kbd-trap-tab: "Tab" +// --text-v-kbd-trap-tab-exits: "Tab\21C5" +// --text-v-kbd-trap-grid: "\229E" +// --text-v-kbd-trap-arrows-all: "\2962\2963\2965\2964" +// --text-v-kbd-trap-arrows-horizontal: "\2962\2964" +// --text-v-kbd-trap-arrows-vertical: "\2963\2965" + +[data-v-kbd-trap] + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "#{$TextVKeyboardTrapEnabled}") + --v-kbd-trap-esc: "" + --v-kbd-trap-tab: "" + --v-kbd-trap-roving: "" + + &:where(:has(:focus-visible)) + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "#{$TextVKeyboardTrapEnabled}") var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "#{$TextVKeyboardTrapEsc}") + + &:after + content: var(--v-kbd-trap, "") var(--v-kbd-trap-esc, "") var(--v-kbd-trap-tab, "") var(--v-kbd-trap-roving, "") + pointer-events: none + position: absolute + top: 2px + right: 2px + font: italic small-caps bold 14px monospace + line-height: 1em + padding: 4px + background-color: var(--color-v-kbd-trap-background, #{$ColorVKeyboardTrapBackground}) + border-radius: 2px + z-index: 1 + + &:where([tabindex="-9999"], dialog, [popover]) + outline: none + + &:after + color: var(--color-v-kbd-trap-disabled, #{$ColorVKeyboardTrapDisabled}) + + &:where([data-v-kbd-trap-active]):after + color: var(--color-v-kbd-trap-enabled, #{$ColorVKeyboardTrapEnabled}) + + &:where([data-v-kbd-trap-active]) + --v-kbd-trap: "" + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "#{$TextVKeyboardTrapEsc}") + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab, "#{$TextVKeyboardTrapTab}") + --v-kbd-trap-roving: "" + + &:where([data-v-kbd-trap~=roving]) + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab-exits, "#{$TextVKeyboardTrapTabExits}") + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-all, "#{$TextVKeyboardTrapArrowsAll}") + + &:where([data-v-kbd-trap~=tabinside]) + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab, "#{$TextVKeyboardTrapTab}") + + &:where([data-v-kbd-trap~=vertical]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-vertical, "#{$TextVKeyboardTrapArrowsVertical}") + + &:where([data-v-kbd-trap~=horizontal]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-horizontal, "#{$TextVKeyboardTrapArrowsHorizontal}") + + &:where([data-v-kbd-trap~=grid], [role=grid]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-grid, "#{$TextVKeyboardTrapGrid}") + + &:has(input:not([disabled]):not([type="button"]):not([type="checkbox"]):not([type="file"]):not([type="image"]):not([type="radio"]):not([type="reset"]):not([type="submit"]):focus-visible, select:not([disabled]):focus-visible, select:not([disabled]) *:focus-visible, textarea:not([disabled]):focus-visible, [contenteditable]:not([contenteditable="false"]):focus-visible, [contenteditable]:not([contenteditable="false"]) *:focus-visible) + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab, "#{$TextVKeyboardTrapTab}") + --v-kbd-trap-roving: "" + + &:where([data-v-kbd-trap~=escrefocus]) + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-refocus, "#{$TextVKeyboardTrapEscRefocus}") + + &:where([data-v-kbd-trap~=escexits]) + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-exits, "#{$TextVKeyboardTrapEscExits}") + + &:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-horizontal, "#{$TextVKeyboardTrapArrowsHorizontal}") + &:after + color: var(--color-v-kbd-trap-enabled, #{$ColorVKeyboardTrapEnabled}) + + &:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-vertical, "#{$TextVKeyboardTrapArrowsVertical}") + &:after + color: var(--color-v-kbd-trap-enabled, #{$ColorVKeyboardTrapEnabled}) diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts new file mode 100644 index 0000000..e822b4f --- /dev/null +++ b/dist/types/index.d.ts @@ -0,0 +1,39 @@ +import type { App, ComponentPublicInstance, MaybeRefOrGetter } from 'vue'; + +export interface IVueKeyboardTrapDirectiveOptions { + name?: string; + datasetName?: string; + focusableSelector?: string; + rovingSkipSelector?: string; + gridSkipSelector?: string; + autofocusSelector?: string; + trapTabIndex?: number; +} + +export interface IUseKeyboardTrapModifiers { + autofocus: boolean; + escexits: boolean; + escrefocus: boolean; + grid: boolean; + horizontal: boolean; + indexorder: boolean; + roving: boolean; + tabinside: boolean; + vertical: boolean; +} + +type IVueDirectivePlugin = { + install( app: App, options: IVueKeyboardTrapDirectiveOptions ): void, +}; + +export const VueKeyboardTrapDirectivePlugin: IVueDirectivePlugin; +export function VueKeyboardTrapDirectiveFactory(options?: IVueKeyboardTrapDirectiveOptions): { name: string, directive: object; }; + +export type IUseKeyboardTrap = ( + el: MaybeRefOrGetter, + modifiers?: MaybeRefOrGetter>, + active?: MaybeRefOrGetter, +) => void; +export function useKeyboardTrapFactory(options?: IVueKeyboardTrapDirectiveOptions): IUseKeyboardTrap + +export default VueKeyboardTrapDirectivePlugin; diff --git a/dist/web-types/index.json b/dist/web-types/index.json new file mode 100644 index 0000000..54621d5 --- /dev/null +++ b/dist/web-types/index.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json.schemastore.org/web-types", + "framework": "vue", + "name": "vue-pdan", + "version": "1.0.0", + "js-types-syntax": "typescript", + "contributions": { + "html": { + "vue-directives": [ + { + "name": "v-kbd-trap", + "source": { + "module": "vue-pdan", + "symbol": "KbdTrap" + }, + "required": false, + "description": "VueKeyboardTrap - directive for keyboard navigation - roving movement and trapping inside container", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/", + "vue-modifiers": [ + { + "name": "autofocus", + "description": "Autofocuses the element with [autofocus] or [data-autofocus] attribute when the directive is mounted or enabled (only if it not covered by another element)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "roving", + "description": "Allow roving navigation (Home, End, ArrowKeys) - it's the same as using .roving.vertical.horizontal", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "vertical", + "description": "Used with .roving - allow roving navigation (Home, End, ArrowUp, ArrowDown)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "horizontal", + "description": "Used with .roving - allow roving navigation (Home, End, ArrowLeft, ArrowRight)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "grid", + "description": "Used with .roving - allow roving navigation (Home, End, ArrowKeys) using dataset attrs on elements [data-${ camelCase from datasetName }-(row|col)]; [data-${ camelCase from datasetName }-(row|col)~=\"*\"] is a catchall", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "tabinside", + "description": "Used with .roving - Tab key navigates to next/prev element inside trap (by default Tab key navigates to next/prev element outside trap in roving mode)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "escrefocus", + "description": "Refocus element that was in focus before activating the trap on Esc", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "escexits", + "description": "Refocus a parent trap on Esc (has priority over .escrefocus)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "indexorder", + "description": "When used without .grid modifier and on elements without [role=\"grid\"]: force usage of order in tabindex (tabindex in ascending order and then DOM order)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + } + ], + "value": { + "kind": "expression", + "type": "boolean", + "description": "Disable directive if false" + } + } + ] + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2caa22c --- /dev/null +++ b/package.json @@ -0,0 +1,113 @@ +{ + "name": "@pdanpdan/vue-keyboard-trap", + "version": "1.2.0", + "description": "Vue3 and Vue2 directive for keyboard navigation - roving movement and trapping inside container", + "productName": "Vue Keyboard Trap", + "author": { + "name": "Dan Popescu", + "email": "pdan.popescu@gmail.com", + "url": "https://github.com/pdanpdan" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/pdanpdan" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/pdanpdan/vue-keyboard-trap.git" + }, + "keywords": [ + "vue", + "vue2", + "vue3", + "composable", + "directive", + "keyboard", + "navigation", + "trap", + "roving", + "grid", + "gridcell", + "a11y", + "accessibility", + "cycle", + "tab", + "tabindex", + "use", + "wai-aria" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/pdanpdan/vue-keyboard-trap/issues" + }, + "homepage": "https://pdanpdan.github.io/vue-keyboard-trap", + "type": "module", + "main": "./dist/index.umd.js", + "module": "./dist/index.es.js", + "exports": { + ".": { + "require": "./dist/index.umd.js", + "import": "./dist/index.es.js", + "types": "./dist/types/index.d.ts", + "style": "./dist/styles/index.css", + "css": "./dist/styles/index.css", + "sass": "./dist/styles/index.sass", + "web-types": "./dist/web-types/index.json" + }, + "./styles/css": "./dist/styles/index.css", + "./styles/sass": "./dist/styles/index.sass", + "./types": "./dist/types/index.d.ts" + }, + "typings": "./dist/types/index.d.ts", + "types": "./dist/types/index.d.ts", + "web-types": "./dist/web-types/index.json", + "files": [ + "dist/", + "src/" + ], + "directories": { + "doc": "docs" + }, + "sideEffects": [ + "*.sass", + "*.css" + ], + "scripts": { + "dev": "vite --config ./vite.dev.config.js", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:deploy": "./deploy.docs.sh", + "lint": "eslint --ext .js,.vue ./", + "build": "vite build --config ./vite.src.config.js", + "prepublishOnly": "pnpm lint && pnpm build" + }, + "dependencies": { + "vue-demi": "^0.14.10" + }, + "peerDependencies": { + "@vue/composition-api": ">=1.0.0", + "vue": ">=2.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.1.4", + "eslint": "^8.57.1", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-vue": "^9.28.0", + "flexsearch": "^0.7.43", + "markdown-it": "^14.1.0", + "sass-embedded": "^1.79.1", + "vite": "^5.4.6", + "vitepress": "1.3.4", + "vue": "^3.5.6" + }, + "engines": { + "node": ">= 12.22.0" + }, + "packageManager": "pnpm@9.10.0" +} diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..e885ccf --- /dev/null +++ b/src/App.vue @@ -0,0 +1,661 @@ + + + + + diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b3844b654a8500a067a5022aef228cd5c02d024b GIT binary patch literal 57076 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 literal 0 HcmV?d00001 diff --git a/src/directives/keyboard-trap/directive.js b/src/directives/keyboard-trap/directive.js new file mode 100644 index 0000000..32dd0c5 --- /dev/null +++ b/src/directives/keyboard-trap/directive.js @@ -0,0 +1,648 @@ +import { + isVue3, + computed, + markRaw, + unref, + watch, + getCurrentScope, + onScopeDispose, +} from 'vue-demi'; + +import { createConfig } from './options'; +import { + extractNumber, + focus, + visibleFocusCheckFn, + dirIsRtl, +} from './helpers'; + +// options: +// name: snake-case name of the directive (without `v-` prefix) - default `kbd-trap` +// datasetName: camelCase name of the `data-attribute` to be set on element when trap is enabled - default `v${ PascalCase from name}` +// +// focusableSelector: CSS selector for focusable elements +// rovingSkipSelector: CSS selector for elements that should not respond to roving key navigation (input, textarea, ...) +// gridSkipSelector: CSS selector that will be applied in .roving.grid mode to exclude elements - must be a series of :not() selectors +// autofocusSelector: CSS selector for the elements that should be autofocused +// trapTabIndex: tabIndex value to be used when trap element has a tabIndex of -1 and has no `tabindex` attribute (default -9999) +// +// +// value: false to disable +// +// +// modifiers: +// .autofocus - autofocuses the first element that matches `autofocusSelector` or (if no such element is found) the first focusable child element when the directive is mounted or enabled +// .roving, .roving.vertical.horizontal - allow roving navigation (Home, End, ArrowKeys) +// .roving.vertical - allow roving navigation (Home, End, ArrowUp, ArrowDown) +// .roving.horizontal - allow roving navigation (Home, End, ArrowLeft, ArrowRight) +// .roving.grid - allow roving navigation (Home, End, ArrowKeys) using dataset attrs on elements [data-${ camelCase from datasetName }-(row|col)] +// [data-${ camelCase from datasetName }-(row|col)~="*"] is a catchall +// .roving used on an element with [role="grid"] - allow roving navigation (Home, End, ArrowKeys) using role attrs on elements [role="row|gridcell"] +// .roving.tabinside - Tab key navigates to next/prev element inside trap (by default Tab key navigates to next/prev element outside trap in roving mode) +// .escrefocus - refocus element that was in focus before activating the trap on Esc +// .escexits - refocus a parent trap on Esc (has priority over .escrefocus) +// .indexorder used without .grid and not on elements with [role="grid"] - force usage of order in tabindex (tabindex in ascending order and then DOM order) + +let activeTrapEl = null; + +function setActiveTrapEl(newEl, config) { + if (activeTrapEl !== newEl) { + if (newEl != null) { + newEl.dataset[config.datasetNameActive] = ''; + newEl.__vKbdTrapActiveClean = () => { + delete newEl.dataset[config.datasetNameActive]; + newEl.__vKbdTrapActiveClean = undefined; + }; + } + + if (activeTrapEl != null && typeof activeTrapEl.__vKbdTrapActiveClean === 'function') { + activeTrapEl.__vKbdTrapActiveClean(); + } + + activeTrapEl = newEl; + } +} + +function getCtx(el) { + const ctx = (el || {}).__vKbdTrap; + + return ctx === Object(ctx) ? ctx : null; +} + +function setAttributes(el, disable, ctx, config) { + if (disable === true) { + delete el.dataset[config.datasetName]; + + if (el.tabIndex === config.trapTabIndex) { + el.removeAttribute('tabindex'); + } + } else { + el.dataset[config.datasetName] = Object.keys(ctx.modifiers) + .filter((key) => ctx.modifiers[key] === true) + .join(' '); + + if (el.tabIndex < 0 && el.getAttribute('tabindex') == null && el.matches('dialog') === false && el.matches('[popover]') === false) { + el.tabIndex = config.trapTabIndex; + } + } +} + +function createCtx(config, el, value, modifiers) { + const ctx = { + disable: value === false, + modifiers, + + focusTarget: null, + relatedFocusTarget: null, + + bind() { + el.__vKbdTrap = ctx; + el.addEventListener('keydown', ctx.trap); + el.addEventListener('focusin', ctx.activate); + el.addEventListener('focusout', ctx.deactivate); + el.addEventListener('pointerdown', ctx.overwriteFocusTarget, { passive: true }); + + if (ctx.disable === false) { + setAttributes(el, ctx.disable, ctx, config); + } + }, + + unbind() { + delete el.__vKbdTrap; + el.removeEventListener('keydown', ctx.trap); + el.removeEventListener('focusin', ctx.activate); + el.removeEventListener('focusout', ctx.deactivate); + el.removeEventListener('pointerdown', ctx.overwriteFocusTarget); + setAttributes(el, true, ctx, config); + }, + + activate(ev) { + if (ctx.disable === true || ev.__vKbdTrap === true) { + return; + } + + ev.__vKbdTrap = true; + + const oldFocusedElement = ev.relatedTarget; + + if ( + oldFocusedElement != null + && oldFocusedElement !== document.body + && oldFocusedElement.closest(config.datasetNameSelector) !== el + && oldFocusedElement.tabIndex !== config.trapTabIndex + ) { + ctx.relatedFocusTarget = oldFocusedElement; + } + + if ( + activeTrapEl !== el + && ( + oldFocusedElement == null + || oldFocusedElement.closest(config.datasetNameSelector) !== el + ) + ) { + setActiveTrapEl(el, config); + + if ( + oldFocusedElement == null + || oldFocusedElement.dataset[config.datasetNamePreventRefocus] === undefined + || el.contains(oldFocusedElement) === false + ) { + ctx.refocus(ctx.modifiers.roving !== true); + } + } + }, + + deactivate(ev) { + if (ctx.disable === true || ev.__vKbdTrap === true) { + return; + } + + ev.__vKbdTrap = true; + + const newFocusedElement = ev.relatedTarget; + + if ( + activeTrapEl === el + && ( + newFocusedElement == null + || newFocusedElement.closest(config.datasetNameSelector) !== el + ) + ) { + ctx.focusTarget = ev.target; + + if (newFocusedElement == null && ctx.relatedFocusTarget) { + focus(ctx.relatedFocusTarget); + } + setActiveTrapEl(null, config); + } + }, + + trap(ev) { + if (ctx.disable === true || ev.__vKbdTrap === true) { + return; + } + + const { code, shiftKey } = ev; + const { activeElement } = document; + + if (code === 'Escape') { + ev.__vKbdTrap = true; + + if (activeTrapEl === el) { + ctx.focusTarget = activeElement; + + if (shiftKey === true) { + ev.preventDefault(); + } else { + if (ctx.modifiers.escexits === true) { + setActiveTrapEl(el.parentElement == null ? null : el.parentElement.closest(config.datasetNameSelector), config); + + const newCtx = getCtx(activeTrapEl); + + if (newCtx != null) { + newCtx.refocus(); + } + + return; + } + + if (ctx.modifiers.escrefocus === true && focus(ctx.relatedFocusTarget) === true) { + return; + } + } + + const trapEl = el.parentElement && el.parentElement.closest(config.datasetNameSelector); + setActiveTrapEl(trapEl || null, config); + } else { + setActiveTrapEl(el, config); + } + + return; + } + + if (activeTrapEl !== el) { + return; + } + + ev.__vKbdTrap = true; + + let step = 0; + let indexSelector = (i) => i; + let rovingExit = false; + let rovingDirection = false; + + if (ctx.modifiers.roving === true) { + const rovingSkipSelector = activeElement.matches(config.rovingSkipSelector); + + if (code !== 'Tab' && rovingSkipSelector === true) { + return; + } + + if (code === 'Tab') { + if (rovingSkipSelector === false && ctx.modifiers.tabinside !== true) { + rovingExit = el.parentElement.closest(config.datasetNameSelector); + + if (rovingExit != null) { + ev.__vKbdTrap = undefined; + } + + if (shiftKey === true) { + step = 1; + indexSelector = (_, iMax) => iMax; + } else { + step = -1; + indexSelector = () => 0; + } + } else { + step = shiftKey === true ? -1 : 1; + } + } else if (code === 'Home') { + step = 1; + indexSelector = (_, iMax) => iMax; + } else if (code === 'End') { + step = -1; + indexSelector = () => 0; + } else if ( + el.parentElement != null + && ( + ( + ctx.modifiers.vertical === true + && ctx.modifiers.horizontal !== true + && (code === 'ArrowLeft' || code === 'ArrowRight') + ) || ( + ctx.modifiers.horizontal === true + && ctx.modifiers.vertical !== true + && (code === 'ArrowUp' || code === 'ArrowDown') + ) + ) + ) { + const parentTrap = el.parentElement.closest( + ctx.modifiers.vertical === true + ? config.datasetNameSelectorRovingHorizontal + : config.datasetNameSelectorRovingVertical, + ); + + if (parentTrap != null) { + rovingExit = parentTrap; + + ev.__vKbdTrap = undefined; + + if (code === (dirIsRtl(activeElement, el) === true ? 'ArrowRight' : 'ArrowLeft') || code === 'ArrowUp') { + step = 1; + indexSelector = (_, iMax) => iMax; + } else { + step = -1; + indexSelector = () => 0; + } + } + } else { + if (ctx.modifiers.vertical === true || ctx.modifiers.horizontal !== true) { + if (code === 'ArrowUp') { + step = -1; + rovingDirection = 'v'; + } else if (code === 'ArrowDown') { + step = 1; + rovingDirection = 'v'; + } + } + + if (ctx.modifiers.vertical !== true || ctx.modifiers.horizontal === true) { + if (code === 'ArrowLeft') { + step = -1; + rovingDirection = 'h'; + } else if (code === 'ArrowRight') { + step = 1; + rovingDirection = 'h'; + } + + if (step !== 0 && rovingDirection === 'h' && dirIsRtl(activeElement, el) === true) { + step *= -1; + } + } + } + } else if (code === 'Tab') { + step = shiftKey === true ? -1 : 1; + } + + if (step === 0) { + return; + } + + if (rovingExit === false) { + ev.preventDefault(); + } else { + ctx.focusTarget = activeElement; + ctx.focusTarget.dataset[config.datasetNamePreventRefocus] = ''; + + requestAnimationFrame(() => { + if (ctx.focusTarget) { + delete ctx.focusTarget.dataset[config.datasetNamePreventRefocus]; + } + }); + } + + let focusableList = []; + + if (rovingDirection !== false) { + let focusableMap; + + if (ctx.modifiers.grid === true) { + const row = extractNumber(activeElement.dataset[config.datasetNameRow]); + const col = extractNumber(activeElement.dataset[config.datasetNameCol]); + + const focusableSelector = rovingDirection === 'v' ? config.datasetNameColSelector(col) : config.datasetNameRowSelector(row); + focusableList = Array.from(el.querySelectorAll(focusableSelector)); + + focusableMap = new WeakMap( + focusableList.map((o) => { + const r = extractNumber(o.dataset[config.datasetNameRow]); + const c = extractNumber(o.dataset[config.datasetNameCol]); + let val; + + if (rovingDirection === 'v') { + if (r !== row || c === col) { + val = 1000 * r + 1 * c; + } + } else if (c !== col || r === row) { + val = 1000 * c + 1 * r; + } + + return [o, val]; + }), + ); + } else if (el.matches('[role="grid"]') === true && activeElement.matches('[role="row"] [role="gridcell"]')) { + const rows = Array.from(el.querySelectorAll('[role="row"]')); + const elToRowCol = new WeakMap(); + const rowsCells = rows.map((r, rIndex) => { + const cols = Array.from(r.querySelectorAll('[role="gridcell"]')); + + cols.forEach((o, cIndex) => { + elToRowCol.set(o, [rIndex + 1, cIndex + 1]); + }); + + return cols; + }); + const curRow = activeElement.closest('[role="row"]'); + const row = rows.indexOf(curRow) + 1; + const col = rowsCells[row - 1].indexOf(activeElement) + 1; + + const { focusableSelector } = config; + focusableList = Array.from(el.querySelectorAll(focusableSelector)); + + focusableMap = new WeakMap( + focusableList.map((o) => { + const [r, c] = elToRowCol.get(o) || [null, null]; + let val; + + if (rovingDirection === 'v') { + if (c === col) { + val = 1 * r; + } + } else if (r === row) { + val = 1 * c; + } + + return [o, val]; + }), + ); + } + + if (focusableMap != null && rovingExit == null) { + focusableList = focusableList.filter((o) => focusableMap.get(o) !== undefined); + focusableList.sort((el1, el2) => focusableMap.get(el1) - focusableMap.get(el2)); + } + } + + if (focusableList.length === 0) { + const { focusableSelector } = config; + focusableList = Array.from(el.querySelectorAll(focusableSelector)); + + if (modifiers.indexorder === true && rovingExit == null) { + const tabindexOrder = new WeakMap( + focusableList.map((o) => ([o, Math.max(o.tabIndex || 0, 0)])), + ); + + focusableList.sort((el1, el2) => tabindexOrder.get(el1) - tabindexOrder.get(el2)); + } + + if (el.matches(focusableSelector)) { + focusableList.unshift(el); + } + } + + const focusableIndexLast = focusableList.length - 1; + + let focusableIndex = indexSelector(focusableList.indexOf(activeElement), focusableIndexLast); + + for (let i = 0; i < focusableIndexLast; i += 1) { + focusableIndex += step; + + if (focusableIndex < 0) { + focusableIndex = focusableIndexLast; + } else if (focusableIndex > focusableIndexLast) { + focusableIndex = 0; + } + + if (focus(focusableList[focusableIndex]) === true) { + if (rovingExit !== false) { + setActiveTrapEl(rovingExit, config); + } + + return; + } + } + }, + + overwriteFocusTarget(ev) { + if (ctx.disable === false && ev.__vKbdTrap !== true) { + ev.__vKbdTrap = true; + + ctx.focusTarget = ev.target; + } + }, + + refocus(onlyIfTrapEl) { + if ( + ctx.disable === false + && activeTrapEl === el + && ctx.focusTarget + ) { + let trapEl = ctx.focusTarget.closest(config.datasetNameSelector); + + while (trapEl && trapEl !== el) { + const newCtx = getCtx(trapEl); + + if (newCtx !== null && newCtx.disable === false && newCtx.focusTarget) { + setActiveTrapEl(trapEl, config); + return newCtx.refocus(onlyIfTrapEl !== undefined ? newCtx.modifiers.roving !== true : undefined); + } + + trapEl = trapEl.parentElement && trapEl.parentElement.closest(config.datasetNameSelector); + } + + if (ctx.focusTarget.tabIndex === config.trapTabIndex || ctx.focusTarget.matches('dialog') === true || ctx.focusTarget.matches('[popover]') === true) { + return (ctx.modifiers.autofocus === true && focus(el.querySelector(config.autofocusSelector)) === true) + || focus(el.querySelector(config.focusableSelector)) === true + || focus(ctx.focusTarget) === true; + } + + return onlyIfTrapEl === true + ? false + : focus(ctx.focusTarget) === true + || focus(el.querySelector(config.focusableSelector)) === true; + } + + return false; + }, + + autofocus() { + setActiveTrapEl(el, config); + + if (ctx.disable === false && focus(el.querySelector(config.autofocusSelector), visibleFocusCheckFn) === false) { + focus(el.querySelector(config.focusableSelector), visibleFocusCheckFn); + } + }, + }; + + return ctx; +} + +function bindFn(config, el, value, modifiers) { + const ctx = createCtx(config, el, value, modifiers); + + ctx.bind(); + + if (modifiers.autofocus === true) { + ctx.autofocus(); + } +} + +function updateFn(config, ctx, el, value, modifiers) { + const disable = value === false; + + ctx.modifiers = modifiers; + + setAttributes(el, disable, ctx, config); + + if (activeTrapEl === el) { + if (disable === true) { + setActiveTrapEl(null, config); + } else { + el.dataset[config.datasetNameActive] = ''; + } + } + + if (ctx.disable !== disable) { + ctx.disable = disable; + + if (modifiers.autofocus === true) { + ctx.autofocus(); + } else if (disable === false && activeTrapEl !== el && el.contains(document.activeElement) === true) { + setActiveTrapEl(el, config); + } + } +} + +function unbindFn(config, el) { + const ctx = getCtx(el); + + if (ctx !== null) { + ctx.unbind(); + } + + if (activeTrapEl === el) { + if (ctx.relatedFocusTarget) { + focus(ctx.relatedFocusTarget); + } + setActiveTrapEl(null, config); + } +} + +export default function directiveFactory(options) { + const config = createConfig(options); + + const mounted = (el, { value, modifiers }) => bindFn(config, el, value, modifiers); + + const updated = (el, { value, modifiers }) => { + const ctx = getCtx(el); + + if (ctx !== null) { + updateFn(config, ctx, el, value, modifiers); + } else if (isVue3) { + mounted(el, { value, modifiers }); + } else if (activeTrapEl === el) { + setActiveTrapEl(null, config); + } + }; + + const unmounted = (el) => unbindFn(config, el); + + return isVue3 + ? markRaw({ + name: config.name, + + directive: { + mounted, + updated, + unmounted, + getSSRProps() { }, + }, + }) + : { + name: config.name, + + directive: { + bind: mounted, + update: updated, + unbind: unmounted, + }, + }; +} + +// helper because vue-demi does not have it +function toValue(maybeRef) { + return typeof maybeRef === 'function' + ? maybeRef() + : unref(maybeRef); +} + +export function composableFactory(options) { + const config = createConfig(options); + + return (maybeElementOrComponentRefOrComputed, modifiersRefOrComputed = {}, activeRefOrComputed = true) => { + const elComputed = computed(() => { + const elOrComponent = toValue(maybeElementOrComponentRefOrComputed); + if (elOrComponent == null) { + return null; + } + return markRaw('$el' in elOrComponent ? elOrComponent.$el : elOrComponent); + }); + + const unwatch = watch(() => [elComputed.value, toValue(activeRefOrComputed), toValue(modifiersRefOrComputed)], ([el, value, modifiers], [oldEl] = []) => { + if (el == null && oldEl == null) { + return; + } + + if (oldEl == null && el != null) { + bindFn(config, el, value, modifiers); + } else if (el == null) { + unbindFn(config, el); + } else if (el !== oldEl) { + unbindFn(config, oldEl); + bindFn(config, el, value, modifiers); + } else { + updateFn(config, getCtx(el), el, value, modifiers); + } + }, { flush: 'sync', deep: true, immediate: true }); + + if (getCurrentScope()) { + onScopeDispose(() => { + unwatch(); + if (elComputed.value != null) { + unbindFn(config, elComputed.value); + } + }); + } + }; +} diff --git a/src/directives/keyboard-trap/helpers.js b/src/directives/keyboard-trap/helpers.js new file mode 100644 index 0000000..920f22c --- /dev/null +++ b/src/directives/keyboard-trap/helpers.js @@ -0,0 +1,101 @@ +function defaultFocusCheckFn() { + return true; +} + +export function visibleFocusCheckFn(el, scrolled = false) { + if (el.closest('dialog') != null) { + return true; + } + + const { + left, + right, + top, + bottom, + } = el.getBoundingClientRect(); + + if (left === right && top === bottom) { + return true; + } + + const posList = [ + [left, top], + [left, (top + bottom) / 2], + [left, bottom], + [(left + right) / 2, top], + [(left + right) / 2, (top + bottom) / 2], + [(left + right) / 2, bottom], + [right, top], + [right, (top + bottom) / 2], + [right, bottom], + ]; + + let elAtPosFound = false; + + for (let i = 0; i < 9; i += 1) { + const elAtPos = document.elementFromPoint(...posList[i]); + + if (el.contains(elAtPos) === true) { + return true; + } + + if (elAtPos != null) { + elAtPosFound = true; + } + } + + if (scrolled === true || typeof el.scrollIntoView !== 'function') { + return !elAtPosFound; + } + + const scrollPos = []; + let parent = el.parentElement; + + while (parent != null) { + scrollPos.push([parent, parent.scrollLeft, parent.scrollTop]); + parent = parent.parentElement; + } + + el.scrollIntoView(); + + const visible = visibleFocusCheckFn(el, true); + + for (let i = scrollPos.length - 1; i >= 0; i -= 1) { + const [scrollEl, scrollLeft, scrollTop] = scrollPos[i]; + scrollEl.scrollLeft = scrollLeft; + scrollEl.scrollTop = scrollTop; + } + + return visible; +} + +let focusTargetEl; +export function focus(el, checkFn = defaultFocusCheckFn) { + if (el == null || typeof el.focus !== 'function' || checkFn(el) !== true) { + return false; + } + + focusTargetEl = el; + el.focus(); + + return [focusTargetEl, el].includes(document.activeElement) + || (document.activeElement != null && [focusTargetEl, el].includes(document.activeElement.__focusTargetPlaceholder)); +} + +const reNumber = /(\d+)/; + +export function extractNumber(val) { + const match = reNumber.exec(val); + + return match == null ? '' : match[1]; +} + +export function dirIsRtl(activeElement, currentTrapEl) { + const dirEl = ( + activeElement && activeElement !== currentTrapEl + ? activeElement.parentElement || currentTrapEl + : currentTrapEl + ).closest('[dir="rtl"],[dir="ltr"]'); + + return dirEl && dirEl.matches('[dir="rtl"]'); +} diff --git a/src/directives/keyboard-trap/index.js b/src/directives/keyboard-trap/index.js new file mode 100644 index 0000000..41561a2 --- /dev/null +++ b/src/directives/keyboard-trap/index.js @@ -0,0 +1,17 @@ +import directiveFactory, { composableFactory } from './directive'; + +const VueKeyboardTrapDirectivePlugin = { + install(app, options) { + const { name, directive } = directiveFactory(options); + + app.directive(name, directive); + }, +}; + +export { + VueKeyboardTrapDirectivePlugin, + directiveFactory as VueKeyboardTrapDirectiveFactory, + composableFactory as useKeyboardTrapFactory, +}; + +export default VueKeyboardTrapDirectivePlugin; diff --git a/src/directives/keyboard-trap/options.js b/src/directives/keyboard-trap/options.js new file mode 100644 index 0000000..cf44b5a --- /dev/null +++ b/src/directives/keyboard-trap/options.js @@ -0,0 +1,106 @@ +// options: +// name: snake-case name of the directive (without `v-` prefix) - default `kbd-trap` +// datasetName: camelCase name of the `data-attribute` to be set on element when trap is enabled - default `v${ PascalCase from name}` +// +// focusableSelector: CSS selector for focusable elements +// rovingSkipSelector: CSS selector for elements that should not respond to roving key navigation (input, textarea, ...) +// gridSkipSelector: CSS selector that will be applied in .roving.grid mode to exclude elements - must be a series of :not() selectors +// autofocusSelector: CSS selector for the elements that should be autofocused +// trapTabIndex: tabIndex value to be used when trap element has a tabIndex of -1 and has no `tabindex` attribute (default -9999) + +function createConfig(options) { + const config = { + name: 'kbd-trap', + + focusableSelector: [':focus'] + .concat( + [ + 'a[href]', + 'area[href]', + 'audio[controls]', + 'video[controls]', + 'iframe', + '[tabindex]:not(slot)', + '[contenteditable]:not([contenteditable="false"])', + 'details > summary:first-of-type', + ].map((s) => `${ s }:not([tabindex^="-"])`), + ) + .concat( + [ + 'input:not([type="hidden"]):not(fieldset[disabled] input)', + 'select:not(fieldset[disabled] select)', + 'textarea:not(fieldset[disabled] textarea)', + 'button:not(fieldset[disabled] button)', + '[class*="focusable"]', + ].map((s) => `${ s }:not([disabled]):not([tabindex^="-"])`), + ) + .concat( + [ + 'input:not([type="hidden"])', + 'select', + 'textarea', + 'button', + ].map((s) => `fieldset[disabled]:not(fieldset[disabled] fieldset) > legend ${ s }:not([disabled]):not([tabindex^="-"])`), + ) + .join(','), + + rovingSkipSelector: [ + 'input:not([disabled]):not([type="button"]):not([type="checkbox"]):not([type="file"]):not([type="image"]):not([type="radio"]):not([type="reset"]):not([type="submit"])', + 'select:not([disabled])', + 'select:not([disabled]) *', + 'textarea:not([disabled])', + '[contenteditable]:not([contenteditable="false"])', + '[contenteditable]:not([contenteditable="false"]) *', + ].join(','), + + gridSkipSelector: [ + ':not([disabled])', + ':not([tabindex^="-"])', + ].join(''), + + autofocusSelector: [ + '[autofocus]:not([autofocus="false"])', + '[data-autofocus]:not([data-autofocus="false"])', + ].map((s) => `${ s }:not([disabled])`).join(','), + + trapTabIndex: -9999, + + ...options, + }; + + const pascalName = config.name + .toLocaleLowerCase() + .split(/[^a-z0-9]+/) + .filter((t) => t.length > 0) + .map((t) => `${ t[0].toLocaleUpperCase() }${ t.slice(1) }`) + .join(''); + + if (config.datasetName === undefined) { + config.datasetName = `v${ pascalName }`; + } + + config.datasetNameActive = `${ config.datasetName }Active`; + config.datasetNamePreventRefocus = `${ config.datasetName }PreventRefocus`; + + if (typeof window === 'undefined') { + return config; + } + + const dsEl = document.createElement('span'); + dsEl.dataset[config.datasetName] = ''; + const datasetNameSnake = dsEl.getAttributeNames()[0]; + + config.datasetNameSelector = `[${ datasetNameSnake }]`; + config.datasetNameSelectorRovingHorizontal = `[${ datasetNameSnake }~="roving"][${ datasetNameSnake }~="horizontal"],[${ datasetNameSnake }~="roving"]:not([${ datasetNameSnake }~="vertical"])`; + config.datasetNameSelectorRovingVertical = `[${ datasetNameSnake }~="roving"][${ datasetNameSnake }~="vertical"],[${ datasetNameSnake }~="roving"]:not([${ datasetNameSnake }~="horizontal"])`; + + config.datasetNameRow = `${ config.datasetName }Row`; + config.datasetNameRowSelector = (i) => `:focus,[${ datasetNameSnake }-row~="${ i }"]${ config.gridSkipSelector },[${ datasetNameSnake }-row~="*"]${ config.gridSkipSelector }`; + + config.datasetNameCol = `${ config.datasetName }Col`; + config.datasetNameColSelector = (i) => `:focus,[${ datasetNameSnake }-col~="${ i }"]${ config.gridSkipSelector },[${ datasetNameSnake }-col~="*"]${ config.gridSkipSelector }`; + + return config; +} + +export { createConfig }; diff --git a/src/exports.js b/src/exports.js new file mode 100644 index 0000000..9e3187f --- /dev/null +++ b/src/exports.js @@ -0,0 +1,13 @@ +import { + useKeyboardTrapFactory, + VueKeyboardTrapDirectivePlugin, + VueKeyboardTrapDirectiveFactory, +} from './directives/keyboard-trap/index'; + +export default VueKeyboardTrapDirectivePlugin; + +export { + useKeyboardTrapFactory, + VueKeyboardTrapDirectivePlugin, + VueKeyboardTrapDirectiveFactory, +}; diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..c57941a --- /dev/null +++ b/src/main.js @@ -0,0 +1,17 @@ +import { createApp } from 'vue'; +import { + VueKeyboardTrapDirectivePlugin, + // VueKeyboardTrapDirectiveFactory, +} from './exports.js'; +import App from './App.vue'; + +import './public/styles/index.sass'; + +const app = createApp(App); + +app.use(VueKeyboardTrapDirectivePlugin); + +// const { name, directive } = VueKeyboardTrapDirectiveFactory(); +// app.directive(name, directive); + +app.mount('#app'); diff --git a/src/public/styles/index.css b/src/public/styles/index.css new file mode 100644 index 0000000..470f73d --- /dev/null +++ b/src/public/styles/index.css @@ -0,0 +1,96 @@ +@charset "UTF-8"; + +[data-v-kbd-trap] { + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "Trap"); + --v-kbd-trap-esc: ""; + --v-kbd-trap-tab: ""; + --v-kbd-trap-roving: ""; +} + +[data-v-kbd-trap]:where(:has(:focus-visible)) { + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "Trap") var(--text-v-kbd-trap-separator, "/"); + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "Esc"); +} + +[data-v-kbd-trap]:where(:has(:focus-visible)):after { + content: var(--v-kbd-trap, "") var(--v-kbd-trap-esc, "") var(--v-kbd-trap-tab, "") var(--v-kbd-trap-roving, ""); + pointer-events: none; + position: absolute; + top: 2px; + right: 2px; + font: italic small-caps bold 14px monospace; + line-height: 1em; + padding: 4px; + background-color: var(--color-v-kbd-trap-background, rgba(238, 238, 238, 0.9333333333)); + border-radius: 2px; + z-index: 1; +} + +[data-v-kbd-trap]:where([tabindex="-9999"], dialog, [popover]) { + outline: none; +} + +[data-v-kbd-trap]:after { + color: var(--color-v-kbd-trap-disabled, #999); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):after { + color: var(--color-v-kbd-trap-enabled, #c33); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]) { + --v-kbd-trap: ""; + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "Esc"); + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab, "Tab"); + --v-kbd-trap-roving: ""; +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]) { + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab-exits, "Tab⇅"); + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-all, "⥢⥣⥥⥤"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=tabinside]) { + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab, "Tab"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=vertical]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-vertical, "⥣⥥"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=horizontal]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-horizontal, "⥢⥤"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):where([data-v-kbd-trap~=grid], [role=grid]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-grid, "⊞"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=roving]):has(input:not([disabled]):not([type=button]):not([type=checkbox]):not([type=file]):not([type=image]):not([type=radio]):not([type=reset]):not([type=submit]):focus-visible, select:not([disabled]):focus-visible, select:not([disabled]) *:focus-visible, textarea:not([disabled]):focus-visible, [contenteditable]:not([contenteditable=false]):focus-visible, [contenteditable]:not([contenteditable=false]) *:focus-visible) { + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-tab, "Tab"); + --v-kbd-trap-roving: ""; +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=escrefocus]) { + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-refocus, "Esc⥉"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap-active]):where([data-v-kbd-trap~=escexits]) { + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-exits, "Esc⤣"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-horizontal, "⥢⥤"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):after { + color: var(--color-v-kbd-trap-enabled, #c33); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]) { + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "/") var(--text-v-kbd-trap-arrows-vertical, "⥣⥥"); +} + +[data-v-kbd-trap]:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):after { + color: var(--color-v-kbd-trap-enabled, #c33); +} diff --git a/src/public/styles/index.sass b/src/public/styles/index.sass new file mode 100644 index 0000000..1f3480a --- /dev/null +++ b/src/public/styles/index.sass @@ -0,0 +1,107 @@ +@charset "UTF-8" + +$ColorVKeyboardTrapEnabled: #c33 !default +$ColorVKeyboardTrapDisabled: #999 !default +$ColorVKeyboardTrapBackground: #eeee !default + +$TextVKeyboardTrapSeparator: "/" !default +$TextVKeyboardTrapEnabled: "Trap" !default +$TextVKeyboardTrapEsc: "Esc" !default +$TextVKeyboardTrapEscRefocus: "Esc\2949" !default +$TextVKeyboardTrapEscExits: "Esc\2923" !default +$TextVKeyboardTrapTab: "Tab" !default +$TextVKeyboardTrapTabExits: "Tab\21C5" !default +$TextVKeyboardTrapGrid: "\229E" !default +$TextVKeyboardTrapArrowsAll: "\2962\2963\2965\2964" !default +$TextVKeyboardTrapArrowsHorizontal: "\2962\2964" !default +$TextVKeyboardTrapArrowsVertical: "\2963\2965" !default + +// :root +// --color-v-kbd-trap-enabled: #c33 +// --color-v-kbd-trap-disabled: #999 +// --color-v-kbd-trap-background: #eeee +// --text-v-kbd-trap-separator: "/" +// --text-v-kbd-trap-enabled: "Trap" +// --text-v-kbd-trap-esc: "Esc" +// --text-v-kbd-trap-esc-refocus: "Esc\2949" +// --text-v-kbd-trap-esc-exits: "Esc\2923" +// --text-v-kbd-trap-tab: "Tab" +// --text-v-kbd-trap-tab-exits: "Tab\21C5" +// --text-v-kbd-trap-grid: "\229E" +// --text-v-kbd-trap-arrows-all: "\2962\2963\2965\2964" +// --text-v-kbd-trap-arrows-horizontal: "\2962\2964" +// --text-v-kbd-trap-arrows-vertical: "\2963\2965" + +[data-v-kbd-trap] + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "#{$TextVKeyboardTrapEnabled}") + --v-kbd-trap-esc: "" + --v-kbd-trap-tab: "" + --v-kbd-trap-roving: "" + + &:where(:has(:focus-visible)) + --v-kbd-trap: var(--text-v-kbd-trap-enabled, "#{$TextVKeyboardTrapEnabled}") var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "#{$TextVKeyboardTrapEsc}") + + &:after + content: var(--v-kbd-trap, "") var(--v-kbd-trap-esc, "") var(--v-kbd-trap-tab, "") var(--v-kbd-trap-roving, "") + pointer-events: none + position: absolute + top: 2px + right: 2px + font: italic small-caps bold 14px monospace + line-height: 1em + padding: 4px + background-color: var(--color-v-kbd-trap-background, #{$ColorVKeyboardTrapBackground}) + border-radius: 2px + z-index: 1 + + &:where([tabindex="-9999"], dialog, [popover]) + outline: none + + &:after + color: var(--color-v-kbd-trap-disabled, #{$ColorVKeyboardTrapDisabled}) + + &:where([data-v-kbd-trap-active]):after + color: var(--color-v-kbd-trap-enabled, #{$ColorVKeyboardTrapEnabled}) + + &:where([data-v-kbd-trap-active]) + --v-kbd-trap: "" + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc, "#{$TextVKeyboardTrapEsc}") + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab, "#{$TextVKeyboardTrapTab}") + --v-kbd-trap-roving: "" + + &:where([data-v-kbd-trap~=roving]) + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab-exits, "#{$TextVKeyboardTrapTabExits}") + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-all, "#{$TextVKeyboardTrapArrowsAll}") + + &:where([data-v-kbd-trap~=tabinside]) + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab, "#{$TextVKeyboardTrapTab}") + + &:where([data-v-kbd-trap~=vertical]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-vertical, "#{$TextVKeyboardTrapArrowsVertical}") + + &:where([data-v-kbd-trap~=horizontal]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-horizontal, "#{$TextVKeyboardTrapArrowsHorizontal}") + + &:where([data-v-kbd-trap~=grid], [role=grid]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-grid, "#{$TextVKeyboardTrapGrid}") + + &:has(input:not([disabled]):not([type="button"]):not([type="checkbox"]):not([type="file"]):not([type="image"]):not([type="radio"]):not([type="reset"]):not([type="submit"]):focus-visible, select:not([disabled]):focus-visible, select:not([disabled]) *:focus-visible, textarea:not([disabled]):focus-visible, [contenteditable]:not([contenteditable="false"]):focus-visible, [contenteditable]:not([contenteditable="false"]) *:focus-visible) + --v-kbd-trap-tab: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-tab, "#{$TextVKeyboardTrapTab}") + --v-kbd-trap-roving: "" + + &:where([data-v-kbd-trap~=escrefocus]) + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-refocus, "#{$TextVKeyboardTrapEscRefocus}") + + &:where([data-v-kbd-trap~=escexits]) + --v-kbd-trap-esc: var(--text-v-kbd-trap-esc-exits, "#{$TextVKeyboardTrapEscExits}") + + &:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-horizontal, "#{$TextVKeyboardTrapArrowsHorizontal}") + &:after + color: var(--color-v-kbd-trap-enabled, #{$ColorVKeyboardTrapEnabled}) + + &:where([data-v-kbd-trap~=roving][data-v-kbd-trap~=vertical]):has([data-v-kbd-trap-active][data-v-kbd-trap~=roving][data-v-kbd-trap~=horizontal]) + --v-kbd-trap-roving: var(--text-v-kbd-trap-separator, "#{$TextVKeyboardTrapSeparator}") var(--text-v-kbd-trap-arrows-vertical, "#{$TextVKeyboardTrapArrowsVertical}") + &:after + color: var(--color-v-kbd-trap-enabled, #{$ColorVKeyboardTrapEnabled}) diff --git a/src/public/types/index.d.ts b/src/public/types/index.d.ts new file mode 100644 index 0000000..e822b4f --- /dev/null +++ b/src/public/types/index.d.ts @@ -0,0 +1,39 @@ +import type { App, ComponentPublicInstance, MaybeRefOrGetter } from 'vue'; + +export interface IVueKeyboardTrapDirectiveOptions { + name?: string; + datasetName?: string; + focusableSelector?: string; + rovingSkipSelector?: string; + gridSkipSelector?: string; + autofocusSelector?: string; + trapTabIndex?: number; +} + +export interface IUseKeyboardTrapModifiers { + autofocus: boolean; + escexits: boolean; + escrefocus: boolean; + grid: boolean; + horizontal: boolean; + indexorder: boolean; + roving: boolean; + tabinside: boolean; + vertical: boolean; +} + +type IVueDirectivePlugin = { + install( app: App, options: IVueKeyboardTrapDirectiveOptions ): void, +}; + +export const VueKeyboardTrapDirectivePlugin: IVueDirectivePlugin; +export function VueKeyboardTrapDirectiveFactory(options?: IVueKeyboardTrapDirectiveOptions): { name: string, directive: object; }; + +export type IUseKeyboardTrap = ( + el: MaybeRefOrGetter, + modifiers?: MaybeRefOrGetter>, + active?: MaybeRefOrGetter, +) => void; +export function useKeyboardTrapFactory(options?: IVueKeyboardTrapDirectiveOptions): IUseKeyboardTrap + +export default VueKeyboardTrapDirectivePlugin; diff --git a/src/public/web-types/index.json b/src/public/web-types/index.json new file mode 100644 index 0000000..54621d5 --- /dev/null +++ b/src/public/web-types/index.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json.schemastore.org/web-types", + "framework": "vue", + "name": "vue-pdan", + "version": "1.0.0", + "js-types-syntax": "typescript", + "contributions": { + "html": { + "vue-directives": [ + { + "name": "v-kbd-trap", + "source": { + "module": "vue-pdan", + "symbol": "KbdTrap" + }, + "required": false, + "description": "VueKeyboardTrap - directive for keyboard navigation - roving movement and trapping inside container", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/", + "vue-modifiers": [ + { + "name": "autofocus", + "description": "Autofocuses the element with [autofocus] or [data-autofocus] attribute when the directive is mounted or enabled (only if it not covered by another element)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "roving", + "description": "Allow roving navigation (Home, End, ArrowKeys) - it's the same as using .roving.vertical.horizontal", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "vertical", + "description": "Used with .roving - allow roving navigation (Home, End, ArrowUp, ArrowDown)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "horizontal", + "description": "Used with .roving - allow roving navigation (Home, End, ArrowLeft, ArrowRight)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "grid", + "description": "Used with .roving - allow roving navigation (Home, End, ArrowKeys) using dataset attrs on elements [data-${ camelCase from datasetName }-(row|col)]; [data-${ camelCase from datasetName }-(row|col)~=\"*\"] is a catchall", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "tabinside", + "description": "Used with .roving - Tab key navigates to next/prev element inside trap (by default Tab key navigates to next/prev element outside trap in roving mode)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "escrefocus", + "description": "Refocus element that was in focus before activating the trap on Esc", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "escexits", + "description": "Refocus a parent trap on Esc (has priority over .escrefocus)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + }, + { + "name": "indexorder", + "description": "When used without .grid modifier and on elements without [role=\"grid\"]: force usage of order in tabindex (tabindex in ascending order and then DOM order)", + "doc-url": "https://pdanpdan.github.io/vue-keyboard-trap/" + } + ], + "value": { + "kind": "expression", + "type": "boolean", + "description": "Disable directive if false" + } + } + ] + } + } +}