Fix FunLightbox and FunTooltip

This commit is contained in:
Jamie Kyle 2025-06-03 06:29:51 -07:00 committed by GitHub
commit 06ff9fa09e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 891 additions and 211 deletions

View file

@ -197,6 +197,30 @@ Signal Desktop makes use of the following open source projects.
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.
## @radix-ui/react-tooltip
MIT License
Copyright (c) 2022 WorkOS
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.
## @react-aria/focus
Apache License

View file

@ -114,6 +114,7 @@
"@indutny/simple-windows-notifications": "2.0.16",
"@indutny/sneequals": "4.0.0",
"@popperjs/core": "2.11.8",
"@radix-ui/react-tooltip": "1.2.7",
"@react-aria/focus": "3.19.1",
"@react-aria/interactions": "3.23.0",
"@react-aria/utils": "3.25.3",

420
pnpm-lock.yaml generated
View file

@ -110,6 +110,9 @@ importers:
'@popperjs/core':
specifier: 2.11.8
version: 2.11.8
'@radix-ui/react-tooltip':
specifier: 1.2.7
version: 1.2.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@react-aria/focus':
specifier: 3.19.1
version: 3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -1333,6 +1336,21 @@ packages:
resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
'@floating-ui/core@1.7.0':
resolution: {integrity: sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==}
'@floating-ui/dom@1.7.0':
resolution: {integrity: sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==}
'@floating-ui/react-dom@2.1.2':
resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@floating-ui/utils@0.2.9':
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
'@formatjs/ecma402-abstract@1.11.4':
resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==}
@ -1918,6 +1936,215 @@ packages:
'@protobufjs/utf8@1.1.0':
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
'@radix-ui/primitive@1.1.2':
resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==}
'@radix-ui/react-arrow@1.1.7':
resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-compose-refs@1.1.2':
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-context@1.1.2':
resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-dismissable-layer@1.1.10':
resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-id@1.1.1':
resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-popper@1.2.7':
resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-portal@1.1.9':
resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-presence@1.1.4':
resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-primitive@2.1.3':
resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-slot@1.2.3':
resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-tooltip@1.2.7':
resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-use-callback-ref@1.1.1':
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-controllable-state@1.2.2':
resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-effect-event@0.0.2':
resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-escape-keydown@1.1.1':
resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-layout-effect@1.1.1':
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-rect@1.1.1':
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-size@1.1.1':
resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-visually-hidden@1.2.3':
resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
'@react-aria/accordion@3.0.0-alpha.35':
resolution: {integrity: sha512-eZcsHJDVDNIZ2XUmJynHScRv1YAF/+fj5T0zoGdyEPImIIxJLROupQ75uwarAI5btGSR2TFeqYRmRXJrVuxgoA==}
peerDependencies:
@ -10171,6 +10398,23 @@ snapshots:
'@eslint/js@8.56.0': {}
'@floating-ui/core@1.7.0':
dependencies:
'@floating-ui/utils': 0.2.9
'@floating-ui/dom@1.7.0':
dependencies:
'@floating-ui/core': 1.7.0
'@floating-ui/utils': 0.2.9
'@floating-ui/react-dom@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/dom': 1.7.0
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@floating-ui/utils@0.2.9': {}
'@formatjs/ecma402-abstract@1.11.4':
dependencies:
'@formatjs/intl-localematcher': 0.2.25
@ -10946,6 +11190,182 @@ snapshots:
'@protobufjs/utf8@1.1.0': {}
'@radix-ui/primitive@1.1.2': {}
'@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.20)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-context@1.1.2(@types/react@18.3.20)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.2
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/react-id@1.1.1(@types/react@18.3.20)(react@18.3.1)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-popper@1.2.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-use-size': 1.1.1(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/rect': 1.1.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/react-presence@1.1.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-slot': 1.2.3(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/react-slot@1.2.3(@types/react@18.3.20)(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-tooltip@1.2.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.2
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-context': 1.1.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-id': 1.1.1(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-slot': 1.2.3(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.20)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.20)(react@18.3.1)':
dependencies:
'@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.20)(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.20)(react@18.3.1)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.20)(react@18.3.1)':
dependencies:
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.20)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-use-rect@1.1.1(@types/react@18.3.20)(react@18.3.1)':
dependencies:
'@radix-ui/rect': 1.1.1
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-use-size@1.1.1(@types/react@18.3.20)(react@18.3.1)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.20)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.20
'@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.20
'@types/react-dom': 18.3.6(@types/react@18.3.20)
'@radix-ui/rect@1.1.1': {}
'@react-aria/accordion@3.0.0-alpha.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@react-aria/button': 3.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)

