From b6cfe0933d3f8fe6bc0c2635a963d4e350c06ce0 Mon Sep 17 00:00:00 2001 From: Josh Perez <60019601+josh-signal@users.noreply.github.com> Date: Wed, 29 Sep 2021 16:59:37 -0400 Subject: [PATCH] Adds open/close animations to dialogs and modals --- ACKNOWLEDGMENTS.md | 49 ++++ package.json | 4 +- ts/components/App.tsx | 10 + ts/components/ConfirmationDialog.tsx | 97 ++++--- ts/components/ForwardMessageModal.tsx | 356 ++++++++++++++------------ ts/components/Modal.tsx | 67 ++++- ts/hooks/useAnimated.tsx | 37 +++ ts/hooks/useReducedMotion.ts | 34 +++ ts/util/lint/exceptions.json | 147 +++++++++++ yarn.lock | 58 ++++- 10 files changed, 635 insertions(+), 224 deletions(-) create mode 100644 ts/hooks/useAnimated.tsx create mode 100644 ts/hooks/useReducedMotion.ts diff --git a/ACKNOWLEDGMENTS.md b/ACKNOWLEDGMENTS.md index 272d5988e..ed5dc9a54 100644 --- a/ACKNOWLEDGMENTS.md +++ b/ACKNOWLEDGMENTS.md @@ -9,6 +9,30 @@ Signal Desktop makes use of the following open source projects. License: MIT +## @react-spring/web + + MIT License + + Copyright (c) 2018-present Paul Henschel, react-spring, all contributors + + 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. + ## @sindresorhus/is MIT License @@ -471,6 +495,31 @@ Signal Desktop makes use of the following open source projects. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +## bezier-easing + + Copyright (c) 2014 Gaƫtan Renaudeau + + 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. + ## blob-util Apache License diff --git a/package.json b/package.json index f07e283fa..01707280d 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ }, "dependencies": { "@popperjs/core": "2.9.2", + "@react-spring/web": "9.2.4", "@signalapp/signal-client": "0.9.5", "@sindresorhus/is": "0.8.0", "abort-controller": "3.0.0", @@ -80,6 +81,7 @@ "axe-core": "4.1.4", "backbone": "1.4.0", "better-sqlite3": "https://github.com/signalapp/better-sqlite3#2fa02d2484e9f9a10df5ac7ea4617fb2dff30006", + "bezier-easing": "2.1.0", "blob-util": "2.0.2", "blueimp-load-image": "5.14.0", "blurhash": "1.1.3", @@ -212,7 +214,7 @@ "@types/pino": "6.3.6", "@types/pino-multi-stream": "5.1.0", "@types/quill": "1.3.10", - "@types/react": "16.8.5", + "@types/react": "16.8.6", "@types/react-dom": "16.8.2", "@types/react-measure": "2.0.5", "@types/react-redux": "7.1.2", diff --git a/ts/components/App.tsx b/ts/components/App.tsx index 65e022afc..25eef2eda 100644 --- a/ts/components/App.tsx +++ b/ts/components/App.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React, { ComponentProps, useEffect } from 'react'; +import { Globals } from '@react-spring/web'; import classNames from 'classnames'; import { AppViewType } from '../state/ducks/app'; @@ -10,6 +11,7 @@ import { Install } from './Install'; import { StandaloneRegistration } from './StandaloneRegistration'; import { ThemeType } from '../types/Util'; import { usePageVisibility } from '../hooks/usePageVisibility'; +import { useReducedMotion } from '../hooks/useReducedMotion'; type PropsType = { appView: AppViewType; @@ -84,6 +86,14 @@ export const App = ({ document.body.classList.toggle('page-is-visible', isPageVisible); }, [isPageVisible]); + // A11y settings for react-spring + const prefersReducedMotion = useReducedMotion(); + useEffect(() => { + Globals.assign({ + skipAnimation: prefersReducedMotion, + }); + }, [prefersReducedMotion]); + return (
{ - const cancelAndClose = React.useCallback(() => { + const { close, renderAnimation } = useAnimated( + { + from: { opacity: 0, transform: 'scale(0.25)' }, + enter: { opacity: 1, transform: 'scale(1)' }, + leave: { opacity: 0, onRest: () => onClose() }, + config: { + duration: 150, + }, + }, + onClose + ); + + const cancelAndClose = useCallback(() => { if (onCancel) { onCancel(); } - onClose(); - }, [onCancel, onClose]); + close(); + }, [close, onCancel]); - const handleCancel = React.useCallback( - (e: React.MouseEvent) => { + const handleCancel = useCallback( + (e: MouseEvent) => { if (e.target === e.currentTarget) { cancelAndClose(); } @@ -80,40 +94,43 @@ export const ConfirmationDialog = React.memo( const hasActions = Boolean(actions.length); return ( - - {children} - - - {actions.map((action, i) => ( - - ))} - - + {children} + + + {actions.map((action, i) => ( + + ))} + + + )} + ); } ); diff --git a/ts/components/ForwardMessageModal.tsx b/ts/components/ForwardMessageModal.tsx index 5ca7f7ec2..0de436740 100644 --- a/ts/components/ForwardMessageModal.tsx +++ b/ts/components/ForwardMessageModal.tsx @@ -30,6 +30,7 @@ import { SearchInput } from './SearchInput'; import { StagedLinkPreview } from './conversation/StagedLinkPreview'; import { assert } from '../util/assert'; import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations'; +import { useAnimated } from '../hooks/useAnimated'; export type DataPropsType = { attachments?: Array; @@ -198,13 +199,28 @@ export const ForwardMessageModal: FunctionComponent = ({ [contactLookup, selectedContacts, setSelectedContacts] ); + const { close, renderAnimation } = useAnimated( + { + from: { opacity: 0, transform: 'translateY(48px)' }, + enter: { opacity: 1, transform: 'translateY(0px)' }, + leave: { + opacity: 0, + transform: 'translateY(48px)', + }, + config: { + duration: 200, + }, + }, + onClose + ); + const handleBackOrClose = useCallback(() => { if (isEditingMessage) { setIsEditingMessage(false); } else { - onClose(); + close(); } - }, [isEditingMessage, onClose, setIsEditingMessage]); + }, [isEditingMessage, close, setIsEditingMessage]); const rowCount = filteredConversations.length; const getRow = (index: number): undefined | Row => { @@ -249,182 +265,188 @@ export const ForwardMessageModal: FunctionComponent = ({ {i18n('GroupV2--cannot-send')} )} - -
-
+ + {renderAnimation( +
+
+ {isEditingMessage ? ( + + ) : ( +
{isEditingMessage ? ( - - ) : ( -
- {isEditingMessage ? ( -
- {linkPreview ? ( -
- + {linkPreview ? ( +
+ removeLinkPreview()} + title={linkPreview.title} + /> +
+ ) : null} + {attachmentsToForward && attachmentsToForward.length ? ( + removeLinkPreview()} - title={linkPreview.title} + onCloseAttachment={(attachment: AttachmentType) => { + const newAttachments = attachmentsToForward.filter( + currentAttachment => currentAttachment !== attachment + ); + setAttachmentsToForward(newAttachments); + }} /> -
- ) : null} - {attachmentsToForward && attachmentsToForward.length ? ( - { - const newAttachments = attachmentsToForward.filter( - currentAttachment => currentAttachment !== attachment - ); - setAttachmentsToForward(newAttachments); - }} - /> - ) : null} -
- { - setMessageBodyText(messageText); - onEditorStateChange(messageText, bodyRanges, caretLocation); - }} - onPickEmoji={onPickEmoji} - onSubmit={forwardMessage} - onTextTooLong={onTextTooLong} - /> -
- + { + setMessageBodyText(messageText); + onEditorStateChange( + messageText, + bodyRanges, + caretLocation + ); + }} + onPickEmoji={onPickEmoji} + onSubmit={forwardMessage} + onTextTooLong={onTextTooLong} /> +
+ +
-
- ) : ( -
- { - setSearchTerm(event.target.value); - }} - ref={inputRef} - value={searchTerm} - /> - {candidateConversations.length ? ( - - {({ contentRect, measureRef }: MeasuredComponentProps) => { - // We disable this ESLint rule because we're capturing a bubbled - // keydown event. See [this note in the jsx-a11y docs][0]. - // - // [0]: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/c275964f52c35775208bd00cb612c6f82e42e34f/docs/rules/no-static-element-interactions.md#case-the-event-handler-is-only-being-used-to-capture-bubbled-events - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( -
- { - if ( - disabledReason !== - ContactCheckboxDisabledReason.MaximumContactsSelected - ) { - toggleSelectedConversation(conversationId); - } - }} - onSelectConversation={shouldNeverBeCalled} - renderMessageSearchResult={() => { - shouldNeverBeCalled(); - return
; - }} - rowCount={rowCount} - shouldRecomputeRowHeights={false} - showChooseGroupMembers={shouldNeverBeCalled} - startNewConversationFromPhoneNumber={ - shouldNeverBeCalled - } - /> -
- ); - /* eslint-enable jsx-a11y/no-static-element-interactions */ + ) : ( +
+ { + setSearchTerm(event.target.value); }} - - ) : ( -
- {i18n('noContactsFound')} -
- )} -
- )} -
-
- {Boolean(selectedContacts.length) && - selectedContacts.map(contact => contact.title).join(', ')} -
-
- {isEditingMessage || !isMessageEditable ? ( -
-
+ )}
); diff --git a/ts/components/Modal.tsx b/ts/components/Modal.tsx index ae28069fe..2261f5f54 100644 --- a/ts/components/Modal.tsx +++ b/ts/components/Modal.tsx @@ -1,7 +1,7 @@ // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only -import React, { useRef, useState, ReactElement, ReactNode } from 'react'; +import React, { ReactElement, ReactNode, useRef, useState } from 'react'; import Measure, { ContentRect, MeasuredComponentProps } from 'react-measure'; import classNames from 'classnames'; import { noop } from 'lodash'; @@ -10,6 +10,7 @@ import { LocalizerType } from '../types/Util'; import { ModalHost } from './ModalHost'; import { Theme } from '../util/theme'; import { getClassNamesFor } from '../util/getClassNamesFor'; +import { useAnimated } from '../hooks/useAnimated'; import { useHasWrapped } from '../hooks/useHasWrapped'; type PropsType = { @@ -18,9 +19,12 @@ type PropsType = { hasXButton?: boolean; i18n: LocalizerType; moduleClassName?: string; - noMouseClose?: boolean; onClose?: () => void; title?: ReactNode; +}; + +type ModalPropsType = PropsType & { + noMouseClose?: boolean; theme?: Theme; }; @@ -36,8 +40,51 @@ export function Modal({ onClose = noop, title, theme, -}: Readonly): ReactElement { +}: Readonly): ReactElement { + const { close, renderAnimation } = useAnimated( + { + from: { opacity: 0, transform: 'translateY(48px)' }, + enter: { opacity: 1, transform: 'translateY(0px)' }, + leave: { + opacity: 0, + transform: 'translateY(48px)', + }, + config: { + duration: 200, + }, + }, + onClose + ); + + return ( + + {renderAnimation( + + {children} + + )} + + ); +} + +export function ModalWindow({ + children, + hasStickyButtons, + hasXButton, + i18n, + moduleClassName, + onClose = noop, + title, +}: Readonly): JSX.Element { const modalRef = useRef(null); + const bodyRef = useRef(null); const [scrolled, setScrolled] = useState(false); const [hasOverflow, setHasOverflow] = useState(false); @@ -56,10 +103,10 @@ export function Modal({ } return ( - + <> {/* We don't want the click event to propagate to its container node. */} - {/* eslint-disable jsx-a11y/no-static-element-interactions */} - {/* eslint-disable jsx-a11y/click-events-have-key-events */} + {/* eslint-disable-next-line max-len */} + {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */}
- {/* eslint-enable jsx-a11y/no-static-element-interactions */} - {/* eslint-enable jsx-a11y/click-events-have-key-events */} {hasHeader && (
{hasXButton && ( @@ -81,9 +126,7 @@ export function Modal({ type="button" className={getClassName('__close-button')} tabIndex={0} - onClick={() => { - onClose(); - }} + onClick={onClose} /> )} {title && ( @@ -122,7 +165,7 @@ export function Modal({ )}
- + ); } diff --git a/ts/hooks/useAnimated.tsx b/ts/hooks/useAnimated.tsx new file mode 100644 index 000000000..48f484d20 --- /dev/null +++ b/ts/hooks/useAnimated.tsx @@ -0,0 +1,37 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import React, { useState, ReactElement } from 'react'; +import { animated, useTransition, UseTransitionProps } from '@react-spring/web'; +import cubicBezier from 'bezier-easing'; + +export function useAnimated>( + props: UseTransitionProps, + onClose: () => unknown +): { + close: () => unknown; + renderAnimation: (children: ReactElement) => JSX.Element; +} { + const [isOpen, setIsOpen] = useState(true); + + const transitions = useTransition(isOpen, { + ...props, + leave: { + ...props.leave, + onRest: () => onClose(), + }, + config: { + duration: 200, + easing: cubicBezier(0.17, 0.17, 0, 1), + ...props.config, + }, + }); + + return { + close: () => setIsOpen(false), + renderAnimation: children => + transitions((style, item) => + item ? {children} : null + ), + }; +} diff --git a/ts/hooks/useReducedMotion.ts b/ts/hooks/useReducedMotion.ts new file mode 100644 index 000000000..b1fdb3da9 --- /dev/null +++ b/ts/hooks/useReducedMotion.ts @@ -0,0 +1,34 @@ +// Copyright 2021 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +import { useEffect, useState } from 'react'; + +function getReducedMotionQuery(): MediaQueryList { + return window.matchMedia('(prefers-reduced-motion: reduce)'); +} + +// Inspired by . +export function useReducedMotion(): boolean { + const initialQuery = getReducedMotionQuery(); + const [prefersReducedMotion, setPrefersReducedMotion] = useState( + initialQuery.matches + ); + + useEffect(() => { + const query = getReducedMotionQuery(); + + function changePreference() { + setPrefersReducedMotion(query.matches); + } + + changePreference(); + + query.addEventListener('change', changePreference); + + return () => { + query.removeEventListener('change', changePreference); + }; + }); + + return prefersReducedMotion; +} diff --git a/ts/util/lint/exceptions.json b/ts/util/lint/exceptions.json index eab9f9155..20e3d6071 100644 --- a/ts/util/lint/exceptions.json +++ b/ts/util/lint/exceptions.json @@ -240,6 +240,153 @@ "updated": "2018-09-18T19:19:27.699Z", "reasonDetail": "What's being eval'd is a static string" }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/animated/dist/react-spring-animated.cjs.dev.js", + "line": " const instanceRef = React.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/animated/dist/react-spring-animated.cjs.dev.js", + "line": " const observerRef = React.useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/animated/dist/react-spring-animated.cjs.prod.js", + "line": " const instanceRef = React.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/animated/dist/react-spring-animated.cjs.prod.js", + "line": " const observerRef = React.useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/animated/dist/react-spring-animated.esm.js", + "line": " const instanceRef = useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/animated/dist/react-spring-animated.esm.js", + "line": " const observerRef = useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.cjs.dev.js", + "line": " const layoutId = React.useRef(0);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.cjs.dev.js", + "line": " const ctrls = React.useRef([...state.ctrls]);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.cjs.dev.js", + "line": " const usedTransitions = React.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.cjs.prod.js", + "line": " const layoutId = React.useRef(0);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.cjs.prod.js", + "line": " const ctrls = React.useRef([...state.ctrls]);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.cjs.prod.js", + "line": " const usedTransitions = React.useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.esm.js", + "line": " const layoutId = useRef(0);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.esm.js", + "line": " const ctrls = useRef([...state.ctrls]);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/core/dist/react-spring-core.esm.js", + "line": " const usedTransitions = useRef(null);", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/shared/dist/react-spring-shared.cjs.dev.js", + "line": " const committed = React.useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/shared/dist/react-spring-shared.cjs.dev.js", + "line": " const prevRef = React.useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/shared/dist/react-spring-shared.cjs.prod.js", + "line": " const committed = React.useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/shared/dist/react-spring-shared.cjs.prod.js", + "line": " const prevRef = React.useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/shared/dist/react-spring-shared.esm.js", + "line": " const committed = useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, + { + "rule": "React-useRef", + "path": "node_modules/@react-spring/shared/dist/react-spring-shared.esm.js", + "line": " const prevRef = useRef();", + "reasonCategory": "usageTrusted", + "updated": "2021-09-27T21:37:06.339Z" + }, { "rule": "DOM-innerHTML", "path": "node_modules/@sindresorhus/is/dist/index.js", diff --git a/yarn.lock b/yarn.lock index 3845a8a69..308dd489d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1671,6 +1671,51 @@ react-lifecycles-compat "^3.0.4" warning "^3.0.0" +"@react-spring/animated@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.2.4.tgz#062ecc0fdfef89f2541a42d8500428b70035f879" + integrity sha512-AfV6ZM8pCCAT29GY5C8/1bOPjZrv/7kD0vedjiE/tEYvNDwg9GlscrvsTViWR2XykJoYrDfdkYArrldWpsCJ5g== + dependencies: + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/core@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.2.4.tgz#275a4a065e3a315a4f5fb28c9a6f62ce718c25d6" + integrity sha512-R+PwyfsjiuYCWqaTTfCpYpRmsP0h87RNm7uxC1Uxy7QAHUfHEm2sAHn+AdHPwq/MbVwDssVT8C5yf2WGcqiXGg== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/rafz@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.2.4.tgz#44793e9adc14dd0dcd1573d094368af11a89d73a" + integrity sha512-SOKf9eue+vAX+DGo7kWYNl9i9J3gPUlQjifIcV9Bzw9h3i30wPOOP0TjS7iMG/kLp2cdHQYDNFte6nt23VAZkQ== + +"@react-spring/shared@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.2.4.tgz#f9cc66ac5308a77293330a18518e34121f4008c1" + integrity sha512-ZEr4l2BxmyFRUvRA2VCkPfCJii4E7cGkwbjmTBx1EmcGrOnde/V2eF5dxqCTY3k35QuCegkrWe0coRJVkh8q2Q== + dependencies: + "@react-spring/rafz" "~9.2.0" + "@react-spring/types" "~9.2.0" + +"@react-spring/types@~9.2.0": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.2.4.tgz#2365ce9d761f548a9adcb2cd68714bf26765a5de" + integrity sha512-zHUXrWO8nweUN/ISjrjqU7GgXXvoEbFca1CgiE0TY0H/dqJb3l+Rhx8ecPVNYimzFg3ZZ1/T0egpLop8SOv4aA== + +"@react-spring/web@9.2.4": + version "9.2.4" + resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.2.4.tgz#c6d5464a954bfd0d7bc90117050f796a95ebfa08" + integrity sha512-vtPvOalLFvuju/MDBtoSnCyt0xXSL6Amyv82fljOuWPl1yGd4M1WteijnYL9Zlriljl0a3oXcPunAVYTD9dbDQ== + dependencies: + "@react-spring/animated" "~9.2.0" + "@react-spring/core" "~9.2.0" + "@react-spring/shared" "~9.2.0" + "@react-spring/types" "~9.2.0" + "@signalapp/signal-client@0.9.5": version "0.9.5" resolved "https://registry.yarnpkg.com/@signalapp/signal-client/-/signal-client-0.9.5.tgz#b133305aa39c00ceae8743316efd0cc0bfe02e2d" @@ -2844,10 +2889,10 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react@*", "@types/react@16.8.5": - version "16.8.5" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.5.tgz#03b9a6597bc20f6eaaed43f377a160f7e41c2b90" - integrity sha512-8LRySaaSJVLNZb2dbOGvGmzn88cbAfrgDpuWy+6lLgQ0OJFgHHvyuaCX4/7ikqJlpmCPf4uazJAZcfTQRdJqdQ== +"@types/react@*", "@types/react@16.8.6": + version "16.8.6" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.6.tgz#fa1de3fe56cc9b6afeddc73d093d7f30fd5e31cc" + integrity sha512-bN9qDjEMltmHrl0PZRI4IF2AbB7V5UlRfG+OOduckVnRQ4VzXVSzy/1eLAh778IEqhTnW0mmgL9yShfinNverA== dependencies: "@types/prop-types" "*" csstype "^2.2.0" @@ -4732,6 +4777,11 @@ bcrypt-pbkdf@^1.0.0: bindings "^1.5.0" tar "^6.1.0" +bezier-easing@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/bezier-easing/-/bezier-easing-2.1.0.tgz#c04dfe8b926d6ecaca1813d69ff179b7c2025d86" + integrity sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY= + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"