UNPKG

@coreui/coreui-pro

Version:

The most popular front-end framework for developing responsive, mobile-first projects on the web rewritten by the CoreUI Team

151 lines (139 loc) 4.88 kB
/*! * CoreUI focustrap.js v5.23.0 (https://coreui.io) * Copyright 2025 The CoreUI Team (https://github.com/orgs/coreui/people) * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/event-handler.js'), require('../dom/selector-engine.js'), require('./config.js')) : typeof define === 'function' && define.amd ? define(['../dom/event-handler', '../dom/selector-engine', './config'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Focustrap = factory(global.EventHandler, global.SelectorEngine, global.Config)); })(this, (function (EventHandler, SelectorEngine, Config) { 'use strict'; /** * -------------------------------------------------------------------------- * CoreUI util/focustrap.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's util/focustrap.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME = 'focustrap'; const DATA_KEY = 'coreui.focustrap'; const EVENT_KEY = `.${DATA_KEY}`; const EVENT_FOCUSIN = `focusin${EVENT_KEY}`; const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`; const TAB_KEY = 'Tab'; const TAB_NAV_FORWARD = 'forward'; const TAB_NAV_BACKWARD = 'backward'; const Default = { additionalElement: null, autofocus: true, trapElement: null // The element to trap focus inside of }; const DefaultType = { additionalElement: '(element|null|undefined)', autofocus: 'boolean', trapElement: 'element' }; /** * Class definition */ class FocusTrap extends Config { constructor(config) { super(); this._config = this._getConfig(config); this._isActive = false; this._lastTabNavDirection = null; } // Getters static get Default() { return Default; } static get DefaultType() { return DefaultType; } static get NAME() { return NAME; } // Public activate() { if (this._isActive) { return; } if (this._config.autofocus) { this._config.trapElement.focus(); } EventHandler.off(document, EVENT_KEY); // guard against infinite focus loop EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event)); EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); this._isActive = true; } deactivate() { if (!this._isActive) { return; } this._isActive = false; EventHandler.off(document, EVENT_KEY); } // Private _handleFocusin(event) { const { additionalElement, trapElement } = this._config; if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { return; } if (additionalElement && (event.target === additionalElement || additionalElement.contains(event.target))) { return; } const elements = SelectorEngine.focusableChildren(trapElement); if (elements.length === 0) { trapElement.focus(); } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { elements[elements.length - 1].focus(); } else { elements[0].focus(); } } _handleKeydown(event) { if (event.key !== TAB_KEY) { return; } this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; const { additionalElement, trapElement } = this._config; if (!additionalElement) { return; } const trapElements = SelectorEngine.focusableChildren(trapElement); const additionalElements = SelectorEngine.focusableChildren(additionalElement); if (trapElements.length === 0 || additionalElements.length === 0) { return; } event.preventDefault(); if (trapElements.indexOf(event.target) === trapElements.length - 1 && !event.shiftKey) { additionalElements[0].focus(); return; } if (trapElements.indexOf(event.target) === 0 && event.shiftKey) { additionalElements[additionalElements.length - 1].focus(); return; } if (additionalElements.indexOf(event.target) === additionalElements.length - 1 && !event.shiftKey) { trapElements[0].focus(); return; } if (additionalElements.indexOf(event.target) === 0 && event.shiftKey) { trapElements[trapElements.length - 1].focus(); } } } return FocusTrap; })); //# sourceMappingURL=focustrap.js.map