diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index ed5dc9a545b..97ca8804309 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -1003,6 +1003,30 @@ Signal Desktop makes use of the following open source projects. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +## focus-trap-react + + The MIT License (MIT) + + Copyright (c) 2015 David Clark + + 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. + ## form-data Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors diff --git a/package.json b/package.json index 6513d840f01..65b16b4a290 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "fast-glob": "3.2.1", "filesize": "3.6.1", "firstline": "1.2.1", + "focus-trap-react": "8.8.1", "form-data": "3.0.0", "fs-extra": "5.0.0", "fuse.js": "3.4.4", diff --git a/sticker-creator/elements/DropZone.tsx b/sticker-creator/elements/DropZone.tsx index c1bed32e9a0..609079fb23a 100644 --- a/sticker-creator/elements/DropZone.tsx +++ b/sticker-creator/elements/DropZone.tsx @@ -47,7 +47,10 @@ export const DropZone: React.ComponentType = props => { }, [isDragActive, onDragActive]); return ( -
+
diff --git a/ts/components/ModalHost.tsx b/ts/components/ModalHost.tsx index 5abed862a5a..f612c482f08 100644 --- a/ts/components/ModalHost.tsx +++ b/ts/components/ModalHost.tsx @@ -4,6 +4,8 @@ import React, { useEffect } from 'react'; import classNames from 'classnames'; import { createPortal } from 'react-dom'; +import FocusTrap from 'focus-trap-react'; + import { Theme, themeClassName } from '../util/theme'; import { useEscapeHandling } from '../hooks/useEscapeHandling'; @@ -56,17 +58,19 @@ export const ModalHost = React.memo( return root ? createPortal( -
- {children} -
, + +
+ {children} +
+
, root ) : null; diff --git a/ts/components/emoji/EmojiPicker.tsx b/ts/components/emoji/EmojiPicker.tsx index f5013d7ebf9..986372f281d 100644 --- a/ts/components/emoji/EmojiPicker.tsx +++ b/ts/components/emoji/EmojiPicker.tsx @@ -18,6 +18,8 @@ import { last, zipObject, } from 'lodash'; +import FocusTrap from 'focus-trap-react'; + import { Emoji } from './Emoji'; import { dataByCategory, search } from './lib'; import { LocalizerType } from '../../types/Util'; @@ -301,124 +303,126 @@ export const EmojiPicker = React.memo( ); return ( -
-
-
+ {emojiGrid.length > 0 ? ( +
+ + {({ width, height }) => ( + + )} +
) : ( - categories.map(cat => - cat === 'recents' && firstRecent.length === 0 ? null : ( +
+ {i18n('EmojiPicker--empty')} + +
+ )} +
+ {Boolean(onClickSettings) && ( + + ))} +
+ {Boolean(onClickSettings) && ( +
)} - > - {i18n('EmojiPicker--empty')} - -
- )} -
- {Boolean(onClickSettings) && ( - - ))} -
- {Boolean(onClickSettings) && ( -
- )} - -
+ +
+ ); } ) diff --git a/ts/components/stickers/StickerPicker.tsx b/ts/components/stickers/StickerPicker.tsx index 8632f44f7f4..3e8915552b1 100644 --- a/ts/components/stickers/StickerPicker.tsx +++ b/ts/components/stickers/StickerPicker.tsx @@ -3,6 +3,8 @@ import * as React from 'react'; import classNames from 'classnames'; +import FocusTrap from 'focus-trap-react'; + import { useRestoreFocus } from '../../hooks/useRestoreFocus'; import { StickerPackType, StickerType } from '../../state/ducks/stickers'; import { LocalizerType } from '../../types/Util'; @@ -146,187 +148,193 @@ export const StickerPicker = React.memo( const showLongText = showPickerHint; return ( -
-
-
-
- {hasPacks ? ( - - ))} -
- {!isUsingKeyboard && packsPage > 0 ? ( -
-
-
- {showPickerHint ? ( -
- {i18n('stickers--StickerPicker--Hint')} -
- ) : null} - {!hasPacks ? ( -
- {i18n('stickers--StickerPicker--NoPacks')} -
- ) : null} - {pendingCount > 0 ? ( -
- {i18n('stickers--StickerPicker--DownloadPending')} -
- ) : null} - {downloadError ? ( -
- {stickers.length > 0 - ? i18n('stickers--StickerPicker--DownloadError') - : i18n('stickers--StickerPicker--Empty')} -
- ) : null} - {hasPacks && showEmptyText ? ( -
- {isRecents - ? i18n('stickers--StickerPicker--NoRecents') - : i18n('stickers--StickerPicker--Empty')} -
- ) : null} - {!isEmpty ? ( -
- {stickers.map(({ packId, id, url }, index: number) => { - const maybeFocusRef = index === 0 ? focusRef : undefined; - - return ( + +
+
+
+
+ {hasPacks ? ( - ); - })} - {Array(pendingCount) - .fill(0) - .map((_, i) => ( -
+ ) : null} + {packs.map((pack, i) => ( + ))} +
+ {!isUsingKeyboard && packsPage > 0 ? ( +
- ) : null} +
+
+ {showPickerHint ? ( +
+ {i18n('stickers--StickerPicker--Hint')} +
+ ) : null} + {!hasPacks ? ( +
+ {i18n('stickers--StickerPicker--NoPacks')} +
+ ) : null} + {pendingCount > 0 ? ( +
+ {i18n('stickers--StickerPicker--DownloadPending')} +
+ ) : null} + {downloadError ? ( +
+ {stickers.length > 0 + ? i18n('stickers--StickerPicker--DownloadError') + : i18n('stickers--StickerPicker--Empty')} +
+ ) : null} + {hasPacks && showEmptyText ? ( +
+ {isRecents + ? i18n('stickers--StickerPicker--NoRecents') + : i18n('stickers--StickerPicker--Empty')} +
+ ) : null} + {!isEmpty ? ( +
+ {stickers.map(({ packId, id, url }, index: number) => { + const maybeFocusRef = index === 0 ? focusRef : undefined; + + return ( + + ); + })} + {Array(pendingCount) + .fill(0) + .map((_, i) => ( +
+ ))} +
+ ) : null} +
-
+ ); } ) diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index bb6fa9fb414..b81166d0094 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -1688,6 +1688,20 @@ "reasonCategory": "falseMatch", "updated": "2018-09-15T00:38:04.183Z" }, + { + "rule": "React-findDOMNode", + "path": "node_modules/focus-trap-react/dist/focus-trap-react.js", + "line": " var focusTrapElementDOMNodes = this.focusTrapElements.map( // NOTE: `findDOMNode()` does not support CSS selectors; it'll just return", + "reasonCategory": "exampleCode", + "updated": "2021-10-01T23:53:26.107Z" + }, + { + "rule": "React-findDOMNode", + "path": "node_modules/focus-trap-react/src/focus-trap-react.js", + "line": " // NOTE: `findDOMNode()` does not support CSS selectors; it'll just return", + "reasonCategory": "exampleCode", + "updated": "2021-10-01T23:53:26.107Z" + }, { "rule": "jQuery-load(", "path": "node_modules/foreground-child/node_modules/signal-exit/index.js", diff --git a/yarn.lock b/yarn.lock index 6da65c11c57..50a05733b89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8677,6 +8677,20 @@ focus-lock@^0.6.3: resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.6.5.tgz#f6eb37832a9b1b205406175f5277396a28c0fce1" integrity sha512-i/mVBOoa9o+tl+u9owOJUF8k8L85odZNIsctB+JAK2HFT8jckiBwmk+3uydlm6FN8czgnkIwQtBv6yyAbrzXjw== +focus-trap-react@8.8.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/focus-trap-react/-/focus-trap-react-8.8.1.tgz#2c7043e748460a191a1c7e2552e7c964f3f9fd46" + integrity sha512-Uy7U/l3fozlwLYBSQHP91QjuRUUcAQ9FJ3glAGmwF/fXSiPa/4negTy02zWElLZdZDPIfebC8V14aI1Gzc5V3w== + dependencies: + focus-trap "^6.7.1" + +focus-trap@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-6.7.1.tgz#d474f86dbaf3c7fbf0d53cf0b12295f4f4068d10" + integrity sha512-a6czHbT9twVpy2RpkWQA9vIgwQgB9Nx1PIxNNUxQT4nugG/3QibwxO+tWTh9i+zSY2SFiX4pnYhTaFaQF/6ZAg== + dependencies: + tabbable "^5.2.1" + follow-redirects@^1.0.0: version "1.13.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" @@ -17445,6 +17459,11 @@ symbol.prototype.description@^1.0.0: dependencies: has-symbols "^1.0.0" +tabbable@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-5.2.1.tgz#e3fda7367ddbb172dcda9f871c0fdb36d1c4cd9c" + integrity sha512-40pEZ2mhjaZzK0BnI+QGNjJO8UYx9pP5v7BGe17SORTO0OEuuaAwQTkAp8whcZvqon44wKFOikD+Al11K3JICQ== + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"