View file

@ -136,6 +136,7 @@ $image-radius: $button-radius - $image-margin;
}
.FunSubNav__ListBoxItem {
position: relative;
flex: 1;
padding: 1px;
cursor: pointer;
@ -154,6 +155,13 @@ $image-radius: $button-radius - $image-margin;
}
}
.FunSubNav__ListBoxItem__TooltipTarget {
display: block;
position: absolute;
inset: 0;
z-index: 2;
}
.FunSubNav__ListBoxItem__Button {
position: relative;
display: flex;

View file

@ -6,6 +6,8 @@
@use './FunConstants.scss';
.FunTooltip {
position: relative;
z-index: 100000;
max-width: calc(32ch + (8px * 2));
padding-block: 4px;
padding-inline: 8px;

View file

@ -1,12 +1,13 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ForwardedRef, ReactNode } from 'react';
import React, { forwardRef, useEffect, useRef } from 'react';
import { type PressEvent, useLongPress } from 'react-aria';
import type { LongPressEvent } from '@react-types/shared';
import { Button } from 'react-aria-components';
import { mergeRefs } from '@react-aria/utils';
import { PressResponder } from '@react-aria/interactions';
import type {
ForwardedRef,
ReactNode,
DOMAttributes,
PointerEvent,
} from 'react';
import React, { forwardRef, useCallback, useEffect, useMemo } from 'react';
import { mergeProps } from '@react-aria/utils';
import { strictAssert } from '../../../util/assert';
/**
@ -28,49 +29,120 @@ export type FunItemButtonProps = Readonly<
{
'aria-label': string;
excludeFromTabOrder: boolean;
onPress: (event: PressEvent) => void;
onContextMenu?: (event: MouseEvent) => void;
onClick: (event: PointerEvent) => void;
onContextMenu?: (event: PointerEvent) => void;
children: ReactNode;
} & FunItemButtonLongPressProps
>;
export const FunItemButton = forwardRef(function FunItemButton(
props: FunItemButtonProps,
outerRef: ForwardedRef<HTMLButtonElement>
ref: ForwardedRef<HTMLButtonElement>
): JSX.Element {
const { onContextMenu } = props;
const innerRef = useRef<HTMLButtonElement>(null);
const {
'aria-label': ariaLabel,
excludeFromTabOrder,
onClick,
onContextMenu,
children,
longPressAccessibilityDescription,
onLongPress,
...rest
} = props;
const { longPressProps } = useLongPress({
isDisabled: props.onLongPress == null,
accessibilityDescription: props.longPressAccessibilityDescription,
onLongPress: props.onLongPress,
});
const longPressProps = useLongPress(onLongPress ?? null);
useEffect(() => {
strictAssert(innerRef.current, 'Missing ref element');
const element = innerRef.current;
if (onContextMenu == null) {
return () => null;
}
element.addEventListener('contextmenu', onContextMenu);
return () => {
element.removeEventListener('contextmenu', onContextMenu);
};
}, [onContextMenu]);
const handleClick = useCallback(
(event: PointerEvent) => {
if (!event.defaultPrevented) {
onClick(event);
}
},
[onClick]
);
return (
<PressResponder {...longPressProps}>
<Button
ref={mergeRefs(innerRef, outerRef)}
type="button"
className="FunItem__Button"
aria-label={props['aria-label']}
excludeFromTabOrder={props.excludeFromTabOrder}
onPress={props.onPress}
>
{props.children}
</Button>
</PressResponder>
// eslint-disable-next-line jsx-a11y/role-supports-aria-props
<button
ref={ref}
type="button"
className="FunItem__Button"
aria-label={ariaLabel}
aria-description={longPressAccessibilityDescription}
tabIndex={excludeFromTabOrder ? -1 : undefined}
{...mergeProps(
longPressProps,
{
onClick: handleClick,
onContextMenu,
},
rest
)}
>
{children}
</button>
);
});
type LongPressEvent = Readonly<{
pointerType: PointerEvent['pointerType'];
}>;
function useLongPress(
onLongPress: ((event: LongPressEvent) => void) | null
): DOMAttributes<Element> {
const { cleanup, props } = useMemo(() => {
if (onLongPress == null) {
return { props: {} };
}
let timer: ReturnType<typeof setTimeout>;
let isLongPressed = false;
let lastLongPress: number | null = null;
function reset() {
clearTimeout(timer);
isLongPressed = false;
}
function handleCancel(event: PointerEvent) {
if (isLongPressed) {
lastLongPress = event.timeStamp;
}
reset();
}
function handleStart(event: PointerEvent) {
const press: LongPressEvent = { pointerType: event.pointerType };
reset();
timer = setTimeout(() => {
isLongPressed = true;
strictAssert(onLongPress != null, 'Missing callback');
onLongPress(press);
}, 500);
}
function handleClick(event: PointerEvent) {
if (event.timeStamp === lastLongPress) {
event.preventDefault();
}
}
return {
cleanup: reset,
props: {
onPointerDown: handleStart,
onPointerUp: handleCancel,
onPointerCancel: handleCancel,
onPointerLeave: handleCancel,
onClick: handleClick,
} satisfies DOMAttributes<Element>,
};
}, [onLongPress]);
useEffect(() => {
return cleanup;
}, [cleanup]);
return props;
}

View file

@ -5,6 +5,7 @@ import React, { useCallback } from 'react';
import type { Placement } from 'react-aria';
import { Dialog, Popover } from 'react-aria-components';
import classNames from 'classnames';
import * as Tooltip from '@radix-ui/react-tooltip';
import { ThemeType } from '../../../types/Util';
export type FunPopoverProps = Readonly<{
@ -16,8 +17,14 @@ export type FunPopoverProps = Readonly<{
export function FunPopover(props: FunPopoverProps): JSX.Element {
const shouldCloseOnInteractOutside = useCallback(
(element: Element): boolean => {
// Don't close when quill steals focus
const match = element.closest('.module-composition-input__input');
const match = element.closest(
[
// Don't close when quill steals focus
'.module-composition-input__input',
// Don't close when clicking tooltip
'.FunTooltip',
].join(', ')
);
if (match != null) {
return false;
}
@ -27,16 +34,18 @@ export function FunPopover(props: FunPopoverProps): JSX.Element {
);
return (
<Popover
data-fun-overlay
className={classNames('FunPopover', {
'light-theme': props.theme === ThemeType.light,
'dark-theme': props.theme === ThemeType.dark,
})}
placement={props.placement}
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
>
<Dialog className="FunPopover__Dialog">{props.children}</Dialog>
</Popover>
<Tooltip.Provider>
<Popover
data-fun-overlay
className={classNames('FunPopover', {
'light-theme': props.theme === ThemeType.light,
'dark-theme': props.theme === ThemeType.dark,
})}
placement={props.placement}
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
>
<Dialog className="FunPopover__Dialog">{props.children}</Dialog>
</Popover>
</Tooltip.Provider>
);
}

View file

@ -3,7 +3,7 @@
import classNames from 'classnames';
import type { Transition } from 'framer-motion';
import { motion } from 'framer-motion';
import type { ReactNode } from 'react';
import type { ReactNode, Ref } from 'react';
import React, {
createContext,
useCallback,
@ -13,6 +13,7 @@ import React, {
useRef,
useState,
useId,
forwardRef,
} from 'react';
import type { Selection } from 'react-aria-components';
import { ListBox, ListBoxItem } from 'react-aria-components';
@ -25,6 +26,7 @@ import * as log from '../../../logging/log';
import * as Errors from '../../../types/errors';
import { strictAssert } from '../../../util/assert';
import { FunImage } from './FunImage';
import { FunTooltip } from './FunTooltip';
/**
* Sub Nav
@ -246,11 +248,30 @@ function FunSubNavListBoxItemButton(props: {
);
}
const FunSubNavListBoxItemTooltipTarget = forwardRef(
function FunSubNavListBoxItemTooltipTarget(props, ref: Ref<HTMLSpanElement>) {
return (
<span
ref={ref}
{...props}
className="FunSubNav__ListBoxItem__TooltipTarget"
/>
);
}
);
export function FunSubNavListBoxItem(
props: FunSubNavListBoxItemProps
): JSX.Element {
const context = useContext(FunSubNavListBoxContext);
strictAssert(context, 'Must be wrapped with <FunSubNavListBox>');
const [tooltipOpen, setTooltipOpen] = useState(false);
const handleTooltipOpenChange = useCallback((open: boolean) => {
setTooltipOpen(open);
}, []);
return (
<ListBoxItem
id={props.id}
@ -260,22 +281,35 @@ export function FunSubNavListBoxItem(
>
{({ isSelected, isFocusVisible }) => {
return (
<FunSubNavListBoxItemButton isSelected={isSelected}>
<span className="FunSubNav__ListBoxItem__ButtonIcon">
{props.children}
</span>
{isSelected && (
<motion.div
className="FunSubNav__ListBoxItem__ButtonIndicator"
layoutId={`FunSubNav__ListBoxItem__ButtonIndicator--${context.id}`}
layoutDependency={context.selected}
transition={FunSubNavListBoxItemTransition}
/>
)}
{!isSelected && isFocusVisible && (
<div className="FunSubNav__ListBoxItem__ButtonIndicator" />
)}
</FunSubNavListBoxItemButton>
<>
<FunTooltip
open={tooltipOpen || (isSelected && isFocusVisible)}
onOpenChange={handleTooltipOpenChange}
side="top"
content={props.label}
collisionBoundarySelector=".FunPanel"
collisionPadding={6}
disableHoverableContent
>
<FunSubNavListBoxItemTooltipTarget />
</FunTooltip>
<FunSubNavListBoxItemButton isSelected={isSelected}>
<span className="FunSubNav__ListBoxItem__ButtonIcon">
{props.children}
</span>
{isSelected && (
<motion.div
className="FunSubNav__ListBoxItem__ButtonIndicator"
layoutId={`FunSubNav__ListBoxItem__ButtonIndicator--${context.id}`}
layoutDependency={context.selected}
transition={FunSubNavListBoxItemTransition}
/>
)}
{!isSelected && isFocusVisible && (
<div className="FunSubNav__ListBoxItem__ButtonIndicator" />
)}
</FunSubNavListBoxItemButton>
</>
);
}}
</ListBoxItem>

View file

@ -1,19 +1,57 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { type ReactNode } from 'react';
import type { Placement } from 'react-aria';
import { Tooltip } from 'react-aria-components';
import React, { useRef, useState, type ReactNode } from 'react';
import * as Tooltip from '@radix-ui/react-tooltip';
import { useLayoutEffect } from '@react-aria/utils';
import { strictAssert } from '../../../util/assert';
export type FunTooltipProps = Readonly<{
placement?: Placement;
open?: boolean;
onOpenChange?: (open: boolean) => void;
disableHoverableContent?: boolean;
side?: Tooltip.TooltipContentProps['side'];
align?: Tooltip.TooltipContentProps['align'];
collisionBoundarySelector?: string;
collisionPadding?: number;
content: ReactNode;
children: ReactNode;
}>;
export function FunTooltip(props: FunTooltipProps): JSX.Element {
const ref = useRef<HTMLButtonElement>(null);
const [collisionBoundary, setCollisionBoundary] = useState<Element | null>(
null
);
useLayoutEffect(() => {
if (props.collisionBoundarySelector == null) {
return;
}
strictAssert(ref.current, 'missing ref');
const trigger = ref.current;
setCollisionBoundary(trigger.closest(props.collisionBoundarySelector));
}, [props.collisionBoundarySelector]);
return (
<Tooltip className="FunTooltip" placement={props.placement}>
{props.children}
</Tooltip>
<Tooltip.Root
open={props.open}
onOpenChange={props.onOpenChange}
disableHoverableContent={props.disableHoverableContent}
>
<Tooltip.Trigger ref={ref} asChild>
{props.children}
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content
side={props.side}
align={props.align}
className="FunTooltip"
collisionBoundary={collisionBoundary}
collisionPadding={props.collisionPadding}
>
<span className="FunTooltip__Text">{props.content}</span>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
);
}

View file

@ -1,16 +1,16 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
import type { MouseEvent, PointerEvent } from 'react';
import {
Dialog,
DialogTrigger,
Heading,
OverlayArrow,
Popover,
TooltipTrigger,
} from 'react-aria-components';
import type { PressEvent } from 'react-aria';
import { VisuallyHidden } from 'react-aria';
import * as Tooltip from '@radix-ui/react-tooltip';
import type { LocalizerType } from '../../../types/I18N';
import { strictAssert } from '../../../util/assert';
import { missingCaseError } from '../../../util/missingCaseError';
@ -388,95 +388,99 @@ export function FunPanelEmojis({
</FunPanelFooter>
)}
<FunPanelBody>
<FunScroller
ref={scrollerRef}
sectionGap={EMOJI_GRID_SECTION_GAP}
onScrollSectionChange={handleScrollSectionChange}
>
{layout.sections.length === 0 && (
<FunResults aria-busy={false}>
<FunResultsHeader>
{i18n('icu:FunPanelEmojis__SearchResults__EmptyHeading')}{' '}
<FunStaticEmoji
size={16}
role="presentation"
emoji={emojiVariantConstant('\u{1F641}')}
/>
</FunResultsHeader>
</FunResults>
)}
{layout.sections.length > 0 && (
<FunKeyboard
scrollerRef={scrollerRef}
keyboard={keyboard}
onStateChange={handleKeyboardStateChange}
>
<FunGridContainer
totalSize={layout.totalHeight}
columnCount={EMOJI_GRID_COLUMNS}
cellWidth={EMOJI_GRID_CELL_WIDTH}
cellHeight={EMOJI_GRID_CELL_HEIGHT}
<Tooltip.Provider skipDelayDuration={0}>
<FunScroller
ref={scrollerRef}
sectionGap={EMOJI_GRID_SECTION_GAP}
onScrollSectionChange={handleScrollSectionChange}
>
{layout.sections.length === 0 && (
<FunResults aria-busy={false}>
<FunResultsHeader>
{i18n('icu:FunPanelEmojis__SearchResults__EmptyHeading')}{' '}
<FunStaticEmoji
size={16}
role="presentation"
emoji={emojiVariantConstant('\u{1F641}')}
/>
</FunResultsHeader>
</FunResults>
)}
{layout.sections.length > 0 && (
<FunKeyboard
scrollerRef={scrollerRef}
keyboard={keyboard}
onStateChange={handleKeyboardStateChange}
>
{layout.sections.map(section => {
return (
<FunGridScrollerSection
key={section.key}
id={section.id}
sectionOffset={section.sectionOffset}
sectionSize={section.sectionSize}
>
<FunGridHeader
id={section.header.key}
headerOffset={section.header.headerOffset}
headerSize={section.header.headerSize}
<FunGridContainer
totalSize={layout.totalHeight}
columnCount={EMOJI_GRID_COLUMNS}
cellWidth={EMOJI_GRID_CELL_WIDTH}
cellHeight={EMOJI_GRID_CELL_HEIGHT}
>
{layout.sections.map(section => {
return (
<FunGridScrollerSection
key={section.key}
id={section.id}
sectionOffset={section.sectionOffset}
sectionSize={section.sectionSize}
>
<FunGridHeaderText>
{getTitleForSection(
i18n,
section.id as FunEmojisSection
)}
</FunGridHeaderText>
{section.id ===
EmojiPickerCategory.SmileysAndPeople && (
<SectionSkinToneHeaderPopover
i18n={i18n}
open={skinTonePopoverOpen}
onOpenChange={handleSkinTonePopoverOpenChange}
onSelectSkinTone={fun.onEmojiSkinToneDefaultChange}
/>
)}
</FunGridHeader>
<FunGridRowGroup
aria-labelledby={section.header.key}
colCount={section.colCount}
rowCount={section.rowCount}
rowGroupOffset={section.rowGroup.rowGroupOffset}
rowGroupSize={section.rowGroup.rowGroupSize}
>
{section.rowGroup.rows.map(row => {
return (
<Row
key={row.key}
<FunGridHeader
id={section.header.key}
headerOffset={section.header.headerOffset}
headerSize={section.header.headerSize}
>
<FunGridHeaderText>
{getTitleForSection(
i18n,
section.id as FunEmojisSection
)}
</FunGridHeaderText>
{section.id ===
EmojiPickerCategory.SmileysAndPeople && (
<SectionSkinToneHeaderPopover
i18n={i18n}
rowIndex={row.rowIndex}
cells={row.cells}
focusedCellKey={focusedCellKey}
emojiSkinToneDefault={fun.emojiSkinToneDefault}
onSelectEmoji={handleSelectEmoji}
onEmojiSkinToneDefaultChange={
open={skinTonePopoverOpen}
onOpenChange={handleSkinTonePopoverOpenChange}
onSelectSkinTone={
fun.onEmojiSkinToneDefaultChange
}
/>
);
})}
</FunGridRowGroup>
</FunGridScrollerSection>
);
})}
</FunGridContainer>
</FunKeyboard>
)}
</FunScroller>
)}
</FunGridHeader>
<FunGridRowGroup
aria-labelledby={section.header.key}
colCount={section.colCount}
rowCount={section.rowCount}
rowGroupOffset={section.rowGroup.rowGroupOffset}
rowGroupSize={section.rowGroup.rowGroupSize}
>
{section.rowGroup.rows.map(row => {
return (
<Row
key={row.key}
i18n={i18n}
rowIndex={row.rowIndex}
cells={row.cells}
focusedCellKey={focusedCellKey}
emojiSkinToneDefault={fun.emojiSkinToneDefault}
onSelectEmoji={handleSelectEmoji}
onEmojiSkinToneDefaultChange={
fun.onEmojiSkinToneDefaultChange
}
/>
);
})}
</FunGridRowGroup>
</FunGridScrollerSection>
);
})}
</FunGridContainer>
</FunKeyboard>
)}
</FunScroller>
</Tooltip.Provider>
</FunPanelBody>
</FunPanel>
);
@ -572,8 +576,8 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
return getEmojiVariantByParentKeyAndSkinTone(emojiParent.key, skinTone);
}, [emojiParent, skinTone]);
const handlePress = useCallback(
(event: PressEvent) => {
const handleClick = useCallback(
(event: PointerEvent) => {
if (emojiHasSkinToneVariants && emojiSkinToneDefault == null) {
setPopoverOpen(true);
return;
@ -585,7 +589,7 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
skinTone,
};
const shouldClose =
(event.pointerType === 'keyboard' || event.pointerType === 'virtual') &&
event.nativeEvent.pointerType !== 'mouse' &&
!(event.ctrlKey || event.metaKey);
onSelectEmoji(emojiSelection, shouldClose);
},
@ -654,12 +658,20 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
colIndex={props.colIndex}
rowIndex={props.rowIndex}
>
<TooltipTrigger>
<FunTooltip
side="top"
content={`:${emojiShortNameDisplay}:`}
collisionBoundarySelector=".FunScroller__Viewport"
collisionPadding={6}
// `skipDelayDuration=0` doesn't work with `disableHoverableContent`
// FIX: https://github.com/radix-ui/primitives/pull/3562
// disableHoverableContent
>
<FunItemButton
ref={popoverTriggerRef}
excludeFromTabOrder={!props.isTabbable}
aria-label={emojiName}
onPress={handlePress}
onClick={handleClick}
onLongPress={handleLongPress}
onContextMenu={handleContextMenu}
longPressAccessibilityDescription={i18n(
@ -668,9 +680,7 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
>
<FunStaticEmoji role="presentation" size={32} emoji={emojiVariant} />
</FunItemButton>
<FunTooltip placement="top">{`:${emojiShortNameDisplay}:`}</FunTooltip>
</TooltipTrigger>
d
</FunTooltip>
{emojiHasSkinToneVariants && (
<Popover
data-fun-overlay

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import type { Range } from '@tanstack/react-virtual';
import { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual';
import type { PointerEvent } from 'react';
import React, {
memo,
useCallback,
@ -11,7 +12,6 @@ import React, {
useState,
useId,
} from 'react';
import type { PressEvent } from 'react-aria';
import { VisuallyHidden } from 'react-aria';
import { LRUCache } from 'lru-cache';
import { FunItemButton } from '../base/FunItem';
@ -356,8 +356,8 @@ export function FunPanelGifs({
[]
);
const handlePressGif = useCallback(
(_event: PressEvent, gifSelection: FunGifSelection) => {
const handleClickGif = useCallback(
(_event: PointerEvent, gifSelection: FunGifSelection) => {
onFunSelectGif(gifSelection);
onSelectGif(gifSelection);
setSelectedItemKey(null);
@ -520,7 +520,7 @@ export function FunPanelGifs({
itemOffset={item.start}
itemLane={item.lane}
isTabbable={isTabbable}
onPressGif={handlePressGif}
onClickGif={handleClickGif}
fetchGif={fetchGif}
/>
);
@ -542,16 +542,16 @@ const Item = memo(function Item(props: {
itemOffset: number;
itemLane: number;
isTabbable: boolean;
onPressGif: (event: PressEvent, gifSelection: FunGifSelection) => void;
onClickGif: (event: PointerEvent, gifSelection: FunGifSelection) => void;
fetchGif: typeof tenorDownload;
}) {
const { onPressGif, fetchGif } = props;
const { onClickGif, fetchGif } = props;
const handlePress = useCallback(
async (event: PressEvent) => {
onPressGif(event, { gif: props.gif });
const handleClick = useCallback(
async (event: PointerEvent) => {
onClickGif(event, { gif: props.gif });
},
[props.gif, onPressGif]
[props.gif, onClickGif]
);
const descriptionId = `FunGifsPanelItem__GifDescription--${props.gif.id}`;
@ -606,7 +606,7 @@ const Item = memo(function Item(props: {
>
<FunItemButton
aria-label={props.gif.title}
onPress={handlePress}
onClick={handleClick}
excludeFromTabOrder={!props.isTabbable}
>
{src != null && (

View file

@ -1,6 +1,6 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { CSSProperties } from 'react';
import type { CSSProperties, PointerEvent } from 'react';
import React, {
memo,
useCallback,
@ -9,7 +9,6 @@ import React, {
useRef,
useState,
} from 'react';
import type { PressEvent } from 'react-aria';
import type {
StickerPackType,
StickerType,
@ -344,8 +343,8 @@ export function FunPanelStickers({
return searchInput.length > 0;
}, [searchInput]);
const handlePressSticker = useCallback(
(event: PressEvent, stickerSelection: FunStickerSelection) => {
const handleClickSticker = useCallback(
(event: PointerEvent, stickerSelection: FunStickerSelection) => {
onFunSelectSticker(stickerSelection);
onSelectSticker(stickerSelection);
if (!(event.ctrlKey || event.metaKey)) {
@ -356,8 +355,8 @@ export function FunPanelStickers({
[onFunSelectSticker, onSelectSticker, onClose]
);
const handlePressTimeSticker = useCallback(
(event: PressEvent, style: FunTimeStickerStyle) => {
const handleClickTimeSticker = useCallback(
(event: PointerEvent, style: FunTimeStickerStyle) => {
onSelectTimeSticker?.(style);
if (!(event.ctrlKey || event.metaKey)) {
onClose();
@ -492,8 +491,8 @@ export function FunPanelStickers({
cells={row.cells}
stickerLookup={stickerLookup}
focusedCellKey={focusedCellKey}
onPressSticker={handlePressSticker}
onPressTimeSticker={handlePressTimeSticker}
onClickSticker={handleClickSticker}
onClickTimeSticker={handleClickTimeSticker}
/>
);
})}
@ -515,11 +514,11 @@ const Row = memo(function Row(props: {
stickerLookup: StickerLookup;
cells: ReadonlyArray<CellLayoutNode>;
focusedCellKey: CellKey | null;
onPressSticker: (
event: PressEvent,
onClickSticker: (
event: PointerEvent,
stickerSelection: FunStickerSelection
) => void;
onPressTimeSticker: (event: PressEvent, style: FunTimeStickerStyle) => void;
onClickTimeSticker: (event: PointerEvent, style: FunTimeStickerStyle) => void;
}): JSX.Element {
return (
<FunGridRow rowIndex={props.rowIndex}>
@ -537,8 +536,8 @@ const Row = memo(function Row(props: {
colIndex={cell.colIndex}
stickerLookup={props.stickerLookup}
isTabbable={isTabbable}
onPressSticker={props.onPressSticker}
onPressTimeSticker={props.onPressTimeSticker}
onClickSticker={props.onClickSticker}
onClickTimeSticker={props.onClickTimeSticker}
/>
);
})}
@ -553,28 +552,28 @@ const Cell = memo(function Cell(props: {
rowIndex: number;
stickerLookup: StickerLookup;
isTabbable: boolean;
onPressSticker: (
event: PressEvent,
onClickSticker: (
event: PointerEvent,
stickerSelection: FunStickerSelection
) => void;
onPressTimeSticker: (event: PressEvent, style: FunTimeStickerStyle) => void;
onClickTimeSticker: (event: PointerEvent, style: FunTimeStickerStyle) => void;
}): JSX.Element {
const { onPressSticker, onPressTimeSticker } = props;
const { onClickSticker, onClickTimeSticker } = props;
const stickerLookupItem = props.stickerLookup[props.value];
const handlePress = useCallback(
(event: PressEvent) => {
const handleClick = useCallback(
(event: PointerEvent) => {
if (stickerLookupItem.kind === 'sticker') {
onPressSticker(event, {
onClickSticker(event, {
stickerPackId: stickerLookupItem.sticker.packId,
stickerId: stickerLookupItem.sticker.id,
stickerUrl: stickerLookupItem.sticker.url,
});
} else if (stickerLookupItem.kind === 'timeSticker') {
onPressTimeSticker(event, stickerLookupItem.style);
onClickTimeSticker(event, stickerLookupItem.style);
}
},
[stickerLookupItem, onPressSticker, onPressTimeSticker]
[stickerLookupItem, onClickSticker, onClickTimeSticker]
);
return (
@ -590,7 +589,7 @@ const Cell = memo(function Cell(props: {
? (stickerLookupItem.sticker.emoji ?? '')
: stickerLookupItem.style
}
onPress={handlePress}
onClick={handleClick}
>
{stickerLookupItem.kind === 'sticker' && (
<FunSticker

View file

@ -15,6 +15,69 @@
"updated": "2018-09-18T19:19:27.699Z",
"reasonDetail": "Part of runtime library for C++ transpiled code"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const isOpenDelayedRef = React.useRef(true);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const isPointerInTransitRef = React.useRef(false);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const skipDelayTimerRef = React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const openTimerRef = React.useRef(0);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const wasOpenDelayedRef = React.useRef(false);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const ref = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const isPointerDownRef = React.useRef(false);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const hasPointerMoveOpenedRef = React.useRef(false);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "node_modules/@radix-ui/react-tooltip/dist/index.js",
"line": " const ref = React.useRef(null);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "DOM-innerHTML",
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
@ -1842,13 +1905,6 @@
"reasonCategory": "usageTrusted",
"updated": "2025-02-19T20:14:46.879Z"
},
{
"rule": "React-useRef",
"path": "ts/components/fun/base/FunItem.tsx",
"line": " const innerRef = useRef<HTMLButtonElement>(null);",
"reasonCategory": "usageTrusted",
"updated": "2025-04-23T23:43:10.675Z"
},
{
"rule": "React-useRef",
"path": "ts/components/fun/base/FunScroller.tsx",
@ -1905,6 +1961,13 @@
"reasonCategory": "usageTrusted",
"updated": "2025-02-19T20:14:46.879Z"
},
{
"rule": "React-useRef",
"path": "ts/components/fun/base/FunTooltip.tsx",
"line": " const ref = useRef<HTMLButtonElement>(null);",
"reasonCategory": "usageTrusted",
"updated": "2025-05-30T20:26:57.154Z"
},
{
"rule": "React-useRef",
"path": "ts/components/fun/data/infinite.ts",