Animates ModalHost overlay

This commit is contained in:
Josh Perez 2021-10-14 12:52:42 -04:00 committed by GitHub
parent cfc5407d03
commit d0e8fbd5a6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 340 additions and 292 deletions

View file

@ -73,7 +73,7 @@
}, },
"dependencies": { "dependencies": {
"@popperjs/core": "2.9.2", "@popperjs/core": "2.9.2",
"@react-spring/web": "9.2.4", "@react-spring/web": "9.2.6",
"@signalapp/signal-client": "0.9.5", "@signalapp/signal-client": "0.9.5",
"@sindresorhus/is": "0.8.0", "@sindresorhus/is": "0.8.0",
"abort-controller": "3.0.0", "abort-controller": "3.0.0",

View file

@ -8725,22 +8725,26 @@ button.module-image__border-overlay:focus {
.module-modal-host__overlay { .module-modal-host__overlay {
background: $color-black-alpha-40; background: $color-black-alpha-40;
position: absolute;
height: 100vh; height: 100vh;
width: 100vw;
left: 0; left: 0;
position: absolute;
top: 0; top: 0;
width: 100vw;
z-index: 2; z-index: 2;
}
.module-modal-host__container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100vh;
justify-content: center; justify-content: center;
left: 0;
overflow: hidden; overflow: hidden;
padding: 20px; padding: 20px;
position: absolute;
top: 0;
width: 100vw;
z-index: 2;
} }
// Module: GroupV2 Join Dialog // Module: GroupV2 Join Dialog

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import React, { MouseEvent, useCallback } from 'react'; import React, { MouseEvent, useCallback } from 'react';
import { animated } from '@react-spring/web';
import { Button, ButtonVariant } from './Button'; import { Button, ButtonVariant } from './Button';
import { LocalizerType } from '../types/Util'; import { LocalizerType } from '../types/Util';
import { ModalHost } from './ModalHost'; import { ModalHost } from './ModalHost';
@ -63,17 +64,10 @@ export const ConfirmationDialog = React.memo(
title, title,
hasXButton, hasXButton,
}: Props) => { }: Props) => {
const { close, renderAnimation } = useAnimated( const { close, overlayStyles, modalStyles } = useAnimated(onClose, {
{ getFrom: () => ({ opacity: 0, transform: 'scale(0.25)' }),
from: { opacity: 0, transform: 'scale(0.25)' }, getTo: isOpen => ({ opacity: isOpen ? 1 : 0, transform: 'scale(1)' }),
enter: { opacity: 1, transform: 'scale(1)' }, });
leave: { opacity: 0, onRest: () => onClose() },
config: {
duration: 150,
},
},
onClose
);
const cancelAndClose = useCallback(() => { const cancelAndClose = useCallback(() => {
if (onCancel) { if (onCancel) {
@ -94,8 +88,8 @@ export const ConfirmationDialog = React.memo(
const hasActions = Boolean(actions.length); const hasActions = Boolean(actions.length);
return ( return (
<ModalHost onClose={close} theme={theme}> <ModalHost onClose={close} theme={theme} overlayStyles={overlayStyles}>
{renderAnimation( <animated.div style={modalStyles}>
<ModalWindow <ModalWindow
hasXButton={hasXButton} hasXButton={hasXButton}
i18n={i18n} i18n={i18n}
@ -129,7 +123,7 @@ export const ConfirmationDialog = React.memo(
))} ))}
</Modal.ButtonFooter> </Modal.ButtonFooter>
</ModalWindow> </ModalWindow>
)} </animated.div>
</ModalHost> </ModalHost>
); );
} }

View file

@ -11,6 +11,7 @@ import React, {
} from 'react'; } from 'react';
import Measure, { MeasuredComponentProps } from 'react-measure'; import Measure, { MeasuredComponentProps } from 'react-measure';
import { noop } from 'lodash'; import { noop } from 'lodash';
import { animated } from '@react-spring/web';
import classNames from 'classnames'; import classNames from 'classnames';
import { AttachmentList } from './conversation/AttachmentList'; import { AttachmentList } from './conversation/AttachmentList';
@ -199,20 +200,16 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
[contactLookup, selectedContacts, setSelectedContacts] [contactLookup, selectedContacts, setSelectedContacts]
); );
const { close, renderAnimation } = useAnimated( const { close, modalStyles, overlayStyles } = useAnimated(onClose, {
{ getFrom: () => ({ opacity: 0, transform: 'translateY(48px)' }),
from: { opacity: 0, transform: 'translateY(48px)' }, getTo: isOpen =>
enter: { opacity: 1, transform: 'translateY(0px)' }, isOpen
leave: { ? { opacity: 1, transform: 'translateY(0px)' }
opacity: 0, : {
transform: 'translateY(48px)', opacity: 0,
}, transform: 'translateY(48px)',
config: { },
duration: 200, });
},
},
onClose
);
const handleBackOrClose = useCallback(() => { const handleBackOrClose = useCallback(() => {
if (isEditingMessage) { if (isEditingMessage) {
@ -265,188 +262,189 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
{i18n('GroupV2--cannot-send')} {i18n('GroupV2--cannot-send')}
</ConfirmationDialog> </ConfirmationDialog>
)} )}
<ModalHost onEscape={handleBackOrClose} onClose={close}> <ModalHost
{renderAnimation( onEscape={handleBackOrClose}
<div className="module-ForwardMessageModal"> onClose={close}
<div overlayStyles={overlayStyles}
className={classNames('module-ForwardMessageModal__header', { >
'module-ForwardMessageModal__header--edit': isEditingMessage, <animated.div
})} className="module-ForwardMessageModal"
> style={modalStyles}
{isEditingMessage ? ( >
<button <div
aria-label={i18n('back')} className={classNames('module-ForwardMessageModal__header', {
className="module-ForwardMessageModal__header--back" 'module-ForwardMessageModal__header--edit': isEditingMessage,
onClick={() => setIsEditingMessage(false)} })}
type="button" >
>
&nbsp;
</button>
) : (
<button
aria-label={i18n('close')}
className="module-ForwardMessageModal__header--close"
onClick={close}
type="button"
/>
)}
<h1>{i18n('forwardMessage')}</h1>
</div>
{isEditingMessage ? ( {isEditingMessage ? (
<div className="module-ForwardMessageModal__main-body"> <button
{linkPreview ? ( aria-label={i18n('back')}
<div className="module-ForwardMessageModal--link-preview"> className="module-ForwardMessageModal__header--back"
<StagedLinkPreview onClick={() => setIsEditingMessage(false)}
date={linkPreview.date || null} type="button"
description={linkPreview.description || ''} >
domain={linkPreview.url} &nbsp;
i18n={i18n} </button>
image={linkPreview.image} ) : (
onClose={() => removeLinkPreview()} <button
title={linkPreview.title} aria-label={i18n('close')}
/> className="module-ForwardMessageModal__header--close"
</div> onClick={close}
) : null} type="button"
{attachmentsToForward && attachmentsToForward.length ? ( />
<AttachmentList )}
attachments={attachmentsToForward} <h1>{i18n('forwardMessage')}</h1>
</div>
{isEditingMessage ? (
<div className="module-ForwardMessageModal__main-body">
{linkPreview ? (
<div className="module-ForwardMessageModal--link-preview">
<StagedLinkPreview
date={linkPreview.date || null}
description={linkPreview.description || ''}
domain={linkPreview.url}
i18n={i18n} i18n={i18n}
onCloseAttachment={(attachment: AttachmentType) => { image={linkPreview.image}
const newAttachments = attachmentsToForward.filter( onClose={() => removeLinkPreview()}
currentAttachment => currentAttachment !== attachment title={linkPreview.title}
);
setAttachmentsToForward(newAttachments);
}}
/> />
) : null} </div>
<div className="module-ForwardMessageModal__text-edit-area"> ) : null}
<CompositionInput {attachmentsToForward && attachmentsToForward.length ? (
clearQuotedMessage={shouldNeverBeCalled} <AttachmentList
draftText={messageBodyText} attachments={attachmentsToForward}
getQuotedMessage={noop} i18n={i18n}
onCloseAttachment={(attachment: AttachmentType) => {
const newAttachments = attachmentsToForward.filter(
currentAttachment => currentAttachment !== attachment
);
setAttachmentsToForward(newAttachments);
}}
/>
) : null}
<div className="module-ForwardMessageModal__text-edit-area">
<CompositionInput
clearQuotedMessage={shouldNeverBeCalled}
draftText={messageBodyText}
getQuotedMessage={noop}
i18n={i18n}
inputApi={inputApiRef}
large
moduleClassName="module-ForwardMessageModal__input"
onEditorStateChange={(
messageText,
bodyRanges,
caretLocation
) => {
setMessageBodyText(messageText);
onEditorStateChange(messageText, bodyRanges, caretLocation);
}}
onPickEmoji={onPickEmoji}
onSubmit={forwardMessage}
onTextTooLong={onTextTooLong}
/>
<div className="module-ForwardMessageModal__emoji">
<EmojiButton
i18n={i18n} i18n={i18n}
inputApi={inputApiRef} onClose={focusTextEditInput}
large onPickEmoji={insertEmoji}
moduleClassName="module-ForwardMessageModal__input" onSetSkinTone={onSetSkinTone}
onEditorStateChange={( recentEmojis={recentEmojis}
messageText, skinTone={skinTone}
bodyRanges,
caretLocation
) => {
setMessageBodyText(messageText);
onEditorStateChange(
messageText,
bodyRanges,
caretLocation
);
}}
onPickEmoji={onPickEmoji}
onSubmit={forwardMessage}
onTextTooLong={onTextTooLong}
/> />
<div className="module-ForwardMessageModal__emoji">
<EmojiButton
i18n={i18n}
onClose={focusTextEditInput}
onPickEmoji={insertEmoji}
onSetSkinTone={onSetSkinTone}
recentEmojis={recentEmojis}
skinTone={skinTone}
/>
</div>
</div> </div>
</div> </div>
) : ( </div>
<div className="module-ForwardMessageModal__main-body"> ) : (
<SearchInput <div className="module-ForwardMessageModal__main-body">
disabled={candidateConversations.length === 0} <SearchInput
placeholder={i18n('contactSearchPlaceholder')} disabled={candidateConversations.length === 0}
onChange={event => { placeholder={i18n('contactSearchPlaceholder')}
setSearchTerm(event.target.value); onChange={event => {
}} setSearchTerm(event.target.value);
ref={inputRef} }}
value={searchTerm} ref={inputRef}
/> value={searchTerm}
{candidateConversations.length ? ( />
<Measure bounds> {candidateConversations.length ? (
{({ contentRect, measureRef }: MeasuredComponentProps) => { <Measure bounds>
// We disable this ESLint rule because we're capturing a bubbled {({ contentRect, measureRef }: MeasuredComponentProps) => {
// keydown event. See [this note in the jsx-a11y docs][0]. // 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 */ // [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
return ( /* eslint-disable jsx-a11y/no-static-element-interactions */
<div return (
className="module-ForwardMessageModal__list-wrapper" <div
ref={measureRef} className="module-ForwardMessageModal__list-wrapper"
> ref={measureRef}
<ConversationList >
dimensions={contentRect.bounds} <ConversationList
getRow={getRow} dimensions={contentRect.bounds}
i18n={i18n} getRow={getRow}
onClickArchiveButton={shouldNeverBeCalled} i18n={i18n}
onClickContactCheckbox={( onClickArchiveButton={shouldNeverBeCalled}
conversationId: string, onClickContactCheckbox={(
disabledReason: conversationId: string,
| undefined disabledReason:
| ContactCheckboxDisabledReason | undefined
) => { | ContactCheckboxDisabledReason
if ( ) => {
disabledReason !== if (
ContactCheckboxDisabledReason.MaximumContactsSelected disabledReason !==
) { ContactCheckboxDisabledReason.MaximumContactsSelected
toggleSelectedConversation(conversationId); ) {
} toggleSelectedConversation(conversationId);
}}
onSelectConversation={shouldNeverBeCalled}
renderMessageSearchResult={() => {
shouldNeverBeCalled();
return <div />;
}}
rowCount={rowCount}
shouldRecomputeRowHeights={false}
showChooseGroupMembers={shouldNeverBeCalled}
startNewConversationFromPhoneNumber={
shouldNeverBeCalled
} }
/> }}
</div> onSelectConversation={shouldNeverBeCalled}
); renderMessageSearchResult={() => {
/* eslint-enable jsx-a11y/no-static-element-interactions */ shouldNeverBeCalled();
}} return <div />;
</Measure> }}
) : ( rowCount={rowCount}
<div className="module-ForwardMessageModal__no-candidate-contacts"> shouldRecomputeRowHeights={false}
{i18n('noContactsFound')} showChooseGroupMembers={shouldNeverBeCalled}
</div> startNewConversationFromPhoneNumber={
)} shouldNeverBeCalled
</div> }
)} />
<div className="module-ForwardMessageModal__footer"> </div>
<div> );
{Boolean(selectedContacts.length) && /* eslint-enable jsx-a11y/no-static-element-interactions */
selectedContacts.map(contact => contact.title).join(', ')} }}
</div> </Measure>
<div> ) : (
{isEditingMessage || !isMessageEditable ? ( <div className="module-ForwardMessageModal__no-candidate-contacts">
<Button {i18n('noContactsFound')}
aria-label={i18n('ForwardMessageModal--continue')} </div>
className="module-ForwardMessageModal__send-button module-ForwardMessageModal__send-button--forward" )}
disabled={!canForwardMessage} </div>
onClick={forwardMessage} )}
/> <div className="module-ForwardMessageModal__footer">
) : ( <div>
<Button {Boolean(selectedContacts.length) &&
aria-label={i18n('forwardMessage')} selectedContacts.map(contact => contact.title).join(', ')}
className="module-ForwardMessageModal__send-button module-ForwardMessageModal__send-button--continue" </div>
disabled={!hasContactsSelected} <div>
onClick={() => setIsEditingMessage(true)} {isEditingMessage || !isMessageEditable ? (
/> <Button
)} aria-label={i18n('ForwardMessageModal--continue')}
</div> className="module-ForwardMessageModal__send-button module-ForwardMessageModal__send-button--forward"
disabled={!canForwardMessage}
onClick={forwardMessage}
/>
) : (
<Button
aria-label={i18n('forwardMessage')}
className="module-ForwardMessageModal__send-button module-ForwardMessageModal__send-button--continue"
disabled={!hasContactsSelected}
onClick={() => setIsEditingMessage(true)}
/>
)}
</div> </div>
</div> </div>
)} </animated.div>
</ModalHost> </ModalHost>
</> </>
); );

View file

@ -47,6 +47,12 @@ const INITIAL_IMAGE_TRANSFORM = {
scale: 1, scale: 1,
translateX: 0, translateX: 0,
translateY: 0, translateY: 0,
config: {
clamp: true,
friction: 20,
mass: 0.5,
tension: 350,
},
}; };
export function Lightbox({ export function Lightbox({

View file

@ -5,6 +5,7 @@ import React, { ReactElement, ReactNode, useRef, useState } from 'react';
import Measure, { ContentRect, MeasuredComponentProps } from 'react-measure'; import Measure, { ContentRect, MeasuredComponentProps } from 'react-measure';
import classNames from 'classnames'; import classNames from 'classnames';
import { noop } from 'lodash'; import { noop } from 'lodash';
import { animated } from '@react-spring/web';
import { LocalizerType } from '../types/Util'; import { LocalizerType } from '../types/Util';
import { ModalHost } from './ModalHost'; import { ModalHost } from './ModalHost';
@ -42,24 +43,22 @@ export function Modal({
title, title,
theme, theme,
}: Readonly<ModalPropsType>): ReactElement { }: Readonly<ModalPropsType>): ReactElement {
const { close, renderAnimation } = useAnimated( const { close, modalStyles, overlayStyles } = useAnimated(onClose, {
{ getFrom: () => ({ opacity: 0, transform: 'translateY(48px)' }),
from: { opacity: 0, transform: 'translateY(48px)' }, getTo: isOpen =>
enter: { opacity: 1, transform: 'translateY(0px)' }, isOpen
leave: { ? { opacity: 1, transform: 'translateY(0px)' }
opacity: 0, : { opacity: 0, transform: 'translateY(48px)' },
transform: 'translateY(48px)', });
},
config: {
duration: 200,
},
},
onClose
);
return ( return (
<ModalHost noMouseClose={noMouseClose} onClose={close} theme={theme}> <ModalHost
{renderAnimation( noMouseClose={noMouseClose}
onClose={close}
overlayStyles={overlayStyles}
theme={theme}
>
<animated.div style={modalStyles}>
<ModalWindow <ModalWindow
hasStickyButtons={hasStickyButtons} hasStickyButtons={hasStickyButtons}
hasXButton={hasXButton} hasXButton={hasXButton}
@ -70,7 +69,7 @@ export function Modal({
> >
{children} {children}
</ModalWindow> </ModalWindow>
)} </animated.div>
</ModalHost> </ModalHost>
); );
} }

View file

@ -5,20 +5,30 @@ import React, { useEffect } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import FocusTrap from 'focus-trap-react'; import FocusTrap from 'focus-trap-react';
import { SpringValues, animated } from '@react-spring/web';
import type { ModalConfigType } from '../hooks/useAnimated';
import { Theme, themeClassName } from '../util/theme'; import { Theme, themeClassName } from '../util/theme';
import { useEscapeHandling } from '../hooks/useEscapeHandling'; import { useEscapeHandling } from '../hooks/useEscapeHandling';
export type PropsType = { export type PropsType = {
readonly noMouseClose?: boolean;
readonly onEscape?: () => unknown;
readonly onClose: () => unknown;
readonly children: React.ReactElement; readonly children: React.ReactElement;
readonly noMouseClose?: boolean;
readonly onClose: () => unknown;
readonly onEscape?: () => unknown;
readonly overlayStyles?: SpringValues<ModalConfigType>;
readonly theme?: Theme; readonly theme?: Theme;
}; };
export const ModalHost = React.memo( export const ModalHost = React.memo(
({ onEscape, onClose, children, noMouseClose, theme }: PropsType) => { ({
children,
noMouseClose,
onClose,
onEscape,
theme,
overlayStyles,
}: PropsType) => {
const [root, setRoot] = React.useState<HTMLElement | null>(null); const [root, setRoot] = React.useState<HTMLElement | null>(null);
const [isMouseDown, setIsMouseDown] = React.useState(false); const [isMouseDown, setIsMouseDown] = React.useState(false);
@ -64,16 +74,18 @@ export const ModalHost = React.memo(
allowOutsideClick: false, allowOutsideClick: false,
}} }}
> >
<div <div>
role="presentation" <animated.div
className={classNames( role="presentation"
'module-modal-host__overlay', className={classNames(
theme ? themeClassName(theme) : undefined 'module-modal-host__overlay',
)} theme ? themeClassName(theme) : undefined
onMouseDown={noMouseClose ? undefined : handleMouseDown} )}
onMouseUp={noMouseClose ? undefined : handleMouseUp} onMouseDown={noMouseClose ? undefined : handleMouseDown}
> onMouseUp={noMouseClose ? undefined : handleMouseUp}
{children} style={overlayStyles}
/>
<div className="module-modal-host__container">{children}</div>
</div> </div>
</FocusTrap>, </FocusTrap>,
root root

View file

@ -1,37 +1,72 @@
// Copyright 2021 Signal Messenger, LLC // Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import React, { useState, ReactElement } from 'react'; import { useState } from 'react';
import { animated, useTransition, UseTransitionProps } from '@react-spring/web'; import {
import cubicBezier from 'bezier-easing'; SpringValues,
useChain,
useSpring,
useSpringRef,
} from '@react-spring/web';
export function useAnimated<Props extends Record<string, unknown>>( export type ModalConfigType = {
props: UseTransitionProps, opacity: number;
onClose: () => unknown transform?: string;
};
export function useAnimated(
onClose: () => unknown,
{
getFrom,
getTo,
}: {
getFrom: (isOpen: boolean) => ModalConfigType;
getTo: (isOpen: boolean) => ModalConfigType;
}
): { ): {
close: () => unknown; close: () => unknown;
renderAnimation: (children: ReactElement) => JSX.Element; modalStyles: SpringValues<ModalConfigType>;
overlayStyles: SpringValues<ModalConfigType>;
} { } {
const [isOpen, setIsOpen] = useState(true); const [isOpen, setIsOpen] = useState(true);
const transitions = useTransition<boolean, Props>(isOpen, { const modalRef = useSpringRef();
...props,
leave: { const modalStyles = useSpring({
...props.leave, from: getFrom(isOpen),
onRest: () => onClose(), to: getTo(isOpen),
onRest: () => {
if (!isOpen) {
onClose();
}
}, },
config: { config: {
duration: 200, clamp: true,
easing: cubicBezier(0.17, 0.17, 0, 1), friction: 20,
...props.config, mass: 0.5,
tension: 350,
}, },
ref: modalRef,
}); });
const overlayRef = useSpringRef();
const overlayStyles = useSpring({
from: { opacity: 0 },
to: { opacity: isOpen ? 1 : 0 },
config: {
clamp: true,
friction: 22,
tension: 360,
},
ref: overlayRef,
});
useChain(isOpen ? [overlayRef, modalRef] : [modalRef, overlayRef]);
return { return {
close: () => setIsOpen(false), close: () => setIsOpen(false),
renderAnimation: children => overlayStyles,
transitions((style, item) => modalStyles,
item ? <animated.div style={style}>{children}</animated.div> : null
),
}; };
} }

View file

@ -1671,50 +1671,50 @@
react-lifecycles-compat "^3.0.4" react-lifecycles-compat "^3.0.4"
warning "^3.0.0" warning "^3.0.0"
"@react-spring/animated@~9.2.0": "@react-spring/animated@~9.2.6-beta.0":
version "9.2.4" version "9.2.6"
resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.2.4.tgz#062ecc0fdfef89f2541a42d8500428b70035f879" resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.2.6.tgz#58f30fb75d8bfb7ccbc156cfd6b974a8f3dfd54e"
integrity sha512-AfV6ZM8pCCAT29GY5C8/1bOPjZrv/7kD0vedjiE/tEYvNDwg9GlscrvsTViWR2XykJoYrDfdkYArrldWpsCJ5g== integrity sha512-xjL6nmixYNDvnpTs1FFMsMfSC0tURwPCU3b2jWNriYGLfwZ7c/TcyaEZA7yiNnmdFnuR3f3Z27AqIgaFC083Cw==
dependencies: dependencies:
"@react-spring/shared" "~9.2.0" "@react-spring/shared" "~9.2.6-beta.0"
"@react-spring/types" "~9.2.0" "@react-spring/types" "~9.2.6-beta.0"
"@react-spring/core@~9.2.0": "@react-spring/core@~9.2.6-beta.0":
version "9.2.4" version "9.2.6"
resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.2.4.tgz#275a4a065e3a315a4f5fb28c9a6f62ce718c25d6" resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.2.6.tgz#ae22338fe55d070caf03abb4293b5519ba620d93"
integrity sha512-R+PwyfsjiuYCWqaTTfCpYpRmsP0h87RNm7uxC1Uxy7QAHUfHEm2sAHn+AdHPwq/MbVwDssVT8C5yf2WGcqiXGg== integrity sha512-uPHUxmu+w6mHJrfQTMtmGJ8iZEwiVxz9kH7dRyk69bkZJt9z+w0Oj3UF4J3VcECZsbm3HRhN2ogXSAaqGjwhQw==
dependencies: dependencies:
"@react-spring/animated" "~9.2.0" "@react-spring/animated" "~9.2.6-beta.0"
"@react-spring/shared" "~9.2.0" "@react-spring/shared" "~9.2.6-beta.0"
"@react-spring/types" "~9.2.0" "@react-spring/types" "~9.2.6-beta.0"
"@react-spring/rafz@~9.2.0": "@react-spring/rafz@~9.2.6-beta.0":
version "9.2.4" version "9.2.6"
resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.2.4.tgz#44793e9adc14dd0dcd1573d094368af11a89d73a" resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.2.6.tgz#d97484003875bf5fb5e6ec22dee97cc208363e48"
integrity sha512-SOKf9eue+vAX+DGo7kWYNl9i9J3gPUlQjifIcV9Bzw9h3i30wPOOP0TjS7iMG/kLp2cdHQYDNFte6nt23VAZkQ== integrity sha512-62SivLKEpo7EfHPkxO5J3g9Cr9LF6+1A1RVOMJhkcpEYtbdbmma/d63Xp8qpMPEpk7uuWxaTb6jjyxW33pW3sg==
"@react-spring/shared@~9.2.0": "@react-spring/shared@~9.2.6-beta.0":
version "9.2.4" version "9.2.6"
resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.2.4.tgz#f9cc66ac5308a77293330a18518e34121f4008c1" resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.2.6.tgz#2c84e62cc0cfbbbbeb5546acd46c1f4b248bc562"
integrity sha512-ZEr4l2BxmyFRUvRA2VCkPfCJii4E7cGkwbjmTBx1EmcGrOnde/V2eF5dxqCTY3k35QuCegkrWe0coRJVkh8q2Q== integrity sha512-Qrm9fopKG/RxZ3Rw+4euhrpnB3uXSyiON9skHbcBfmkkzagpkUR66MX1YLrhHw0UchcZuSDnXs0Lonzt1rpWag==
dependencies: dependencies:
"@react-spring/rafz" "~9.2.0" "@react-spring/rafz" "~9.2.6-beta.0"
"@react-spring/types" "~9.2.0" "@react-spring/types" "~9.2.6-beta.0"
"@react-spring/types@~9.2.0": "@react-spring/types@~9.2.6-beta.0":
version "9.2.4" version "9.2.6"
resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.2.4.tgz#2365ce9d761f548a9adcb2cd68714bf26765a5de" resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.2.6.tgz#f60722fcf9f8492ae16d0bdc47f0ea3c2a16d2cf"
integrity sha512-zHUXrWO8nweUN/ISjrjqU7GgXXvoEbFca1CgiE0TY0H/dqJb3l+Rhx8ecPVNYimzFg3ZZ1/T0egpLop8SOv4aA== integrity sha512-l7mCw182DtDMnCI8CB9orgTAEoFZRtdQ6aS6YeEAqYcy3nQZPmPggIHH9DxyLw7n7vBPRSzu9gCvUMgXKpTflg==
"@react-spring/web@9.2.4": "@react-spring/web@9.2.6":
version "9.2.4" version "9.2.6"
resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.2.4.tgz#c6d5464a954bfd0d7bc90117050f796a95ebfa08" resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.2.6.tgz#c4fba69e1b1b43bd1d6a62346530cfb07f2be09b"
integrity sha512-vtPvOalLFvuju/MDBtoSnCyt0xXSL6Amyv82fljOuWPl1yGd4M1WteijnYL9Zlriljl0a3oXcPunAVYTD9dbDQ== integrity sha512-0HkRsEYR/CO3Uw46FWDWaF2wg2rUXcWE2R9AoZXthEYLUn5w9uE1mf2Jel7BxBxWGQ73owkqSQv+klA1Hb+ViQ==
dependencies: dependencies:
"@react-spring/animated" "~9.2.0" "@react-spring/animated" "~9.2.6-beta.0"
"@react-spring/core" "~9.2.0" "@react-spring/core" "~9.2.6-beta.0"
"@react-spring/shared" "~9.2.0" "@react-spring/shared" "~9.2.6-beta.0"
"@react-spring/types" "~9.2.0" "@react-spring/types" "~9.2.6-beta.0"
"@signalapp/signal-client@0.9.5": "@signalapp/signal-client@0.9.5":
version "0.9.5" version "0.9.5"