diff --git a/js/package-lock.json b/js/package-lock.json index efe81d05ed..ead654e30e 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -13,6 +13,7 @@ "color-thief-browser": "^2.0.2", "dayjs": "^1.10.4", "expose-loader": "^1.0.3", + "focus-trap": "^6.6.0", "jquery": "^3.6.0", "jquery.hotkeys": "^0.1.0", "mithril": "^2.0.4", @@ -3612,6 +3613,14 @@ "readable-stream": "^2.3.6" } }, + "node_modules/focus-trap": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.6.0.tgz", + "integrity": "sha512-2hWVR3XbBejn5v8wDW9DFzLWXcxMNaSJ/CtE3E+FJjjBCLwIYbZJwjUi2RDBfQPM58gHEt5hck0jrJgHR9/s+A==", + "dependencies": { + "tabbable": "^5.2.0" + } + }, "node_modules/follow-redirects": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz", @@ -6169,6 +6178,11 @@ "node": ">=4" } }, + "node_modules/tabbable": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.2.0.tgz", + "integrity": "sha512-0uyt8wbP0P3T4rrsfYg/5Rg3cIJ8Shl1RJ54QMqYxm1TLdWqJD1u6+RQjr2Lor3wmfT7JRHkirIwy99ydBsyPg==" + }, "node_modules/tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -10299,6 +10313,14 @@ "readable-stream": "^2.3.6" } }, + "focus-trap": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-6.6.0.tgz", + "integrity": "sha512-2hWVR3XbBejn5v8wDW9DFzLWXcxMNaSJ/CtE3E+FJjjBCLwIYbZJwjUi2RDBfQPM58gHEt5hck0jrJgHR9/s+A==", + "requires": { + "tabbable": "^5.2.0" + } + }, "follow-redirects": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz", @@ -12334,6 +12356,11 @@ "has-flag": "^3.0.0" } }, + "tabbable": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.2.0.tgz", + "integrity": "sha512-0uyt8wbP0P3T4rrsfYg/5Rg3cIJ8Shl1RJ54QMqYxm1TLdWqJD1u6+RQjr2Lor3wmfT7JRHkirIwy99ydBsyPg==" + }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", diff --git a/js/package.json b/js/package.json index a43d17131e..fa49486000 100644 --- a/js/package.json +++ b/js/package.json @@ -10,6 +10,7 @@ "color-thief-browser": "^2.0.2", "dayjs": "^1.10.4", "expose-loader": "^1.0.3", + "focus-trap": "^6.6.0", "jquery": "^3.6.0", "jquery.hotkeys": "^0.1.0", "mithril": "^2.0.4", diff --git a/js/src/common/compat.js b/js/src/common/compat.js index cc615ae38b..23667f52c8 100644 --- a/js/src/common/compat.js +++ b/js/src/common/compat.js @@ -31,6 +31,7 @@ import extractText from './utils/extractText'; import formatNumber from './utils/formatNumber'; import mapRoutes from './utils/mapRoutes'; import withAttr from './utils/withAttr'; +import * as FocusTrap from './utils/focusTrap'; import Notification from './models/Notification'; import User from './models/User'; import Post from './models/Post'; @@ -114,6 +115,7 @@ export default { 'utils/mapRoutes': mapRoutes, 'utils/withAttr': withAttr, 'utils/throttleDebounce': ThrottleDebounce, + 'utils/focusTrap': FocusTrap, 'models/Notification': Notification, 'models/User': User, 'models/Post': Post, diff --git a/js/src/common/utils/focusTrap.ts b/js/src/common/utils/focusTrap.ts new file mode 100644 index 0000000000..3724aea9a1 --- /dev/null +++ b/js/src/common/utils/focusTrap.ts @@ -0,0 +1,29 @@ +import { createFocusTrap as _createFocusTrap } from 'focus-trap'; + +/** + * Creates a focus trap for the given element with the given options. + * + * This function applies some default options that are different to the library. + * Your own options still override these custom defaults: + * + * ```json + * { + escapeDeactivates: false, + * } + * ``` + * + * @param element The element to be the focus trap, or a selector that will be used to find the element. + * + * @see https://github.com/focus-trap/focus-trap#readme - Library documentation + */ +function createFocusTrap(...args: Parameters): ReturnType { + args[1] = { + escapeDeactivates: false, + ...args[1], + }; + + return _createFocusTrap(...args); +} + +export * from 'focus-trap'; +export { createFocusTrap };