Adds open/close animations to dialogs and modals
This commit is contained in:
parent
fc066e05df
commit
b6cfe0933d
10 changed files with 635 additions and 224 deletions
|
@ -9,6 +9,30 @@ Signal Desktop makes use of the following open source projects.
|
||||||
|
|
||||||
License: MIT
|
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
|
## @sindresorhus/is
|
||||||
|
|
||||||
MIT License
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
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
|
## blob-util
|
||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@popperjs/core": "2.9.2",
|
"@popperjs/core": "2.9.2",
|
||||||
|
"@react-spring/web": "9.2.4",
|
||||||
"@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",
|
||||||
|
@ -80,6 +81,7 @@
|
||||||
"axe-core": "4.1.4",
|
"axe-core": "4.1.4",
|
||||||
"backbone": "1.4.0",
|
"backbone": "1.4.0",
|
||||||
"better-sqlite3": "https://github.com/signalapp/better-sqlite3#2fa02d2484e9f9a10df5ac7ea4617fb2dff30006",
|
"better-sqlite3": "https://github.com/signalapp/better-sqlite3#2fa02d2484e9f9a10df5ac7ea4617fb2dff30006",
|
||||||
|
"bezier-easing": "2.1.0",
|
||||||
"blob-util": "2.0.2",
|
"blob-util": "2.0.2",
|
||||||
"blueimp-load-image": "5.14.0",
|
"blueimp-load-image": "5.14.0",
|
||||||
"blurhash": "1.1.3",
|
"blurhash": "1.1.3",
|
||||||
|
@ -212,7 +214,7 @@
|
||||||
"@types/pino": "6.3.6",
|
"@types/pino": "6.3.6",
|
||||||
"@types/pino-multi-stream": "5.1.0",
|
"@types/pino-multi-stream": "5.1.0",
|
||||||
"@types/quill": "1.3.10",
|
"@types/quill": "1.3.10",
|
||||||
"@types/react": "16.8.5",
|
"@types/react": "16.8.6",
|
||||||
"@types/react-dom": "16.8.2",
|
"@types/react-dom": "16.8.2",
|
||||||
"@types/react-measure": "2.0.5",
|
"@types/react-measure": "2.0.5",
|
||||||
"@types/react-redux": "7.1.2",
|
"@types/react-redux": "7.1.2",
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import React, { ComponentProps, useEffect } from 'react';
|
import React, { ComponentProps, useEffect } from 'react';
|
||||||
|
import { Globals } from '@react-spring/web';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { AppViewType } from '../state/ducks/app';
|
import { AppViewType } from '../state/ducks/app';
|
||||||
|
@ -10,6 +11,7 @@ import { Install } from './Install';
|
||||||
import { StandaloneRegistration } from './StandaloneRegistration';
|
import { StandaloneRegistration } from './StandaloneRegistration';
|
||||||
import { ThemeType } from '../types/Util';
|
import { ThemeType } from '../types/Util';
|
||||||
import { usePageVisibility } from '../hooks/usePageVisibility';
|
import { usePageVisibility } from '../hooks/usePageVisibility';
|
||||||
|
import { useReducedMotion } from '../hooks/useReducedMotion';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
appView: AppViewType;
|
appView: AppViewType;
|
||||||
|
@ -84,6 +86,14 @@ export const App = ({
|
||||||
document.body.classList.toggle('page-is-visible', isPageVisible);
|
document.body.classList.toggle('page-is-visible', isPageVisible);
|
||||||
}, [isPageVisible]);
|
}, [isPageVisible]);
|
||||||
|
|
||||||
|
// A11y settings for react-spring
|
||||||
|
const prefersReducedMotion = useReducedMotion();
|
||||||
|
useEffect(() => {
|
||||||
|
Globals.assign({
|
||||||
|
skipAnimation: prefersReducedMotion,
|
||||||
|
});
|
||||||
|
}, [prefersReducedMotion]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
// Copyright 2019-2021 Signal Messenger, LLC
|
// Copyright 2019-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import * as React from 'react';
|
import React, { MouseEvent, useCallback } from 'react';
|
||||||
import { Button, ButtonVariant } from './Button';
|
import { Button, ButtonVariant } from './Button';
|
||||||
import { LocalizerType } from '../types/Util';
|
import { LocalizerType } from '../types/Util';
|
||||||
import { Modal } from './Modal';
|
import { ModalHost } from './ModalHost';
|
||||||
|
import { Modal, ModalWindow } from './Modal';
|
||||||
import { Theme } from '../util/theme';
|
import { Theme } from '../util/theme';
|
||||||
|
import { useAnimated } from '../hooks/useAnimated';
|
||||||
|
|
||||||
export type ActionSpec = {
|
export type ActionSpec = {
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -61,15 +63,27 @@ export const ConfirmationDialog = React.memo(
|
||||||
title,
|
title,
|
||||||
hasXButton,
|
hasXButton,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
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) {
|
if (onCancel) {
|
||||||
onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
onClose();
|
close();
|
||||||
}, [onCancel, onClose]);
|
}, [close, onCancel]);
|
||||||
|
|
||||||
const handleCancel = React.useCallback(
|
const handleCancel = useCallback(
|
||||||
(e: React.MouseEvent) => {
|
(e: MouseEvent) => {
|
||||||
if (e.target === e.currentTarget) {
|
if (e.target === e.currentTarget) {
|
||||||
cancelAndClose();
|
cancelAndClose();
|
||||||
}
|
}
|
||||||
|
@ -80,40 +94,43 @@ export const ConfirmationDialog = React.memo(
|
||||||
const hasActions = Boolean(actions.length);
|
const hasActions = Boolean(actions.length);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<ModalHost onClose={close} theme={theme}>
|
||||||
moduleClassName={moduleClassName}
|
{renderAnimation(
|
||||||
i18n={i18n}
|
<ModalWindow
|
||||||
onClose={cancelAndClose}
|
hasXButton={hasXButton}
|
||||||
title={title}
|
i18n={i18n}
|
||||||
theme={theme}
|
moduleClassName={moduleClassName}
|
||||||
hasXButton={hasXButton}
|
onClose={cancelAndClose}
|
||||||
>
|
title={title}
|
||||||
{children}
|
|
||||||
<Modal.ButtonFooter>
|
|
||||||
<Button
|
|
||||||
onClick={handleCancel}
|
|
||||||
ref={focusRef}
|
|
||||||
variant={
|
|
||||||
hasActions ? ButtonVariant.Secondary : ButtonVariant.Primary
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{cancelText || i18n('confirmation-dialog--Cancel')}
|
{children}
|
||||||
</Button>
|
<Modal.ButtonFooter>
|
||||||
{actions.map((action, i) => (
|
<Button
|
||||||
<Button
|
onClick={handleCancel}
|
||||||
key={action.text}
|
ref={focusRef}
|
||||||
onClick={() => {
|
variant={
|
||||||
action.action();
|
hasActions ? ButtonVariant.Secondary : ButtonVariant.Primary
|
||||||
onClose();
|
}
|
||||||
}}
|
>
|
||||||
data-action={i}
|
{cancelText || i18n('confirmation-dialog--Cancel')}
|
||||||
variant={getButtonVariant(action.style)}
|
</Button>
|
||||||
>
|
{actions.map((action, i) => (
|
||||||
{action.text}
|
<Button
|
||||||
</Button>
|
key={action.text}
|
||||||
))}
|
onClick={() => {
|
||||||
</Modal.ButtonFooter>
|
action.action();
|
||||||
</Modal>
|
close();
|
||||||
|
}}
|
||||||
|
data-action={i}
|
||||||
|
variant={getButtonVariant(action.style)}
|
||||||
|
>
|
||||||
|
{action.text}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</Modal.ButtonFooter>
|
||||||
|
</ModalWindow>
|
||||||
|
)}
|
||||||
|
</ModalHost>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,6 +30,7 @@ import { SearchInput } from './SearchInput';
|
||||||
import { StagedLinkPreview } from './conversation/StagedLinkPreview';
|
import { StagedLinkPreview } from './conversation/StagedLinkPreview';
|
||||||
import { assert } from '../util/assert';
|
import { assert } from '../util/assert';
|
||||||
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
|
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
|
||||||
|
import { useAnimated } from '../hooks/useAnimated';
|
||||||
|
|
||||||
export type DataPropsType = {
|
export type DataPropsType = {
|
||||||
attachments?: Array<AttachmentType>;
|
attachments?: Array<AttachmentType>;
|
||||||
|
@ -198,13 +199,28 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
[contactLookup, selectedContacts, setSelectedContacts]
|
[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(() => {
|
const handleBackOrClose = useCallback(() => {
|
||||||
if (isEditingMessage) {
|
if (isEditingMessage) {
|
||||||
setIsEditingMessage(false);
|
setIsEditingMessage(false);
|
||||||
} else {
|
} else {
|
||||||
onClose();
|
close();
|
||||||
}
|
}
|
||||||
}, [isEditingMessage, onClose, setIsEditingMessage]);
|
}, [isEditingMessage, close, setIsEditingMessage]);
|
||||||
|
|
||||||
const rowCount = filteredConversations.length;
|
const rowCount = filteredConversations.length;
|
||||||
const getRow = (index: number): undefined | Row => {
|
const getRow = (index: number): undefined | Row => {
|
||||||
|
@ -249,182 +265,188 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
{i18n('GroupV2--cannot-send')}
|
{i18n('GroupV2--cannot-send')}
|
||||||
</ConfirmationDialog>
|
</ConfirmationDialog>
|
||||||
)}
|
)}
|
||||||
<ModalHost onEscape={handleBackOrClose} onClose={onClose}>
|
<ModalHost onEscape={handleBackOrClose} onClose={close}>
|
||||||
<div className="module-ForwardMessageModal">
|
{renderAnimation(
|
||||||
<div
|
<div className="module-ForwardMessageModal">
|
||||||
className={classNames('module-ForwardMessageModal__header', {
|
<div
|
||||||
'module-ForwardMessageModal__header--edit': isEditingMessage,
|
className={classNames('module-ForwardMessageModal__header', {
|
||||||
})}
|
'module-ForwardMessageModal__header--edit': isEditingMessage,
|
||||||
>
|
})}
|
||||||
|
>
|
||||||
|
{isEditingMessage ? (
|
||||||
|
<button
|
||||||
|
aria-label={i18n('back')}
|
||||||
|
className="module-ForwardMessageModal__header--back"
|
||||||
|
onClick={() => setIsEditingMessage(false)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
aria-label={i18n('close')}
|
||||||
|
className="module-ForwardMessageModal__header--close"
|
||||||
|
onClick={close}
|
||||||
|
type="button"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<h1>{i18n('forwardMessage')}</h1>
|
||||||
|
</div>
|
||||||
{isEditingMessage ? (
|
{isEditingMessage ? (
|
||||||
<button
|
<div className="module-ForwardMessageModal__main-body">
|
||||||
aria-label={i18n('back')}
|
{linkPreview ? (
|
||||||
className="module-ForwardMessageModal__header--back"
|
<div className="module-ForwardMessageModal--link-preview">
|
||||||
onClick={() => setIsEditingMessage(false)}
|
<StagedLinkPreview
|
||||||
type="button"
|
date={linkPreview.date || null}
|
||||||
>
|
description={linkPreview.description || ''}
|
||||||
|
domain={linkPreview.url}
|
||||||
</button>
|
i18n={i18n}
|
||||||
) : (
|
image={linkPreview.image}
|
||||||
<button
|
onClose={() => removeLinkPreview()}
|
||||||
aria-label={i18n('close')}
|
title={linkPreview.title}
|
||||||
className="module-ForwardMessageModal__header--close"
|
/>
|
||||||
onClick={onClose}
|
</div>
|
||||||
type="button"
|
) : null}
|
||||||
/>
|
{attachmentsToForward && attachmentsToForward.length ? (
|
||||||
)}
|
<AttachmentList
|
||||||
<h1>{i18n('forwardMessage')}</h1>
|
attachments={attachmentsToForward}
|
||||||
</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}
|
||||||
image={linkPreview.image}
|
onCloseAttachment={(attachment: AttachmentType) => {
|
||||||
onClose={() => removeLinkPreview()}
|
const newAttachments = attachmentsToForward.filter(
|
||||||
title={linkPreview.title}
|
currentAttachment => currentAttachment !== attachment
|
||||||
|
);
|
||||||
|
setAttachmentsToForward(newAttachments);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : null}
|
||||||
) : null}
|
<div className="module-ForwardMessageModal__text-edit-area">
|
||||||
{attachmentsToForward && attachmentsToForward.length ? (
|
<CompositionInput
|
||||||
<AttachmentList
|
clearQuotedMessage={shouldNeverBeCalled}
|
||||||
attachments={attachmentsToForward}
|
draftText={messageBodyText}
|
||||||
i18n={i18n}
|
getQuotedMessage={noop}
|
||||||
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}
|
||||||
onClose={focusTextEditInput}
|
inputApi={inputApiRef}
|
||||||
onPickEmoji={insertEmoji}
|
large
|
||||||
onSetSkinTone={onSetSkinTone}
|
moduleClassName="module-ForwardMessageModal__input"
|
||||||
recentEmojis={recentEmojis}
|
onEditorStateChange={(
|
||||||
skinTone={skinTone}
|
messageText,
|
||||||
|
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">
|
||||||
<div className="module-ForwardMessageModal__main-body">
|
<SearchInput
|
||||||
<SearchInput
|
disabled={candidateConversations.length === 0}
|
||||||
disabled={candidateConversations.length === 0}
|
placeholder={i18n('contactSearchPlaceholder')}
|
||||||
placeholder={i18n('contactSearchPlaceholder')}
|
onChange={event => {
|
||||||
onChange={event => {
|
setSearchTerm(event.target.value);
|
||||||
setSearchTerm(event.target.value);
|
|
||||||
}}
|
|
||||||
ref={inputRef}
|
|
||||||
value={searchTerm}
|
|
||||||
/>
|
|
||||||
{candidateConversations.length ? (
|
|
||||||
<Measure bounds>
|
|
||||||
{({ 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 (
|
|
||||||
<div
|
|
||||||
className="module-ForwardMessageModal__list-wrapper"
|
|
||||||
ref={measureRef}
|
|
||||||
>
|
|
||||||
<ConversationList
|
|
||||||
dimensions={contentRect.bounds}
|
|
||||||
getRow={getRow}
|
|
||||||
i18n={i18n}
|
|
||||||
onClickArchiveButton={shouldNeverBeCalled}
|
|
||||||
onClickContactCheckbox={(
|
|
||||||
conversationId: string,
|
|
||||||
disabledReason:
|
|
||||||
| undefined
|
|
||||||
| ContactCheckboxDisabledReason
|
|
||||||
) => {
|
|
||||||
if (
|
|
||||||
disabledReason !==
|
|
||||||
ContactCheckboxDisabledReason.MaximumContactsSelected
|
|
||||||
) {
|
|
||||||
toggleSelectedConversation(conversationId);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onSelectConversation={shouldNeverBeCalled}
|
|
||||||
renderMessageSearchResult={() => {
|
|
||||||
shouldNeverBeCalled();
|
|
||||||
return <div />;
|
|
||||||
}}
|
|
||||||
rowCount={rowCount}
|
|
||||||
shouldRecomputeRowHeights={false}
|
|
||||||
showChooseGroupMembers={shouldNeverBeCalled}
|
|
||||||
startNewConversationFromPhoneNumber={
|
|
||||||
shouldNeverBeCalled
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
/* eslint-enable jsx-a11y/no-static-element-interactions */
|
|
||||||
}}
|
}}
|
||||||
</Measure>
|
ref={inputRef}
|
||||||
) : (
|
value={searchTerm}
|
||||||
<div className="module-ForwardMessageModal__no-candidate-contacts">
|
|
||||||
{i18n('noContactsFound')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="module-ForwardMessageModal__footer">
|
|
||||||
<div>
|
|
||||||
{Boolean(selectedContacts.length) &&
|
|
||||||
selectedContacts.map(contact => contact.title).join(', ')}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{isEditingMessage || !isMessageEditable ? (
|
|
||||||
<Button
|
|
||||||
aria-label={i18n('ForwardMessageModal--continue')}
|
|
||||||
className="module-ForwardMessageModal__send-button module-ForwardMessageModal__send-button--forward"
|
|
||||||
disabled={!canForwardMessage}
|
|
||||||
onClick={forwardMessage}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
{candidateConversations.length ? (
|
||||||
<Button
|
<Measure bounds>
|
||||||
aria-label={i18n('forwardMessage')}
|
{({ contentRect, measureRef }: MeasuredComponentProps) => {
|
||||||
className="module-ForwardMessageModal__send-button module-ForwardMessageModal__send-button--continue"
|
// We disable this ESLint rule because we're capturing a bubbled
|
||||||
disabled={!hasContactsSelected}
|
// keydown event. See [this note in the jsx-a11y docs][0].
|
||||||
onClick={() => setIsEditingMessage(true)}
|
//
|
||||||
/>
|
// [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 (
|
||||||
|
<div
|
||||||
|
className="module-ForwardMessageModal__list-wrapper"
|
||||||
|
ref={measureRef}
|
||||||
|
>
|
||||||
|
<ConversationList
|
||||||
|
dimensions={contentRect.bounds}
|
||||||
|
getRow={getRow}
|
||||||
|
i18n={i18n}
|
||||||
|
onClickArchiveButton={shouldNeverBeCalled}
|
||||||
|
onClickContactCheckbox={(
|
||||||
|
conversationId: string,
|
||||||
|
disabledReason:
|
||||||
|
| undefined
|
||||||
|
| ContactCheckboxDisabledReason
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
disabledReason !==
|
||||||
|
ContactCheckboxDisabledReason.MaximumContactsSelected
|
||||||
|
) {
|
||||||
|
toggleSelectedConversation(conversationId);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onSelectConversation={shouldNeverBeCalled}
|
||||||
|
renderMessageSearchResult={() => {
|
||||||
|
shouldNeverBeCalled();
|
||||||
|
return <div />;
|
||||||
|
}}
|
||||||
|
rowCount={rowCount}
|
||||||
|
shouldRecomputeRowHeights={false}
|
||||||
|
showChooseGroupMembers={shouldNeverBeCalled}
|
||||||
|
startNewConversationFromPhoneNumber={
|
||||||
|
shouldNeverBeCalled
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
/* eslint-enable jsx-a11y/no-static-element-interactions */
|
||||||
|
}}
|
||||||
|
</Measure>
|
||||||
|
) : (
|
||||||
|
<div className="module-ForwardMessageModal__no-candidate-contacts">
|
||||||
|
{i18n('noContactsFound')}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="module-ForwardMessageModal__footer">
|
||||||
|
<div>
|
||||||
|
{Boolean(selectedContacts.length) &&
|
||||||
|
selectedContacts.map(contact => contact.title).join(', ')}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{isEditingMessage || !isMessageEditable ? (
|
||||||
|
<Button
|
||||||
|
aria-label={i18n('ForwardMessageModal--continue')}
|
||||||
|
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>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</ModalHost>
|
</ModalHost>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// 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, { useRef, useState, ReactElement, ReactNode } from 'react';
|
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';
|
||||||
|
@ -10,6 +10,7 @@ import { LocalizerType } from '../types/Util';
|
||||||
import { ModalHost } from './ModalHost';
|
import { ModalHost } from './ModalHost';
|
||||||
import { Theme } from '../util/theme';
|
import { Theme } from '../util/theme';
|
||||||
import { getClassNamesFor } from '../util/getClassNamesFor';
|
import { getClassNamesFor } from '../util/getClassNamesFor';
|
||||||
|
import { useAnimated } from '../hooks/useAnimated';
|
||||||
import { useHasWrapped } from '../hooks/useHasWrapped';
|
import { useHasWrapped } from '../hooks/useHasWrapped';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
|
@ -18,9 +19,12 @@ type PropsType = {
|
||||||
hasXButton?: boolean;
|
hasXButton?: boolean;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
moduleClassName?: string;
|
moduleClassName?: string;
|
||||||
noMouseClose?: boolean;
|
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
title?: ReactNode;
|
title?: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ModalPropsType = PropsType & {
|
||||||
|
noMouseClose?: boolean;
|
||||||
theme?: Theme;
|
theme?: Theme;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,8 +40,51 @@ export function Modal({
|
||||||
onClose = noop,
|
onClose = noop,
|
||||||
title,
|
title,
|
||||||
theme,
|
theme,
|
||||||
}: Readonly<PropsType>): ReactElement {
|
}: Readonly<ModalPropsType>): 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 (
|
||||||
|
<ModalHost noMouseClose={noMouseClose} onClose={close} theme={theme}>
|
||||||
|
{renderAnimation(
|
||||||
|
<ModalWindow
|
||||||
|
hasStickyButtons={hasStickyButtons}
|
||||||
|
hasXButton={hasXButton}
|
||||||
|
i18n={i18n}
|
||||||
|
moduleClassName={moduleClassName}
|
||||||
|
onClose={close}
|
||||||
|
title={title}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ModalWindow>
|
||||||
|
)}
|
||||||
|
</ModalHost>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModalWindow({
|
||||||
|
children,
|
||||||
|
hasStickyButtons,
|
||||||
|
hasXButton,
|
||||||
|
i18n,
|
||||||
|
moduleClassName,
|
||||||
|
onClose = noop,
|
||||||
|
title,
|
||||||
|
}: Readonly<PropsType>): JSX.Element {
|
||||||
const modalRef = useRef<HTMLDivElement | null>(null);
|
const modalRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const bodyRef = useRef<HTMLDivElement | null>(null);
|
const bodyRef = useRef<HTMLDivElement | null>(null);
|
||||||
const [scrolled, setScrolled] = useState(false);
|
const [scrolled, setScrolled] = useState(false);
|
||||||
const [hasOverflow, setHasOverflow] = useState(false);
|
const [hasOverflow, setHasOverflow] = useState(false);
|
||||||
|
@ -56,10 +103,10 @@ export function Modal({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalHost noMouseClose={noMouseClose} onClose={onClose} theme={theme}>
|
<>
|
||||||
{/* We don't want the click event to propagate to its container node. */}
|
{/* We don't want the click event to propagate to its container node. */}
|
||||||
{/* eslint-disable jsx-a11y/no-static-element-interactions */}
|
{/* eslint-disable-next-line max-len */}
|
||||||
{/* eslint-disable jsx-a11y/click-events-have-key-events */}
|
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */}
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
getClassName(''),
|
getClassName(''),
|
||||||
|
@ -71,8 +118,6 @@ export function Modal({
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* eslint-enable jsx-a11y/no-static-element-interactions */}
|
|
||||||
{/* eslint-enable jsx-a11y/click-events-have-key-events */}
|
|
||||||
{hasHeader && (
|
{hasHeader && (
|
||||||
<div className={getClassName('__header')}>
|
<div className={getClassName('__header')}>
|
||||||
{hasXButton && (
|
{hasXButton && (
|
||||||
|
@ -81,9 +126,7 @@ export function Modal({
|
||||||
type="button"
|
type="button"
|
||||||
className={getClassName('__close-button')}
|
className={getClassName('__close-button')}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => {
|
onClick={onClose}
|
||||||
onClose();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{title && (
|
{title && (
|
||||||
|
@ -122,7 +165,7 @@ export function Modal({
|
||||||
)}
|
)}
|
||||||
</Measure>
|
</Measure>
|
||||||
</div>
|
</div>
|
||||||
</ModalHost>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
37
ts/hooks/useAnimated.tsx
Normal file
37
ts/hooks/useAnimated.tsx
Normal file
|
@ -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 extends Record<string, unknown>>(
|
||||||
|
props: UseTransitionProps,
|
||||||
|
onClose: () => unknown
|
||||||
|
): {
|
||||||
|
close: () => unknown;
|
||||||
|
renderAnimation: (children: ReactElement) => JSX.Element;
|
||||||
|
} {
|
||||||
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
|
||||||
|
const transitions = useTransition<boolean, Props>(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 ? <animated.div style={style}>{children}</animated.div> : null
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
34
ts/hooks/useReducedMotion.ts
Normal file
34
ts/hooks/useReducedMotion.ts
Normal file
|
@ -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 <https://github.com/infiniteluke/react-reduce-motion>.
|
||||||
|
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;
|
||||||
|
}
|
|
@ -240,6 +240,153 @@
|
||||||
"updated": "2018-09-18T19:19:27.699Z",
|
"updated": "2018-09-18T19:19:27.699Z",
|
||||||
"reasonDetail": "What's being eval'd is a static string"
|
"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",
|
"rule": "DOM-innerHTML",
|
||||||
"path": "node_modules/@sindresorhus/is/dist/index.js",
|
"path": "node_modules/@sindresorhus/is/dist/index.js",
|
||||||
|
|
58
yarn.lock
58
yarn.lock
|
@ -1671,6 +1671,51 @@
|
||||||
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":
|
||||||
|
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":
|
"@signalapp/signal-client@0.9.5":
|
||||||
version "0.9.5"
|
version "0.9.5"
|
||||||
resolved "https://registry.yarnpkg.com/@signalapp/signal-client/-/signal-client-0.9.5.tgz#b133305aa39c00ceae8743316efd0cc0bfe02e2d"
|
resolved "https://registry.yarnpkg.com/@signalapp/signal-client/-/signal-client-0.9.5.tgz#b133305aa39c00ceae8743316efd0cc0bfe02e2d"
|
||||||
|
@ -2844,10 +2889,10 @@
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react@*", "@types/react@16.8.5":
|
"@types/react@*", "@types/react@16.8.6":
|
||||||
version "16.8.5"
|
version "16.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.5.tgz#03b9a6597bc20f6eaaed43f377a160f7e41c2b90"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.6.tgz#fa1de3fe56cc9b6afeddc73d093d7f30fd5e31cc"
|
||||||
integrity sha512-8LRySaaSJVLNZb2dbOGvGmzn88cbAfrgDpuWy+6lLgQ0OJFgHHvyuaCX4/7ikqJlpmCPf4uazJAZcfTQRdJqdQ==
|
integrity sha512-bN9qDjEMltmHrl0PZRI4IF2AbB7V5UlRfG+OOduckVnRQ4VzXVSzy/1eLAh778IEqhTnW0mmgL9yShfinNverA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/prop-types" "*"
|
"@types/prop-types" "*"
|
||||||
csstype "^2.2.0"
|
csstype "^2.2.0"
|
||||||
|
@ -4732,6 +4777,11 @@ bcrypt-pbkdf@^1.0.0:
|
||||||
bindings "^1.5.0"
|
bindings "^1.5.0"
|
||||||
tar "^6.1.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:
|
big.js@^5.2.2:
|
||||||
version "5.2.2"
|
version "5.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue