{"_id":"@a11y-ngx/tab-cycle","_rev":"4-fd27ff5cff63cf7e6c6690194a270635","name":"@a11y-ngx/tab-cycle","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.1":{"name":"@a11y-ngx/tab-cycle","version":"1.0.1","keywords":["tab","cycle","focus","trap","tabindex","a11y","accessibility","angular","directive"],"author":{"url":"https://github.com/LDV2k3/","name":"Luciano Del Vacchio","email":"lucho.development@gmail.com"},"license":"MIT","_id":"@a11y-ngx/tab-cycle@1.0.1","maintainers":[{"name":"ldv","email":"lucho.development@gmail.com"}],"homepage":"https://github.com/LDV2k3/a11y-libraries/tree/master/projects/a11y-ngx/tab-cycle#readme","bugs":{"url":"https://github.com/LDV2k3/a11y-libraries/issues","email":"lucho.development@gmail.com"},"dist":{"shasum":"0e34f6be700c33d9505596078819934037ef702f","tarball":"https://registry.npmjs.org/@a11y-ngx/tab-cycle/-/tab-cycle-1.0.1.tgz","fileCount":16,"integrity":"sha512-6Fm6H3sUZO1DoGiucSKX6gNHwRYTtuUb3zf5f3gZdC6xjsRwmjPicYr05eQ2XE4RdhBvOHSB8F/WUQ6/wOgOew==","signatures":[{"sig":"MEUCIFJwjQybJ1bK0CQDrnSSeE1eq47Jru+mLbEG23gf8JoqAiEAivtu0GRhYVCut6iPub4ZoVbaEmzlJUcoJOKoMKc9Zfw=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":79647},"main":"bundles/a11y-ngx-tab-cycle.umd.js","es2015":"fesm2015/a11y-ngx-tab-cycle.js","module":"fesm2015/a11y-ngx-tab-cycle.js","esm2015":"esm2015/a11y-ngx-tab-cycle.js","gitHead":"00c6dcd50b36c553d84861d167e371a92da7b445","typings":"a11y-ngx-tab-cycle.d.ts","_npmUser":{"name":"ldv","email":"lucho.development@gmail.com"},"fesm2015":"fesm2015/a11y-ngx-tab-cycle.js","_npmVersion":"8.19.4","description":"An Angular directive to allow focus trap within a DOM element","directories":{},"sideEffects":false,"_nodeVersion":"16.20.2","dependencies":{"tslib":"^1.10.0","@a11y-ngx/dom-helper":">=1.0.0"},"_hasShrinkwrap":false,"peerDependencies":{"@angular/core":"^12.2.0","@angular/common":"^12.2.0"},"_npmOperationalInternal":{"tmp":"tmp/tab-cycle_1.0.1_1726516064138_0.925786588134264","host":"s3://npm-registry-packages"}},"1.0.2":{"name":"@a11y-ngx/tab-cycle","version":"1.0.2","description":"An Angular directive to allow focus trap within a DOM element","keywords":["tab","cycle","focus","trap","tabindex","a11y","accessibility","angular","directive"],"homepage":"https://github.com/LDV2k3/a11y-libraries/tree/master/projects/a11y-ngx/tab-cycle#readme","bugs":{"url":"https://github.com/LDV2k3/a11y-libraries/issues","email":"lucho.development@gmail.com"},"license":"MPL-2.0","author":{"name":"Luciano Del Vacchio","email":"lucho.development@gmail.com","url":"https://github.com/LDV2k3/"},"peerDependencies":{"@angular/common":">=12.2.0 <21.0.0","@angular/core":">=12.2.0 <21.0.0"},"dependencies":{"tslib":"^1.10.0","@a11y-ngx/dom-helper":">=1.1.1"},"main":"bundles/a11y-ngx-tab-cycle.umd.js","module":"fesm2015/a11y-ngx-tab-cycle.js","es2015":"fesm2015/a11y-ngx-tab-cycle.js","esm2015":"esm2015/a11y-ngx-tab-cycle.js","fesm2015":"fesm2015/a11y-ngx-tab-cycle.js","typings":"a11y-ngx-tab-cycle.d.ts","sideEffects":false,"gitHead":"0943b42a1333a74cbda740380ef033e73c182dd5","_id":"@a11y-ngx/tab-cycle@1.0.2","_nodeVersion":"16.20.2","_npmVersion":"8.19.4","dist":{"integrity":"sha512-gDH3Z0D2XaNRw4guL8mR91BBKAmVTYJt/C7xsUmq/LvKB1yt3Pbwd4OHcBORutuBpm9/Sgz+h6aowCyQ9MvqAg==","shasum":"42aa0495a4a77c5c9d6b4d0e04ffa285aec33816","tarball":"https://registry.npmjs.org/@a11y-ngx/tab-cycle/-/tab-cycle-1.0.2.tgz","fileCount":16,"unpackedSize":79669,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDSOu42XmNDNiEimbaGIGlyRte26yyAxri4iiNxX82frgIgRM+o7OammKDeaU/gWqDmINp80ycyNTtf1v4HUVhswUk="}]},"_npmUser":{"name":"ldv","email":"lucho.development@gmail.com"},"directories":{},"maintainers":[{"name":"ldv","email":"lucho.development@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tab-cycle_1.0.2_1761916335162_0.05724511999024773"},"_hasShrinkwrap":false}},"time":{"created":"2024-09-16T19:20:37.670Z","modified":"2025-10-31T13:12:15.561Z","1.0.0":"2024-09-16T19:20:37.969Z","1.0.1":"2024-09-16T19:47:44.299Z","1.0.2":"2025-10-31T13:12:15.354Z"},"bugs":{"url":"https://github.com/LDV2k3/a11y-libraries/issues","email":"lucho.development@gmail.com"},"author":{"name":"Luciano Del Vacchio","email":"lucho.development@gmail.com","url":"https://github.com/LDV2k3/"},"license":"MPL-2.0","homepage":"https://github.com/LDV2k3/a11y-libraries/tree/master/projects/a11y-ngx/tab-cycle#readme","keywords":["tab","cycle","focus","trap","tabindex","a11y","accessibility","angular","directive"],"description":"An Angular directive to allow focus trap within a DOM element","maintainers":[{"name":"ldv","email":"lucho.development@gmail.com"}],"readme":"# Tab Cycle (aka Focus Trap)\r\n\r\nAn Angular directive to allow focus trap within a DOM element.\r\n\r\nThe main idea of this library is to facilitate any type of work related to possible accessibility issues, like when accessing a modal or modal-like element (such as a _popover_), the tab-cycle should be enclosed within, so either the keyboard or screen reader users can have enough context of where they stand.\r\n\r\n> **IMPORTANT:** This will manage the internal tab-cycle when tabbing. It can help you on set the first focus (if needed), but considering there are tons of possible scenarios, you as a developer should return the focus (if apply) to whatever element that was triggered in the first place.\r\n\r\nThis library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 12.2.0.\r\n\r\n## Index\r\n\r\n- [Installation](#installation)\r\n- [The Directive](#the-directive)\r\n  - [Public Properties, Getters, Setters and Methods](#public-properties-getters-setters-and-methods)\r\n  - [Set On or Off the Tab-Cycle](#set-on-or-off-the-tab-cycle)\r\n  - [Set Initial Focus](#set-initial-focus)\r\n  - [The Tabbable Elements](#the-tabbable-elements)\r\n- [Use Example](#use-example)\r\n\r\n## Installation\r\n\r\n1. Install npm package:\r\n\r\n   `npm install @a11y-ngx/tab-cycle --save`\r\n\r\n2. Import `A11yTabCycleModule` into your module or standalone component:\r\n\r\n```typescript\r\nimport { A11yTabCycleModule } from '@a11y-ngx/tab-cycle';\r\n\r\n@NgModule({\r\n    declarations: [...],\r\n    imports: [\r\n        ...\r\n        A11yTabCycleModule,\r\n    ]\r\n})\r\nexport class AppModule { }\r\n```\r\n\r\n## The Directive\r\n\r\n- **Selector:** `[a11yTabCycle]`.\r\n- **Exported As:** `a11yTabCycle`.\r\n\r\nBy using the `a11yTabCycle` directive on any HTML element, it will create a focus trap the moment that the element or any of its children receives focus.\r\n\r\n1. It will create a `keydown` event listener on the host element to check whenever the user presses the `TAB` or `SHIFT+TAB` combination keys.\r\n2. It will check for any tabindex value already set on the host and, if none were found, a `-1` value will be automatically assigned.\r\n3. By considering the host a \"focus trap\", a couple of attributes will be also assigned to help Screen Reader users to have more context:\r\n    - The `role` attribute set to `'dialog'`.\r\n    - The `aria-modal` attribute set to `true`.\r\n4. When `TAB` or `SHIFT+TAB` keys are pressed, it will look for [every possible tabbable (and visible) element](#the-tabbable-elements) within the host and:\r\n    - If none were found, it will set focus on the host itself, preventing going outside.\r\n    - If any tabbable elements are detected, and:\r\n      - The user is standing on the last one and press `TAB`, it will set focus on the _first_ element found.\r\n      - The user is standing on the first one and press `SHIFT+TAB`, it will set focus on the _last_ element found.\r\n\r\n> **Accessibility Considerations:** Since this is an _actual_ trap, please **_do_** provide a way to exit the host using the keyboard, such as by adding a close button or pressing the Escape key.\r\n\r\n### Public Properties, Getters, Setters and Methods\r\n\r\n| Name | Type | Of Type | Description |\r\n|:-----|:-----|:--------|:------------|\r\n| `enabled` | `get`/`set` | `string` or `boolean` | See [how to set On or Off the Tab-Cycle](#set-on-or-off-the-tab-cycle) |\r\n| `tabindex` | `get`/`set` | `string` or `number` | To specify a custom tabindex value |\r\n| `nativeElement` | `get` | `HTMLElement` | The host element |\r\n| `tabbableElements` | `get` | `HTMLElement[]` | See [the tabbable elements](#the-tabbable-elements) |\r\n| `manageKeyDown()` | `method` | `void` | It handles the main logic of the tab-cycle |\r\n| `focus()` | `method` | `void` | See [how to set the initial focus](#set-initial-focus) |\r\n\r\n### Set On or Off the Tab-Cycle\r\n\r\nGiven `a11yTabCycle` is the main attribute entry of the directive, by its single presence, it will be considered as a `string` (empty in this case) and, therefor, it enables the tab-cycle.\r\n\r\nIt can also be established by using a `boolean` if you need to enable/disable on demand.\r\n\r\n```html\r\n<div a11yTabCycle></div>            <!-- Enabled (empty string) -->\r\n<div a11yTabCycle=\"false\"></div>    <!-- Enabled (string) -->\r\n<div [a11yTabCycle]=\"false\"></div>  <!-- Disabled (boolean) -->\r\n```\r\n\r\n### Set Initial Focus\r\n\r\nThe idea of this method is to set the initial focus on the first or last tabbable elements, or the host itself (by default).\r\n\r\n> **IMPORTANT:** The _host_ must be accessible for all asistive technologies, that's why allowing setting focus on any other element is not recommended, you have to be extra careful deciding where to set the initial focus.\r\n\r\nAccepts a single parameter `where` (_optional_) of type `'first'` or `'last'`.\r\n\r\n- If the method is invoked without the `where`, it will set focus on the host element.\r\n- If `'first'` is used, it will look for the first tabbable element and set focus on it.\r\n- If `'last'` is used, it will look for the last tabbable element and set focus on it.\r\n- If no tabbable elements were found, it will set focus on the host element.\r\n\r\n### The Tabbable Elements\r\n\r\nEvery element that could receive focus is considered \"tabbable\", which will allow to decide where to set focus when the start or end limit of the host has been reached when tabbing.\r\n\r\nAn element is considered tabbable/focusable when it can receive focus and is visible.\r\n\r\nYou can find [the list of all possible tabbable elements here](https://www.npmjs.com/package/@a11y-ngx/dom-helper#the-tabbableelements-method).\r\n\r\n- **Dependency:** [DOM Helper package](https://www.npmjs.com/package/@a11y-ngx/dom-helper).\r\n\r\n## Use Example\r\n\r\nIn the next example we are simulating a dialog modal, with a message and a couple of action buttons.\r\n\r\n> 📘 **NOTE:** The styles used for the modal's template are from [Bootstrap website](https://getbootstrap.com/docs/5.3/components/modal).\r\n>\r\n> ⚠️ **Accessibility Consideration:** Modals are far more complex than the following code, this is just an example of how the tab-cycle would work in a simple scenario where you have to trap the keyboard navigation and not allowing the user to go outside until they choose an action.\r\n>\r\n> This is because the modal is causing the rest of the website to be behind it and visually not reachable.\r\n\r\n- When the \"Delete\" button is triggered, the modal is shown and **_we_** tell the directive to set focus on the first tabbable element using the `focus('first')` method.\r\n- When we start to `TAB` or `SHIFT+TAB`, it will set focus only on the buttons, since they are the only tabbable elements within.\r\n- Once we action either \"Accept\" or \"Cancel\" buttons, the modal is hidden and (super important) **_we_** return the focus to the main button.\r\n\r\n```typescript\r\nimport { TabCycleDirective } from '@a11y-ngx/tab-cycle';\r\n...\r\n@Component({...})\r\nexport class MyComponent {\r\n    @ViewChild('myButton') private myButton!: ElementRef<HTMLButtonElement>;\r\n    @ViewChild('myModal') private myModal!: TabCycleDirective;\r\n    \r\n    showModal: boolean = false;\r\n    \r\n    openModal(): void {\r\n        this.showModal = true;\r\n        setTimeout(() => this.myModal.focus('first'));\r\n    }\r\n    \r\n    closeModal(action: string): void {\r\n        console.log(action);\r\n        this.showModal = false;\r\n        this.myButton.nativeElement.focus();\r\n    }\r\n}\r\n```\r\n\r\n```html\r\n<button type=\"button\" #myButton (click)=\"openModal()\" class=\"btn btn-danger\">Delete</button>\r\n\r\n<div\r\n    a11yTabCycle\r\n    #myModal=\"a11yTabCycle\"\r\n    [style.display]=\"showModal ? 'block' : 'none'\"\r\n    class=\"modal fade show\"\r\n    aria-labelledby=\"modal-body\">\r\n    <div class=\"modal-dialog\">\r\n        <div class=\"modal-content\">\r\n            <div class=\"modal-body\" id=\"modal-body\">\r\n                Are you sure you want to delete this record?\r\n            </div>\r\n            <div class=\"modal-footer\">\r\n                <button type=\"button\"\r\n                    class=\"btn btn-sm btn-primary\"\r\n                    (click)=\"closeModal('accept')\">\r\n                    Accept\r\n                </button>\r\n                <button type=\"button\"\r\n                    class=\"btn btn-sm btn-secondary\"\r\n                    (click)=\"closeModal('cancel')\">\r\n                    Cancel\r\n                </button>\r\n            </div>\r\n        </div>\r\n    </div>\r\n</div>\r\n```\r\n\r\n**Result:**\r\n\r\n![\"\"](https://raw.githubusercontent.com/LDV2k3/a11y-libraries/master/projects/a11y-ngx/tab-cycle/src/lib/images/example-modal.jpg)\r\n\r\n![\"\"](https://raw.githubusercontent.com/LDV2k3/a11y-libraries/master/projects/a11y-ngx/tab-cycle/src/lib/images/example-modal-open.jpg)\r\n","readmeFilename":"README.md"}