Fix FunLightbox and FunTooltip
This commit is contained in:
parent
0a91232634
commit
06ff9fa09e
13 changed files with 891 additions and 211 deletions
|
@ -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
|
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.
|
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
|
## @react-aria/focus
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
|
|
|
@ -114,6 +114,7 @@
|
||||||
"@indutny/simple-windows-notifications": "2.0.16",
|
"@indutny/simple-windows-notifications": "2.0.16",
|
||||||
"@indutny/sneequals": "4.0.0",
|
"@indutny/sneequals": "4.0.0",
|
||||||
"@popperjs/core": "2.11.8",
|
"@popperjs/core": "2.11.8",
|
||||||
|
"@radix-ui/react-tooltip": "1.2.7",
|
||||||
"@react-aria/focus": "3.19.1",
|
"@react-aria/focus": "3.19.1",
|
||||||
"@react-aria/interactions": "3.23.0",
|
"@react-aria/interactions": "3.23.0",
|
||||||
"@react-aria/utils": "3.25.3",
|
"@react-aria/utils": "3.25.3",
|
||||||
|
|
420
pnpm-lock.yaml
generated
420
pnpm-lock.yaml
generated
|
@ -110,6 +110,9 @@ importers:
|
||||||
'@popperjs/core':
|
'@popperjs/core':
|
||||||
specifier: 2.11.8
|
specifier: 2.11.8
|
||||||
version: 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':
|
'@react-aria/focus':
|
||||||
specifier: 3.19.1
|
specifier: 3.19.1
|
||||||
version: 3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.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==}
|
resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
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':
|
'@formatjs/ecma402-abstract@1.11.4':
|
||||||
resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==}
|
resolution: {integrity: sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==}
|
||||||
|
|
||||||
|
@ -1918,6 +1936,215 @@ packages:
|
||||||
'@protobufjs/utf8@1.1.0':
|
'@protobufjs/utf8@1.1.0':
|
||||||
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
|
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':
|
'@react-aria/accordion@3.0.0-alpha.35':
|
||||||
resolution: {integrity: sha512-eZcsHJDVDNIZ2XUmJynHScRv1YAF/+fj5T0zoGdyEPImIIxJLROupQ75uwarAI5btGSR2TFeqYRmRXJrVuxgoA==}
|
resolution: {integrity: sha512-eZcsHJDVDNIZ2XUmJynHScRv1YAF/+fj5T0zoGdyEPImIIxJLROupQ75uwarAI5btGSR2TFeqYRmRXJrVuxgoA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -10171,6 +10398,23 @@ snapshots:
|
||||||
|
|
||||||
'@eslint/js@8.56.0': {}
|
'@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':
|
'@formatjs/ecma402-abstract@1.11.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@formatjs/intl-localematcher': 0.2.25
|
'@formatjs/intl-localematcher': 0.2.25
|
||||||
|
@ -10946,6 +11190,182 @@ snapshots:
|
||||||
|
|
||||||
'@protobufjs/utf8@1.1.0': {}
|
'@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)':
|
'@react-aria/accordion@3.0.0-alpha.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@react-aria/button': 3.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
'@react-aria/button': 3.11.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
|
|
@ -136,6 +136,7 @@ $image-radius: $button-radius - $image-margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
.FunSubNav__ListBoxItem {
|
.FunSubNav__ListBoxItem {
|
||||||
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
cursor: pointer;
|
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 {
|
.FunSubNav__ListBoxItem__Button {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
@use './FunConstants.scss';
|
@use './FunConstants.scss';
|
||||||
|
|
||||||
.FunTooltip {
|
.FunTooltip {
|
||||||
|
position: relative;
|
||||||
|
z-index: 100000;
|
||||||
max-width: calc(32ch + (8px * 2));
|
max-width: calc(32ch + (8px * 2));
|
||||||
padding-block: 4px;
|
padding-block: 4px;
|
||||||
padding-inline: 8px;
|
padding-inline: 8px;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import type { ForwardedRef, ReactNode } from 'react';
|
import type {
|
||||||
import React, { forwardRef, useEffect, useRef } from 'react';
|
ForwardedRef,
|
||||||
import { type PressEvent, useLongPress } from 'react-aria';
|
ReactNode,
|
||||||
import type { LongPressEvent } from '@react-types/shared';
|
DOMAttributes,
|
||||||
import { Button } from 'react-aria-components';
|
PointerEvent,
|
||||||
import { mergeRefs } from '@react-aria/utils';
|
} from 'react';
|
||||||
import { PressResponder } from '@react-aria/interactions';
|
import React, { forwardRef, useCallback, useEffect, useMemo } from 'react';
|
||||||
|
import { mergeProps } from '@react-aria/utils';
|
||||||
import { strictAssert } from '../../../util/assert';
|
import { strictAssert } from '../../../util/assert';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,49 +29,120 @@ export type FunItemButtonProps = Readonly<
|
||||||
{
|
{
|
||||||
'aria-label': string;
|
'aria-label': string;
|
||||||
excludeFromTabOrder: boolean;
|
excludeFromTabOrder: boolean;
|
||||||
onPress: (event: PressEvent) => void;
|
onClick: (event: PointerEvent) => void;
|
||||||
onContextMenu?: (event: MouseEvent) => void;
|
onContextMenu?: (event: PointerEvent) => void;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
} & FunItemButtonLongPressProps
|
} & FunItemButtonLongPressProps
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const FunItemButton = forwardRef(function FunItemButton(
|
export const FunItemButton = forwardRef(function FunItemButton(
|
||||||
props: FunItemButtonProps,
|
props: FunItemButtonProps,
|
||||||
outerRef: ForwardedRef<HTMLButtonElement>
|
ref: ForwardedRef<HTMLButtonElement>
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
const { onContextMenu } = props;
|
const {
|
||||||
const innerRef = useRef<HTMLButtonElement>(null);
|
'aria-label': ariaLabel,
|
||||||
|
excludeFromTabOrder,
|
||||||
|
onClick,
|
||||||
|
onContextMenu,
|
||||||
|
children,
|
||||||
|
longPressAccessibilityDescription,
|
||||||
|
onLongPress,
|
||||||
|
...rest
|
||||||
|
} = props;
|
||||||
|
|
||||||
const { longPressProps } = useLongPress({
|
const longPressProps = useLongPress(onLongPress ?? null);
|
||||||
isDisabled: props.onLongPress == null,
|
|
||||||
accessibilityDescription: props.longPressAccessibilityDescription,
|
|
||||||
onLongPress: props.onLongPress,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
const handleClick = useCallback(
|
||||||
strictAssert(innerRef.current, 'Missing ref element');
|
(event: PointerEvent) => {
|
||||||
const element = innerRef.current;
|
if (!event.defaultPrevented) {
|
||||||
if (onContextMenu == null) {
|
onClick(event);
|
||||||
return () => null;
|
}
|
||||||
}
|
},
|
||||||
element.addEventListener('contextmenu', onContextMenu);
|
[onClick]
|
||||||
return () => {
|
);
|
||||||
element.removeEventListener('contextmenu', onContextMenu);
|
|
||||||
};
|
|
||||||
}, [onContextMenu]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PressResponder {...longPressProps}>
|
// eslint-disable-next-line jsx-a11y/role-supports-aria-props
|
||||||
<Button
|
<button
|
||||||
ref={mergeRefs(innerRef, outerRef)}
|
ref={ref}
|
||||||
type="button"
|
type="button"
|
||||||
className="FunItem__Button"
|
className="FunItem__Button"
|
||||||
aria-label={props['aria-label']}
|
aria-label={ariaLabel}
|
||||||
excludeFromTabOrder={props.excludeFromTabOrder}
|
aria-description={longPressAccessibilityDescription}
|
||||||
onPress={props.onPress}
|
tabIndex={excludeFromTabOrder ? -1 : undefined}
|
||||||
>
|
{...mergeProps(
|
||||||
{props.children}
|
longPressProps,
|
||||||
</Button>
|
{
|
||||||
</PressResponder>
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import React, { useCallback } from 'react';
|
||||||
import type { Placement } from 'react-aria';
|
import type { Placement } from 'react-aria';
|
||||||
import { Dialog, Popover } from 'react-aria-components';
|
import { Dialog, Popover } from 'react-aria-components';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||||
import { ThemeType } from '../../../types/Util';
|
import { ThemeType } from '../../../types/Util';
|
||||||
|
|
||||||
export type FunPopoverProps = Readonly<{
|
export type FunPopoverProps = Readonly<{
|
||||||
|
@ -16,8 +17,14 @@ export type FunPopoverProps = Readonly<{
|
||||||
export function FunPopover(props: FunPopoverProps): JSX.Element {
|
export function FunPopover(props: FunPopoverProps): JSX.Element {
|
||||||
const shouldCloseOnInteractOutside = useCallback(
|
const shouldCloseOnInteractOutside = useCallback(
|
||||||
(element: Element): boolean => {
|
(element: Element): boolean => {
|
||||||
// Don't close when quill steals focus
|
const match = element.closest(
|
||||||
const match = element.closest('.module-composition-input__input');
|
[
|
||||||
|
// Don't close when quill steals focus
|
||||||
|
'.module-composition-input__input',
|
||||||
|
// Don't close when clicking tooltip
|
||||||
|
'.FunTooltip',
|
||||||
|
].join(', ')
|
||||||
|
);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -27,16 +34,18 @@ export function FunPopover(props: FunPopoverProps): JSX.Element {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover
|
<Tooltip.Provider>
|
||||||
data-fun-overlay
|
<Popover
|
||||||
className={classNames('FunPopover', {
|
data-fun-overlay
|
||||||
'light-theme': props.theme === ThemeType.light,
|
className={classNames('FunPopover', {
|
||||||
'dark-theme': props.theme === ThemeType.dark,
|
'light-theme': props.theme === ThemeType.light,
|
||||||
})}
|
'dark-theme': props.theme === ThemeType.dark,
|
||||||
placement={props.placement}
|
})}
|
||||||
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
|
placement={props.placement}
|
||||||
>
|
shouldCloseOnInteractOutside={shouldCloseOnInteractOutside}
|
||||||
<Dialog className="FunPopover__Dialog">{props.children}</Dialog>
|
>
|
||||||
</Popover>
|
<Dialog className="FunPopover__Dialog">{props.children}</Dialog>
|
||||||
|
</Popover>
|
||||||
|
</Tooltip.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { Transition } from 'framer-motion';
|
import type { Transition } from 'framer-motion';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode, Ref } from 'react';
|
||||||
import React, {
|
import React, {
|
||||||
createContext,
|
createContext,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
@ -13,6 +13,7 @@ import React, {
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
useId,
|
useId,
|
||||||
|
forwardRef,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import type { Selection } from 'react-aria-components';
|
import type { Selection } from 'react-aria-components';
|
||||||
import { ListBox, ListBoxItem } 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 * as Errors from '../../../types/errors';
|
||||||
import { strictAssert } from '../../../util/assert';
|
import { strictAssert } from '../../../util/assert';
|
||||||
import { FunImage } from './FunImage';
|
import { FunImage } from './FunImage';
|
||||||
|
import { FunTooltip } from './FunTooltip';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sub Nav
|
* 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(
|
export function FunSubNavListBoxItem(
|
||||||
props: FunSubNavListBoxItemProps
|
props: FunSubNavListBoxItemProps
|
||||||
): JSX.Element {
|
): JSX.Element {
|
||||||
const context = useContext(FunSubNavListBoxContext);
|
const context = useContext(FunSubNavListBoxContext);
|
||||||
strictAssert(context, 'Must be wrapped with <FunSubNavListBox>');
|
strictAssert(context, 'Must be wrapped with <FunSubNavListBox>');
|
||||||
|
|
||||||
|
const [tooltipOpen, setTooltipOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleTooltipOpenChange = useCallback((open: boolean) => {
|
||||||
|
setTooltipOpen(open);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListBoxItem
|
<ListBoxItem
|
||||||
id={props.id}
|
id={props.id}
|
||||||
|
@ -260,22 +281,35 @@ export function FunSubNavListBoxItem(
|
||||||
>
|
>
|
||||||
{({ isSelected, isFocusVisible }) => {
|
{({ isSelected, isFocusVisible }) => {
|
||||||
return (
|
return (
|
||||||
<FunSubNavListBoxItemButton isSelected={isSelected}>
|
<>
|
||||||
<span className="FunSubNav__ListBoxItem__ButtonIcon">
|
<FunTooltip
|
||||||
{props.children}
|
open={tooltipOpen || (isSelected && isFocusVisible)}
|
||||||
</span>
|
onOpenChange={handleTooltipOpenChange}
|
||||||
{isSelected && (
|
side="top"
|
||||||
<motion.div
|
content={props.label}
|
||||||
className="FunSubNav__ListBoxItem__ButtonIndicator"
|
collisionBoundarySelector=".FunPanel"
|
||||||
layoutId={`FunSubNav__ListBoxItem__ButtonIndicator--${context.id}`}
|
collisionPadding={6}
|
||||||
layoutDependency={context.selected}
|
disableHoverableContent
|
||||||
transition={FunSubNavListBoxItemTransition}
|
>
|
||||||
/>
|
<FunSubNavListBoxItemTooltipTarget />
|
||||||
)}
|
</FunTooltip>
|
||||||
{!isSelected && isFocusVisible && (
|
<FunSubNavListBoxItemButton isSelected={isSelected}>
|
||||||
<div className="FunSubNav__ListBoxItem__ButtonIndicator" />
|
<span className="FunSubNav__ListBoxItem__ButtonIcon">
|
||||||
)}
|
{props.children}
|
||||||
</FunSubNavListBoxItemButton>
|
</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>
|
</ListBoxItem>
|
||||||
|
|
|
@ -1,19 +1,57 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { type ReactNode } from 'react';
|
import React, { useRef, useState, type ReactNode } from 'react';
|
||||||
import type { Placement } from 'react-aria';
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||||
import { Tooltip } from 'react-aria-components';
|
import { useLayoutEffect } from '@react-aria/utils';
|
||||||
|
import { strictAssert } from '../../../util/assert';
|
||||||
|
|
||||||
export type FunTooltipProps = Readonly<{
|
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;
|
children: ReactNode;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export function FunTooltip(props: FunTooltipProps): JSX.Element {
|
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 (
|
return (
|
||||||
<Tooltip className="FunTooltip" placement={props.placement}>
|
<Tooltip.Root
|
||||||
{props.children}
|
open={props.open}
|
||||||
</Tooltip>
|
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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
|
import React, { memo, useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
import type { MouseEvent, PointerEvent } from 'react';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
Heading,
|
Heading,
|
||||||
OverlayArrow,
|
OverlayArrow,
|
||||||
Popover,
|
Popover,
|
||||||
TooltipTrigger,
|
|
||||||
} from 'react-aria-components';
|
} from 'react-aria-components';
|
||||||
import type { PressEvent } from 'react-aria';
|
|
||||||
import { VisuallyHidden } from 'react-aria';
|
import { VisuallyHidden } from 'react-aria';
|
||||||
|
import * as Tooltip from '@radix-ui/react-tooltip';
|
||||||
import type { LocalizerType } from '../../../types/I18N';
|
import type { LocalizerType } from '../../../types/I18N';
|
||||||
import { strictAssert } from '../../../util/assert';
|
import { strictAssert } from '../../../util/assert';
|
||||||
import { missingCaseError } from '../../../util/missingCaseError';
|
import { missingCaseError } from '../../../util/missingCaseError';
|
||||||
|
@ -388,95 +388,99 @@ export function FunPanelEmojis({
|
||||||
</FunPanelFooter>
|
</FunPanelFooter>
|
||||||
)}
|
)}
|
||||||
<FunPanelBody>
|
<FunPanelBody>
|
||||||
<FunScroller
|
<Tooltip.Provider skipDelayDuration={0}>
|
||||||
ref={scrollerRef}
|
<FunScroller
|
||||||
sectionGap={EMOJI_GRID_SECTION_GAP}
|
ref={scrollerRef}
|
||||||
onScrollSectionChange={handleScrollSectionChange}
|
sectionGap={EMOJI_GRID_SECTION_GAP}
|
||||||
>
|
onScrollSectionChange={handleScrollSectionChange}
|
||||||
{layout.sections.length === 0 && (
|
>
|
||||||
<FunResults aria-busy={false}>
|
{layout.sections.length === 0 && (
|
||||||
<FunResultsHeader>
|
<FunResults aria-busy={false}>
|
||||||
{i18n('icu:FunPanelEmojis__SearchResults__EmptyHeading')}{' '}
|
<FunResultsHeader>
|
||||||
<FunStaticEmoji
|
{i18n('icu:FunPanelEmojis__SearchResults__EmptyHeading')}{' '}
|
||||||
size={16}
|
<FunStaticEmoji
|
||||||
role="presentation"
|
size={16}
|
||||||
emoji={emojiVariantConstant('\u{1F641}')}
|
role="presentation"
|
||||||
/>
|
emoji={emojiVariantConstant('\u{1F641}')}
|
||||||
</FunResultsHeader>
|
/>
|
||||||
</FunResults>
|
</FunResultsHeader>
|
||||||
)}
|
</FunResults>
|
||||||
{layout.sections.length > 0 && (
|
)}
|
||||||
<FunKeyboard
|
{layout.sections.length > 0 && (
|
||||||
scrollerRef={scrollerRef}
|
<FunKeyboard
|
||||||
keyboard={keyboard}
|
scrollerRef={scrollerRef}
|
||||||
onStateChange={handleKeyboardStateChange}
|
keyboard={keyboard}
|
||||||
>
|
onStateChange={handleKeyboardStateChange}
|
||||||
<FunGridContainer
|
|
||||||
totalSize={layout.totalHeight}
|
|
||||||
columnCount={EMOJI_GRID_COLUMNS}
|
|
||||||
cellWidth={EMOJI_GRID_CELL_WIDTH}
|
|
||||||
cellHeight={EMOJI_GRID_CELL_HEIGHT}
|
|
||||||
>
|
>
|
||||||
{layout.sections.map(section => {
|
<FunGridContainer
|
||||||
return (
|
totalSize={layout.totalHeight}
|
||||||
<FunGridScrollerSection
|
columnCount={EMOJI_GRID_COLUMNS}
|
||||||
key={section.key}
|
cellWidth={EMOJI_GRID_CELL_WIDTH}
|
||||||
id={section.id}
|
cellHeight={EMOJI_GRID_CELL_HEIGHT}
|
||||||
sectionOffset={section.sectionOffset}
|
>
|
||||||
sectionSize={section.sectionSize}
|
{layout.sections.map(section => {
|
||||||
>
|
return (
|
||||||
<FunGridHeader
|
<FunGridScrollerSection
|
||||||
id={section.header.key}
|
key={section.key}
|
||||||
headerOffset={section.header.headerOffset}
|
id={section.id}
|
||||||
headerSize={section.header.headerSize}
|
sectionOffset={section.sectionOffset}
|
||||||
|
sectionSize={section.sectionSize}
|
||||||
>
|
>
|
||||||
<FunGridHeaderText>
|
<FunGridHeader
|
||||||
{getTitleForSection(
|
id={section.header.key}
|
||||||
i18n,
|
headerOffset={section.header.headerOffset}
|
||||||
section.id as FunEmojisSection
|
headerSize={section.header.headerSize}
|
||||||
)}
|
>
|
||||||
</FunGridHeaderText>
|
<FunGridHeaderText>
|
||||||
{section.id ===
|
{getTitleForSection(
|
||||||
EmojiPickerCategory.SmileysAndPeople && (
|
i18n,
|
||||||
<SectionSkinToneHeaderPopover
|
section.id as FunEmojisSection
|
||||||
i18n={i18n}
|
)}
|
||||||
open={skinTonePopoverOpen}
|
</FunGridHeaderText>
|
||||||
onOpenChange={handleSkinTonePopoverOpenChange}
|
{section.id ===
|
||||||
onSelectSkinTone={fun.onEmojiSkinToneDefaultChange}
|
EmojiPickerCategory.SmileysAndPeople && (
|
||||||
/>
|
<SectionSkinToneHeaderPopover
|
||||||
)}
|
|
||||||
</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}
|
i18n={i18n}
|
||||||
rowIndex={row.rowIndex}
|
open={skinTonePopoverOpen}
|
||||||
cells={row.cells}
|
onOpenChange={handleSkinTonePopoverOpenChange}
|
||||||
focusedCellKey={focusedCellKey}
|
onSelectSkinTone={
|
||||||
emojiSkinToneDefault={fun.emojiSkinToneDefault}
|
|
||||||
onSelectEmoji={handleSelectEmoji}
|
|
||||||
onEmojiSkinToneDefaultChange={
|
|
||||||
fun.onEmojiSkinToneDefaultChange
|
fun.onEmojiSkinToneDefaultChange
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
)}
|
||||||
})}
|
</FunGridHeader>
|
||||||
</FunGridRowGroup>
|
<FunGridRowGroup
|
||||||
</FunGridScrollerSection>
|
aria-labelledby={section.header.key}
|
||||||
);
|
colCount={section.colCount}
|
||||||
})}
|
rowCount={section.rowCount}
|
||||||
</FunGridContainer>
|
rowGroupOffset={section.rowGroup.rowGroupOffset}
|
||||||
</FunKeyboard>
|
rowGroupSize={section.rowGroup.rowGroupSize}
|
||||||
)}
|
>
|
||||||
</FunScroller>
|
{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>
|
</FunPanelBody>
|
||||||
</FunPanel>
|
</FunPanel>
|
||||||
);
|
);
|
||||||
|
@ -572,8 +576,8 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
|
||||||
return getEmojiVariantByParentKeyAndSkinTone(emojiParent.key, skinTone);
|
return getEmojiVariantByParentKeyAndSkinTone(emojiParent.key, skinTone);
|
||||||
}, [emojiParent, skinTone]);
|
}, [emojiParent, skinTone]);
|
||||||
|
|
||||||
const handlePress = useCallback(
|
const handleClick = useCallback(
|
||||||
(event: PressEvent) => {
|
(event: PointerEvent) => {
|
||||||
if (emojiHasSkinToneVariants && emojiSkinToneDefault == null) {
|
if (emojiHasSkinToneVariants && emojiSkinToneDefault == null) {
|
||||||
setPopoverOpen(true);
|
setPopoverOpen(true);
|
||||||
return;
|
return;
|
||||||
|
@ -585,7 +589,7 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
|
||||||
skinTone,
|
skinTone,
|
||||||
};
|
};
|
||||||
const shouldClose =
|
const shouldClose =
|
||||||
(event.pointerType === 'keyboard' || event.pointerType === 'virtual') &&
|
event.nativeEvent.pointerType !== 'mouse' &&
|
||||||
!(event.ctrlKey || event.metaKey);
|
!(event.ctrlKey || event.metaKey);
|
||||||
onSelectEmoji(emojiSelection, shouldClose);
|
onSelectEmoji(emojiSelection, shouldClose);
|
||||||
},
|
},
|
||||||
|
@ -654,12 +658,20 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
|
||||||
colIndex={props.colIndex}
|
colIndex={props.colIndex}
|
||||||
rowIndex={props.rowIndex}
|
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
|
<FunItemButton
|
||||||
ref={popoverTriggerRef}
|
ref={popoverTriggerRef}
|
||||||
excludeFromTabOrder={!props.isTabbable}
|
excludeFromTabOrder={!props.isTabbable}
|
||||||
aria-label={emojiName}
|
aria-label={emojiName}
|
||||||
onPress={handlePress}
|
onClick={handleClick}
|
||||||
onLongPress={handleLongPress}
|
onLongPress={handleLongPress}
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
longPressAccessibilityDescription={i18n(
|
longPressAccessibilityDescription={i18n(
|
||||||
|
@ -668,9 +680,7 @@ const Cell = memo(function Cell(props: CellProps): JSX.Element {
|
||||||
>
|
>
|
||||||
<FunStaticEmoji role="presentation" size={32} emoji={emojiVariant} />
|
<FunStaticEmoji role="presentation" size={32} emoji={emojiVariant} />
|
||||||
</FunItemButton>
|
</FunItemButton>
|
||||||
<FunTooltip placement="top">{`:${emojiShortNameDisplay}:`}</FunTooltip>
|
</FunTooltip>
|
||||||
</TooltipTrigger>
|
|
||||||
d
|
|
||||||
{emojiHasSkinToneVariants && (
|
{emojiHasSkinToneVariants && (
|
||||||
<Popover
|
<Popover
|
||||||
data-fun-overlay
|
data-fun-overlay
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import type { Range } from '@tanstack/react-virtual';
|
import type { Range } from '@tanstack/react-virtual';
|
||||||
import { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual';
|
import { defaultRangeExtractor, useVirtualizer } from '@tanstack/react-virtual';
|
||||||
|
import type { PointerEvent } from 'react';
|
||||||
import React, {
|
import React, {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
@ -11,7 +12,6 @@ import React, {
|
||||||
useState,
|
useState,
|
||||||
useId,
|
useId,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import type { PressEvent } from 'react-aria';
|
|
||||||
import { VisuallyHidden } from 'react-aria';
|
import { VisuallyHidden } from 'react-aria';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
import { FunItemButton } from '../base/FunItem';
|
import { FunItemButton } from '../base/FunItem';
|
||||||
|
@ -356,8 +356,8 @@ export function FunPanelGifs({
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePressGif = useCallback(
|
const handleClickGif = useCallback(
|
||||||
(_event: PressEvent, gifSelection: FunGifSelection) => {
|
(_event: PointerEvent, gifSelection: FunGifSelection) => {
|
||||||
onFunSelectGif(gifSelection);
|
onFunSelectGif(gifSelection);
|
||||||
onSelectGif(gifSelection);
|
onSelectGif(gifSelection);
|
||||||
setSelectedItemKey(null);
|
setSelectedItemKey(null);
|
||||||
|
@ -520,7 +520,7 @@ export function FunPanelGifs({
|
||||||
itemOffset={item.start}
|
itemOffset={item.start}
|
||||||
itemLane={item.lane}
|
itemLane={item.lane}
|
||||||
isTabbable={isTabbable}
|
isTabbable={isTabbable}
|
||||||
onPressGif={handlePressGif}
|
onClickGif={handleClickGif}
|
||||||
fetchGif={fetchGif}
|
fetchGif={fetchGif}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -542,16 +542,16 @@ const Item = memo(function Item(props: {
|
||||||
itemOffset: number;
|
itemOffset: number;
|
||||||
itemLane: number;
|
itemLane: number;
|
||||||
isTabbable: boolean;
|
isTabbable: boolean;
|
||||||
onPressGif: (event: PressEvent, gifSelection: FunGifSelection) => void;
|
onClickGif: (event: PointerEvent, gifSelection: FunGifSelection) => void;
|
||||||
fetchGif: typeof tenorDownload;
|
fetchGif: typeof tenorDownload;
|
||||||
}) {
|
}) {
|
||||||
const { onPressGif, fetchGif } = props;
|
const { onClickGif, fetchGif } = props;
|
||||||
|
|
||||||
const handlePress = useCallback(
|
const handleClick = useCallback(
|
||||||
async (event: PressEvent) => {
|
async (event: PointerEvent) => {
|
||||||
onPressGif(event, { gif: props.gif });
|
onClickGif(event, { gif: props.gif });
|
||||||
},
|
},
|
||||||
[props.gif, onPressGif]
|
[props.gif, onClickGif]
|
||||||
);
|
);
|
||||||
|
|
||||||
const descriptionId = `FunGifsPanelItem__GifDescription--${props.gif.id}`;
|
const descriptionId = `FunGifsPanelItem__GifDescription--${props.gif.id}`;
|
||||||
|
@ -606,7 +606,7 @@ const Item = memo(function Item(props: {
|
||||||
>
|
>
|
||||||
<FunItemButton
|
<FunItemButton
|
||||||
aria-label={props.gif.title}
|
aria-label={props.gif.title}
|
||||||
onPress={handlePress}
|
onClick={handleClick}
|
||||||
excludeFromTabOrder={!props.isTabbable}
|
excludeFromTabOrder={!props.isTabbable}
|
||||||
>
|
>
|
||||||
{src != null && (
|
{src != null && (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright 2025 Signal Messenger, LLC
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import type { CSSProperties } from 'react';
|
import type { CSSProperties, PointerEvent } from 'react';
|
||||||
import React, {
|
import React, {
|
||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
@ -9,7 +9,6 @@ import React, {
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import type { PressEvent } from 'react-aria';
|
|
||||||
import type {
|
import type {
|
||||||
StickerPackType,
|
StickerPackType,
|
||||||
StickerType,
|
StickerType,
|
||||||
|
@ -344,8 +343,8 @@ export function FunPanelStickers({
|
||||||
return searchInput.length > 0;
|
return searchInput.length > 0;
|
||||||
}, [searchInput]);
|
}, [searchInput]);
|
||||||
|
|
||||||
const handlePressSticker = useCallback(
|
const handleClickSticker = useCallback(
|
||||||
(event: PressEvent, stickerSelection: FunStickerSelection) => {
|
(event: PointerEvent, stickerSelection: FunStickerSelection) => {
|
||||||
onFunSelectSticker(stickerSelection);
|
onFunSelectSticker(stickerSelection);
|
||||||
onSelectSticker(stickerSelection);
|
onSelectSticker(stickerSelection);
|
||||||
if (!(event.ctrlKey || event.metaKey)) {
|
if (!(event.ctrlKey || event.metaKey)) {
|
||||||
|
@ -356,8 +355,8 @@ export function FunPanelStickers({
|
||||||
[onFunSelectSticker, onSelectSticker, onClose]
|
[onFunSelectSticker, onSelectSticker, onClose]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlePressTimeSticker = useCallback(
|
const handleClickTimeSticker = useCallback(
|
||||||
(event: PressEvent, style: FunTimeStickerStyle) => {
|
(event: PointerEvent, style: FunTimeStickerStyle) => {
|
||||||
onSelectTimeSticker?.(style);
|
onSelectTimeSticker?.(style);
|
||||||
if (!(event.ctrlKey || event.metaKey)) {
|
if (!(event.ctrlKey || event.metaKey)) {
|
||||||
onClose();
|
onClose();
|
||||||
|
@ -492,8 +491,8 @@ export function FunPanelStickers({
|
||||||
cells={row.cells}
|
cells={row.cells}
|
||||||
stickerLookup={stickerLookup}
|
stickerLookup={stickerLookup}
|
||||||
focusedCellKey={focusedCellKey}
|
focusedCellKey={focusedCellKey}
|
||||||
onPressSticker={handlePressSticker}
|
onClickSticker={handleClickSticker}
|
||||||
onPressTimeSticker={handlePressTimeSticker}
|
onClickTimeSticker={handleClickTimeSticker}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -515,11 +514,11 @@ const Row = memo(function Row(props: {
|
||||||
stickerLookup: StickerLookup;
|
stickerLookup: StickerLookup;
|
||||||
cells: ReadonlyArray<CellLayoutNode>;
|
cells: ReadonlyArray<CellLayoutNode>;
|
||||||
focusedCellKey: CellKey | null;
|
focusedCellKey: CellKey | null;
|
||||||
onPressSticker: (
|
onClickSticker: (
|
||||||
event: PressEvent,
|
event: PointerEvent,
|
||||||
stickerSelection: FunStickerSelection
|
stickerSelection: FunStickerSelection
|
||||||
) => void;
|
) => void;
|
||||||
onPressTimeSticker: (event: PressEvent, style: FunTimeStickerStyle) => void;
|
onClickTimeSticker: (event: PointerEvent, style: FunTimeStickerStyle) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<FunGridRow rowIndex={props.rowIndex}>
|
<FunGridRow rowIndex={props.rowIndex}>
|
||||||
|
@ -537,8 +536,8 @@ const Row = memo(function Row(props: {
|
||||||
colIndex={cell.colIndex}
|
colIndex={cell.colIndex}
|
||||||
stickerLookup={props.stickerLookup}
|
stickerLookup={props.stickerLookup}
|
||||||
isTabbable={isTabbable}
|
isTabbable={isTabbable}
|
||||||
onPressSticker={props.onPressSticker}
|
onClickSticker={props.onClickSticker}
|
||||||
onPressTimeSticker={props.onPressTimeSticker}
|
onClickTimeSticker={props.onClickTimeSticker}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -553,28 +552,28 @@ const Cell = memo(function Cell(props: {
|
||||||
rowIndex: number;
|
rowIndex: number;
|
||||||
stickerLookup: StickerLookup;
|
stickerLookup: StickerLookup;
|
||||||
isTabbable: boolean;
|
isTabbable: boolean;
|
||||||
onPressSticker: (
|
onClickSticker: (
|
||||||
event: PressEvent,
|
event: PointerEvent,
|
||||||
stickerSelection: FunStickerSelection
|
stickerSelection: FunStickerSelection
|
||||||
) => void;
|
) => void;
|
||||||
onPressTimeSticker: (event: PressEvent, style: FunTimeStickerStyle) => void;
|
onClickTimeSticker: (event: PointerEvent, style: FunTimeStickerStyle) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { onPressSticker, onPressTimeSticker } = props;
|
const { onClickSticker, onClickTimeSticker } = props;
|
||||||
const stickerLookupItem = props.stickerLookup[props.value];
|
const stickerLookupItem = props.stickerLookup[props.value];
|
||||||
|
|
||||||
const handlePress = useCallback(
|
const handleClick = useCallback(
|
||||||
(event: PressEvent) => {
|
(event: PointerEvent) => {
|
||||||
if (stickerLookupItem.kind === 'sticker') {
|
if (stickerLookupItem.kind === 'sticker') {
|
||||||
onPressSticker(event, {
|
onClickSticker(event, {
|
||||||
stickerPackId: stickerLookupItem.sticker.packId,
|
stickerPackId: stickerLookupItem.sticker.packId,
|
||||||
stickerId: stickerLookupItem.sticker.id,
|
stickerId: stickerLookupItem.sticker.id,
|
||||||
stickerUrl: stickerLookupItem.sticker.url,
|
stickerUrl: stickerLookupItem.sticker.url,
|
||||||
});
|
});
|
||||||
} else if (stickerLookupItem.kind === 'timeSticker') {
|
} else if (stickerLookupItem.kind === 'timeSticker') {
|
||||||
onPressTimeSticker(event, stickerLookupItem.style);
|
onClickTimeSticker(event, stickerLookupItem.style);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[stickerLookupItem, onPressSticker, onPressTimeSticker]
|
[stickerLookupItem, onClickSticker, onClickTimeSticker]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -590,7 +589,7 @@ const Cell = memo(function Cell(props: {
|
||||||
? (stickerLookupItem.sticker.emoji ?? '')
|
? (stickerLookupItem.sticker.emoji ?? '')
|
||||||
: stickerLookupItem.style
|
: stickerLookupItem.style
|
||||||
}
|
}
|
||||||
onPress={handlePress}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
{stickerLookupItem.kind === 'sticker' && (
|
{stickerLookupItem.kind === 'sticker' && (
|
||||||
<FunSticker
|
<FunSticker
|
||||||
|
|
|
@ -15,6 +15,69 @@
|
||||||
"updated": "2018-09-18T19:19:27.699Z",
|
"updated": "2018-09-18T19:19:27.699Z",
|
||||||
"reasonDetail": "Part of runtime library for C++ transpiled code"
|
"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",
|
"rule": "DOM-innerHTML",
|
||||||
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
|
"path": "node_modules/@signalapp/quill-cjs/core/editor.js",
|
||||||
|
@ -1842,13 +1905,6 @@
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2025-02-19T20:14:46.879Z"
|
"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",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/fun/base/FunScroller.tsx",
|
"path": "ts/components/fun/base/FunScroller.tsx",
|
||||||
|
@ -1905,6 +1961,13 @@
|
||||||
"reasonCategory": "usageTrusted",
|
"reasonCategory": "usageTrusted",
|
||||||
"updated": "2025-02-19T20:14:46.879Z"
|
"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",
|
"rule": "React-useRef",
|
||||||
"path": "ts/components/fun/data/infinite.ts",
|
"path": "ts/components/fun/data/infinite.ts",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue