) {
return (
-
+
{children}
-
+
)
}
diff --git a/apps/web/src/components/ConfirmSwapModal/index.tsx b/apps/web/src/components/ConfirmSwapModal/index.tsx
index 0d5d5e9c6cf..0ae25a3b899 100644
--- a/apps/web/src/components/ConfirmSwapModal/index.tsx
+++ b/apps/web/src/components/ConfirmSwapModal/index.tsx
@@ -5,6 +5,7 @@ import { SwapHead } from 'components/ConfirmSwapModal/Head'
import { SwapModal } from 'components/ConfirmSwapModal/Modal'
import { Pending } from 'components/ConfirmSwapModal/Pending'
import SwapProgressIndicator from 'components/ConfirmSwapModal/ProgressIndicator'
+import { MODAL_TRANSITION_DURATION } from 'components/Modal'
import { AutoColumn } from 'components/deprecated/Column'
import { SwapDetails } from 'components/swap/SwapDetails'
import { SwapPreview } from 'components/swap/SwapPreview'
@@ -185,7 +186,7 @@ export function ConfirmSwapModal({
setTimeout(() => {
// Reset local state after the modal dismiss animation finishes, to avoid UI flicker as it dismisses
onCancel()
- }, 200)
+ }, MODAL_TRANSITION_DURATION)
// Popups are suppressed when modal is open; re-enable them on dismissal
unsuppressPopups()
}, [confirmModalState, doesTradeDiffer, onCancel, onDismiss, priceUpdate, unsuppressPopups, trade])
diff --git a/apps/web/src/components/ConnectedAccountBlocked.tsx b/apps/web/src/components/ConnectedAccountBlocked.tsx
index f6aa63d4eef..8581f54a42c 100644
--- a/apps/web/src/components/ConnectedAccountBlocked.tsx
+++ b/apps/web/src/components/ConnectedAccountBlocked.tsx
@@ -1,8 +1,9 @@
import Column from 'components/deprecated/Column'
+import Modal from 'components/Modal'
import styled, { useTheme } from 'lib/styled-components'
import { Slash } from 'react-feather'
import { CopyHelper, ExternalLink, ThemedText } from 'theme/components'
-import { AdaptiveWebModal, Flex, Text } from 'ui/src'
+import { Flex, Text } from 'ui/src'
import { Trans } from 'uniswap/src/i18n'
const ContentWrapper = styled(Column)`
@@ -19,7 +20,7 @@ interface ConnectedAccountBlockedProps {
export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedProps) {
const theme = useTheme()
return (
-
+
@@ -55,6 +56,6 @@ export default function ConnectedAccountBlocked(props: ConnectedAccountBlockedPr
/>
-
+
)
}
diff --git a/apps/web/src/components/Dialog/Dialog.tsx b/apps/web/src/components/Dialog/Dialog.tsx
index 007a7fc710e..03d0e7b095f 100644
--- a/apps/web/src/components/Dialog/Dialog.tsx
+++ b/apps/web/src/components/Dialog/Dialog.tsx
@@ -1,4 +1,5 @@
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button/buttons'
+import Modal from 'components/Modal'
import { GetHelpHeader } from 'components/Modal/GetHelpHeader'
import { ColumnCenter } from 'components/deprecated/Column'
import Row from 'components/deprecated/Row'
@@ -6,11 +7,11 @@ import styled, { DefaultTheme } from 'lib/styled-components'
import { ReactNode } from 'react'
import { Gap } from 'theme'
import { ThemedText } from 'theme/components'
-import { AdaptiveWebModal } from 'ui/src'
export const Container = styled(ColumnCenter)`
background-color: ${({ theme }) => theme.surface1};
- border-radius: 16px;
+ outline: 1px solid ${({ theme }) => theme.surface3};
+ border-radius: 20px;
padding: 16px 24px 24px 24px;
width: 100%;
`
@@ -160,11 +161,11 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }:
*/
export function Dialog(props: DialogProps) {
return (
-
+
-
+
)
}
diff --git a/apps/web/src/components/Dialog/__snapshots__/Dialog.test.tsx.snap b/apps/web/src/components/Dialog/__snapshots__/Dialog.test.tsx.snap
index f8fed50a6a7..c3134d9478f 100644
--- a/apps/web/src/components/Dialog/__snapshots__/Dialog.test.tsx.snap
+++ b/apps/web/src/components/Dialog/__snapshots__/Dialog.test.tsx.snap
@@ -1,13 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`
renders different button types 1`] = `
-.c6 {
+.c8 {
box-sizing: border-box;
margin: 0;
min-width: 0;
}
-.c7 {
+.c9 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
@@ -25,7 +25,7 @@ exports[`
renders different button types 1`] = `
gap: 4px;
}
-.c16 {
+.c18 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
@@ -43,7 +43,7 @@ exports[`
renders different button types 1`] = `
gap: 12px;
}
-.c12 {
+.c14 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
@@ -51,7 +51,7 @@ exports[`
renders different button types 1`] = `
letter-spacing: -0.01em;
}
-.c14 {
+.c16 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
@@ -59,7 +59,7 @@ exports[`
renders different button types 1`] = `
letter-spacing: -0.01em;
}
-.c8 {
+.c10 {
color: #222222;
cursor: pointer;
-webkit-text-decoration: none;
@@ -69,15 +69,15 @@ exports[`
renders different button types 1`] = `
transition-duration: 125ms;
}
-.c8:hover {
+.c10:hover {
opacity: 0.6;
}
-.c8:active {
+.c10:active {
opacity: 0.4;
}
-.c4 {
+.c6 {
-webkit-text-decoration: none;
text-decoration: none;
cursor: pointer;
@@ -88,15 +88,15 @@ exports[`
renders different button types 1`] = `
font-weight: 500;
}
-.c4:hover {
+.c6:hover {
opacity: 0.6;
}
-.c4:active {
+.c6:active {
opacity: 0.4;
}
-.c20 {
+.c22 {
background-color: transparent;
bottom: 0;
border-radius: inherit;
@@ -110,7 +110,7 @@ exports[`
renders different button types 1`] = `
width: 100%;
}
-.c17 {
+.c19 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
@@ -145,30 +145,30 @@ exports[`
renders different button types 1`] = `
user-select: none;
}
-.c17:active .c19 {
+.c19:active .c21 {
background-color: #B8C0DC3d;
}
-.c17:focus .c19 {
+.c19:focus .c21 {
background-color: #B8C0DC3d;
}
-.c17:hover .c19 {
+.c19:hover .c21 {
background-color: #98A1C014;
}
-.c17:disabled {
+.c19:disabled {
cursor: default;
opacity: 0.6;
}
-.c17:disabled:active .c19,
-.c17:disabled:focus .c19,
-.c17:disabled:hover .c19 {
+.c19:disabled:active .c21,
+.c19:disabled:focus .c21,
+.c19:disabled:hover .c21 {
background-color: transparent;
}
-.c21 {
+.c23 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
@@ -203,30 +203,73 @@ exports[`
renders different button types 1`] = `
user-select: none;
}
-.c21:active .c19 {
+.c23:active .c21 {
background-color: #B8C0DC3d;
}
-.c21:focus .c19 {
+.c23:focus .c21 {
background-color: #B8C0DC3d;
}
-.c21:hover .c19 {
+.c23:hover .c21 {
background-color: #98A1C014;
}
-.c21:disabled {
+.c23:disabled {
cursor: default;
opacity: 0.6;
}
-.c21:disabled:active .c19,
-.c21:disabled:focus .c19,
-.c21:disabled:hover .c19 {
+.c23:disabled:active .c21,
+.c23:disabled:focus .c21,
+.c23:disabled:hover .c21 {
background-color: transparent;
}
-.c5 {
+.c0 {
+ will-change: transform,opacity;
+}
+
+.c0[data-reach-dialog-overlay] {
+ z-index: 1040;
+ background-color: transparent;
+ overflow: hidden;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ overflow-y: scroll;
+ -webkit-box-pack: center;
+ -webkit-justify-content: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ background-color: rgba(0,0,0,0.60);
+}
+
+.c1 {
+ overflow-y: auto;
+}
+
+.c1[data-reach-dialog-content] {
+ margin: auto;
+ background-color: #F9F9F9;
+ border: 1px solid #22222212;
+ box-shadow: 8px 12px 20px rgba(51,53,72,0.04),4px 6px 12px rgba(51,53,72,0.02),4px 4px 8px rgba(51,53,72,0.04);
+ padding: 0px;
+ width: 50vw;
+ overflow-y: auto;
+ overflow-x: hidden;
+ max-height: 90vh;
+ max-width: 420px;
+ display: inline-table;
+ border-radius: 20px;
+}
+
+.c7 {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
@@ -240,17 +283,17 @@ exports[`
renders different button types 1`] = `
stroke: none;
}
-.c5:hover {
+.c7:hover {
background: #22222212;
color: #222222;
opacity: unset;
}
-.c5:hover path {
+.c7:hover path {
fill: #222222;
}
-.c0 {
+.c2 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -265,7 +308,7 @@ exports[`
renders different button types 1`] = `
gap: 24px;
}
-.c9 {
+.c11 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -280,7 +323,7 @@ exports[`
renders different button types 1`] = `
gap: 16px;
}
-.c11 {
+.c13 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -295,7 +338,7 @@ exports[`
renders different button types 1`] = `
gap: 8px;
}
-.c1 {
+.c3 {
width: 100%;
-webkit-align-items: center;
-webkit-box-align: center;
@@ -303,14 +346,15 @@ exports[`
renders different button types 1`] = `
align-items: center;
}
-.c2 {
+.c4 {
background-color: #FFFFFF;
- border-radius: 16px;
+ outline: 1px solid #22222212;
+ border-radius: 20px;
padding: 16px 24px 24px 24px;
width: 100%;
}
-.c10 {
+.c12 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -329,14 +373,14 @@ exports[`
renders different button types 1`] = `
border-radius: 12px;
}
-.c13 {
+.c15 {
font-size: 24px;
line-height: 32px;
text-align: center;
font-weight: 500;
}
-.c15 {
+.c17 {
font-size: 16px;
font-weight: 500;
line-height: 24px;
@@ -347,7 +391,7 @@ exports[`
renders different button types 1`] = `
text-align: center;
}
-.c18 {
+.c20 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -360,16 +404,40 @@ exports[`
renders different button types 1`] = `
border-radius: 12px;
}
-.c3 {
+.c5 {
padding: 4px 0px;
}
+@media screen and (max-width:640px) {
+ .c0[data-reach-dialog-overlay] {
+ -webkit-align-items: flex-end;
+ -webkit-box-align: flex-end;
+ -ms-flex-align: flex-end;
+ align-items: flex-end;
+ }
+}
+
+@media screen and (max-width:768px) {
+ .c1[data-reach-dialog-content] {
+ width: 65vw;
+ }
+}
+
+@media screen and (max-width:640px) {
+ .c1[data-reach-dialog-content] {
+ margin: 0;
+ width: 100vw;
+ border-radius: 20px;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+}
+
renders different button types 1`] = `
-
-
-
+
-
-
+
+
-
+
-
+
+
+
-
-
-
-
-
- Mock Body
-
+
+ Mock Title
-
-
+
+ Mock Description
+
+
+
+ Mock Body
+
+
+
+
-
-
-
+
+
+
+
`;
exports[` renders the Dialog component correctly 1`] = `
-.c6 {
+.c8 {
box-sizing: border-box;
margin: 0;
min-width: 0;
}
-.c7 {
+.c9 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
@@ -586,7 +640,7 @@ exports[` renders the Dialog component correctly 1`] = `
gap: 4px;
}
-.c16 {
+.c18 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
@@ -604,7 +658,7 @@ exports[` renders the Dialog component correctly 1`] = `
gap: 12px;
}
-.c12 {
+.c14 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
@@ -612,7 +666,7 @@ exports[` renders the Dialog component correctly 1`] = `
letter-spacing: -0.01em;
}
-.c14 {
+.c16 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
@@ -620,7 +674,7 @@ exports[` renders the Dialog component correctly 1`] = `
letter-spacing: -0.01em;
}
-.c8 {
+.c10 {
color: #222222;
cursor: pointer;
-webkit-text-decoration: none;
@@ -630,15 +684,15 @@ exports[` renders the Dialog component correctly 1`] = `
transition-duration: 125ms;
}
-.c8:hover {
+.c10:hover {
opacity: 0.6;
}
-.c8:active {
+.c10:active {
opacity: 0.4;
}
-.c4 {
+.c6 {
-webkit-text-decoration: none;
text-decoration: none;
cursor: pointer;
@@ -649,15 +703,15 @@ exports[` renders the Dialog component correctly 1`] = `
font-weight: 500;
}
-.c4:hover {
+.c6:hover {
opacity: 0.6;
}
-.c4:active {
+.c6:active {
opacity: 0.4;
}
-.c20 {
+.c22 {
background-color: transparent;
bottom: 0;
border-radius: inherit;
@@ -671,7 +725,7 @@ exports[` renders the Dialog component correctly 1`] = `
width: 100%;
}
-.c17 {
+.c19 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
@@ -706,30 +760,73 @@ exports[` renders the Dialog component correctly 1`] = `
user-select: none;
}
-.c17:active .c19 {
+.c19:active .c21 {
background-color: #B8C0DC3d;
}
-.c17:focus .c19 {
+.c19:focus .c21 {
background-color: #B8C0DC3d;
}
-.c17:hover .c19 {
+.c19:hover .c21 {
background-color: #98A1C014;
}
-.c17:disabled {
+.c19:disabled {
cursor: default;
opacity: 0.6;
}
-.c17:disabled:active .c19,
-.c17:disabled:focus .c19,
-.c17:disabled:hover .c19 {
+.c19:disabled:active .c21,
+.c19:disabled:focus .c21,
+.c19:disabled:hover .c21 {
background-color: transparent;
}
-.c5 {
+.c0 {
+ will-change: transform,opacity;
+}
+
+.c0[data-reach-dialog-overlay] {
+ z-index: 1040;
+ background-color: transparent;
+ overflow: hidden;
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ overflow-y: scroll;
+ -webkit-box-pack: center;
+ -webkit-justify-content: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ background-color: rgba(0,0,0,0.60);
+}
+
+.c1 {
+ overflow-y: auto;
+}
+
+.c1[data-reach-dialog-content] {
+ margin: auto;
+ background-color: #F9F9F9;
+ border: 1px solid #22222212;
+ box-shadow: 8px 12px 20px rgba(51,53,72,0.04),4px 6px 12px rgba(51,53,72,0.02),4px 4px 8px rgba(51,53,72,0.04);
+ padding: 0px;
+ width: 50vw;
+ overflow-y: auto;
+ overflow-x: hidden;
+ max-height: 90vh;
+ max-width: 420px;
+ display: inline-table;
+ border-radius: 20px;
+}
+
+.c7 {
width: -webkit-fit-content;
width: -moz-fit-content;
width: fit-content;
@@ -743,17 +840,17 @@ exports[` renders the Dialog component correctly 1`] = `
stroke: none;
}
-.c5:hover {
+.c7:hover {
background: #22222212;
color: #222222;
opacity: unset;
}
-.c5:hover path {
+.c7:hover path {
fill: #222222;
}
-.c0 {
+.c2 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -768,7 +865,7 @@ exports[` renders the Dialog component correctly 1`] = `
gap: 24px;
}
-.c9 {
+.c11 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -783,7 +880,7 @@ exports[` renders the Dialog component correctly 1`] = `
gap: 16px;
}
-.c11 {
+.c13 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -798,7 +895,7 @@ exports[` renders the Dialog component correctly 1`] = `
gap: 8px;
}
-.c1 {
+.c3 {
width: 100%;
-webkit-align-items: center;
-webkit-box-align: center;
@@ -806,14 +903,15 @@ exports[` renders the Dialog component correctly 1`] = `
align-items: center;
}
-.c2 {
+.c4 {
background-color: #FFFFFF;
- border-radius: 16px;
+ outline: 1px solid #22222212;
+ border-radius: 20px;
padding: 16px 24px 24px 24px;
width: 100%;
}
-.c10 {
+.c12 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -832,14 +930,14 @@ exports[` renders the Dialog component correctly 1`] = `
border-radius: 12px;
}
-.c13 {
+.c15 {
font-size: 24px;
line-height: 32px;
text-align: center;
font-weight: 500;
}
-.c15 {
+.c17 {
font-size: 16px;
font-weight: 500;
line-height: 24px;
@@ -850,7 +948,7 @@ exports[` renders the Dialog component correctly 1`] = `
text-align: center;
}
-.c18 {
+.c20 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -863,16 +961,40 @@ exports[` renders the Dialog component correctly 1`] = `
border-radius: 12px;
}
-.c3 {
+.c5 {
padding: 4px 0px;
}
+@media screen and (max-width:640px) {
+ .c0[data-reach-dialog-overlay] {
+ -webkit-align-items: flex-end;
+ -webkit-box-align: flex-end;
+ -ms-flex-align: flex-end;
+ align-items: flex-end;
+ }
+}
+
+@media screen and (max-width:768px) {
+ .c1[data-reach-dialog-content] {
+ width: 65vw;
+ }
+}
+
+@media screen and (max-width:640px) {
+ .c1[data-reach-dialog-content] {
+ margin: 0;
+ width: 100vw;
+ border-radius: 20px;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+}
+
renders the Dialog component correctly 1`] = `
-
-
-
+
-
-
+
+
-
+
-
+
+
+
-
-
-
-
-
- Mock Body
-
+
+ Mock Title
-
-
+
+ Mock Description
+
+
+
+ Mock Body
+
+
+
+
-
-
-
+
+
+
+
`;
diff --git a/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx b/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx
index 5000d3fa8aa..72e2c96807e 100644
--- a/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx
+++ b/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx
@@ -1,4 +1,5 @@
import { SmallButtonPrimary } from 'components/Button/buttons'
+import Modal from 'components/Modal'
import Column from 'components/deprecated/Column'
import Row from 'components/deprecated/Row'
import { useQuickRouteChains } from 'featureFlags/dynamicConfig/quickRouteChains'
@@ -8,7 +9,6 @@ import { X } from 'react-feather'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { BREAKPOINTS } from 'theme'
-import { AdaptiveWebModal } from 'ui/src'
import { SUPPORTED_CHAIN_IDS } from 'uniswap/src/features/chains/types'
import {
DynamicConfigKeys,
@@ -203,7 +203,7 @@ export default function FeatureFlagModal() {
const closeModal = useCloseModal()
return (
-
+
@@ -291,6 +291,6 @@ export default function FeatureFlagModal() {
window.location.reload()}>Reload
-
+
)
}
diff --git a/apps/web/src/components/Modal/index.tsx b/apps/web/src/components/Modal/index.tsx
new file mode 100644
index 00000000000..35f46612a6b
--- /dev/null
+++ b/apps/web/src/components/Modal/index.tsx
@@ -0,0 +1,200 @@
+//
+// @deprecated Prefer Modal from packages/uniswap/src/components/modals/Modal.tsx
+//
+// TODO(WEB-4886): Remove this
+//
+import { DialogContent, DialogOverlay } from '@reach/dialog'
+import { useOnClickOutside } from 'hooks/useOnClickOutside'
+import styled from 'lib/styled-components'
+import React, { KeyboardEvent, useCallback, useRef } from 'react'
+import { animated, easings, useSpring, useTransition } from 'react-spring'
+import { useGesture } from 'react-use-gesture'
+import { Z_INDEX } from 'theme/zIndex'
+import { isMobileWeb } from 'utilities/src/platform'
+
+export const MODAL_TRANSITION_DURATION = 200
+
+const AnimatedDialogOverlay = animated(DialogOverlay)
+
+const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ $scrollOverlay?: boolean }>`
+ will-change: transform, opacity;
+ &[data-reach-dialog-overlay] {
+ z-index: ${Z_INDEX.modalBackdrop};
+ background-color: transparent;
+ overflow: hidden;
+
+ display: flex;
+ align-items: center;
+ @media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
+ align-items: flex-end;
+ }
+ overflow-y: ${({ $scrollOverlay }) => $scrollOverlay && 'scroll'};
+ justify-content: center;
+
+ background-color: ${({ theme }) => theme.scrim};
+ }
+`
+
+type Dimension = number | string
+
+function dimensionsToCss(dimensions: Dimension) {
+ if (typeof dimensions === 'number') {
+ return `${dimensions}px`
+ }
+ return dimensions
+}
+
+type StyledDialogProps = {
+ $height?: Dimension
+ $minHeight?: Dimension
+ $maxHeight?: Dimension
+ $scrollOverlay?: boolean
+ $hideBorder?: boolean
+ $maxWidth: Dimension
+}
+
+const AnimatedDialogContent = animated(DialogContent)
+const StyledDialogContent = styled(AnimatedDialogContent)`
+ overflow-y: auto;
+
+ &[data-reach-dialog-content] {
+ margin: auto;
+ background-color: ${({ theme }) => theme.surface2};
+ border: ${({ theme, $hideBorder }) => !$hideBorder && `1px solid ${theme.surface3}`};
+ box-shadow: ${({ theme }) => theme.deprecated_deepShadow};
+ padding: 0px;
+ width: 50vw;
+ overflow-y: auto;
+ overflow-x: hidden;
+ ${({ $height }) => $height && `height: ${dimensionsToCss($height)};`}
+ ${({ $maxHeight }) => $maxHeight && `max-height: ${dimensionsToCss($maxHeight)};`}
+ ${({ $minHeight }) => $minHeight && `min-height: ${dimensionsToCss($minHeight)};`}
+ ${({ $maxWidth }) => $maxWidth && `max-width: ${dimensionsToCss($maxWidth)};`}
+ display: ${({ $scrollOverlay }) => ($scrollOverlay ? 'inline-table' : 'flex')};
+ border-radius: 20px;
+
+ @media screen and (max-width: ${({ theme }) => theme.breakpoint.md}px) {
+ width: 65vw;
+ }
+ @media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
+ margin: 0;
+ width: 100vw;
+ border-radius: 20px;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ }
+`
+
+interface ModalProps {
+ isOpen: boolean
+ onDismiss?: () => void
+ onSwipe?: () => void
+ height?: Dimension
+ minHeight?: Dimension
+ maxHeight?: Dimension
+ maxWidth?: Dimension
+ initialFocusRef?: React.RefObject
+ children?: React.ReactNode
+ $scrollOverlay?: boolean
+ hideBorder?: boolean
+ slideIn?: boolean
+}
+
+export default function Modal({
+ isOpen,
+ onDismiss,
+ minHeight,
+ maxHeight = '90vh',
+ maxWidth = 420,
+ height,
+ initialFocusRef,
+ children,
+ onSwipe = onDismiss,
+ $scrollOverlay,
+ hideBorder = false,
+ slideIn,
+}: ModalProps) {
+ const ref = useRef(null)
+ useOnClickOutside(ref, () => (isOpen && onDismiss ? onDismiss() : undefined))
+
+ const handleEscape = useCallback(
+ (e: KeyboardEvent) => {
+ if (e.key === 'Escape' && isOpen && onDismiss) {
+ onDismiss()
+ }
+ },
+ [isOpen, onDismiss],
+ )
+
+ const fadeTransition = useTransition(isOpen, {
+ config: { duration: MODAL_TRANSITION_DURATION },
+ from: { opacity: 0 },
+ enter: { opacity: 1 },
+ leave: { opacity: 0 },
+ })
+
+ const slideTransition = useTransition(isOpen, {
+ config: { duration: MODAL_TRANSITION_DURATION, easing: easings.easeInOutCubic },
+ from: { transform: 'translateY(40px)' },
+ enter: { transform: 'translateY(0px)' },
+ leave: { transform: 'translateY(40px)' },
+ })
+
+ const [{ y }, set] = useSpring(() => ({ y: 0, config: { mass: 1, tension: 210, friction: 20 } }))
+ const bind = useGesture({
+ onDrag: (state) => {
+ set({
+ y: state.down ? state.movement[1] : 0,
+ })
+ if (state.movement[1] > 300 || (state.velocity > 3 && state.direction[1] > 0)) {
+ onSwipe?.()
+ }
+ },
+ })
+
+ return (
+
+ {fadeTransition(
+ ({ opacity }, item) =>
+ item && (
+
+ {slideTransition(
+ (styles, item) =>
+ item && (
+ `translateY(${(y as number) > 0 ? y : 0}px)`) },
+ }
+ : slideIn
+ ? { style: styles }
+ : {})}
+ aria-label="dialog"
+ $height={height}
+ $minHeight={minHeight}
+ $maxHeight={maxHeight}
+ $scrollOverlay={$scrollOverlay}
+ $hideBorder={hideBorder}
+ $maxWidth={maxWidth}
+ >
+ {/* prevents the automatic focusing of inputs on mobile by the reach dialog */}
+ {!initialFocusRef && isMobileWeb ? : null}
+ {children}
+
+ ),
+ )}
+
+ ),
+ )}
+
+ )
+}
diff --git a/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx b/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx
index ee14f9a4251..3a1a8a96600 100644
--- a/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx
+++ b/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx
@@ -1,17 +1,23 @@
import { InterfaceModalName } from '@uniswap/analytics-events'
import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
+import Modal from 'components/Modal'
import { useIsAccountCTAExperimentControl } from 'components/NavBar/accountCTAsExperimentUtils'
import { GetStarted } from 'components/NavBar/DownloadApp/Modal/GetStarted'
import { GetTheApp } from 'components/NavBar/DownloadApp/Modal/GetTheApp'
+import styled from 'lib/styled-components'
import { useCallback, useState } from 'react'
import { ArrowLeft, X } from 'react-feather'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { ClickableTamaguiStyle } from 'theme/components'
-import { AdaptiveWebModal, AnimateTransition, Flex, styled as tamaguiStyled } from 'ui/src'
+import { AnimateTransition, Flex, styled as tamaguiStyled } from 'ui/src'
import { iconSizes, zIndices } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
+const StyledModal = styled(Modal)`
+ display: block;
+`
+
const HeaderActionIcon = {
margin: 4,
color: '$neutral1',
@@ -61,7 +67,13 @@ export function GetTheAppModal() {
return (
-
+
-
+
)
}
diff --git a/apps/web/src/components/PrivacyChoices.tsx b/apps/web/src/components/PrivacyChoices.tsx
index f02abaf093b..a1db9e9b0ca 100644
--- a/apps/web/src/components/PrivacyChoices.tsx
+++ b/apps/web/src/components/PrivacyChoices.tsx
@@ -1,9 +1,10 @@
import { InterfaceElementName } from '@uniswap/analytics-events'
+import Modal from 'components/Modal'
import { useAtom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'
import { useCallback, useState } from 'react'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
-import { AdaptiveWebModal, Anchor, Button, Checkbox, Flex, Text, TouchableArea } from 'ui/src'
+import { Anchor, Button, Checkbox, Flex, Text, TouchableArea } from 'ui/src'
import { Lock } from 'ui/src/components/icons/Lock'
import { X } from 'ui/src/components/icons/X'
import { uniswapUrls } from 'uniswap/src/constants/urls'
@@ -33,7 +34,7 @@ export function PrivacyChoicesModal() {
}, [isOptOutChecked, closeModal, setPrivacySharingOptOut])
return (
-
+
@@ -90,6 +91,6 @@ export function PrivacyChoicesModal() {
-
+
)
}
diff --git a/apps/web/src/components/PrivacyPolicy.tsx b/apps/web/src/components/PrivacyPolicy.tsx
index a9b720b4ba3..9d3bc9a7dde 100644
--- a/apps/web/src/components/PrivacyPolicy.tsx
+++ b/apps/web/src/components/PrivacyPolicy.tsx
@@ -1,5 +1,6 @@
import { SharedEventName } from '@uniswap/analytics-events'
import Card, { DarkGrayCard } from 'components/Card/cards'
+import Modal from 'components/Modal'
import { AutoColumn } from 'components/deprecated/Column'
import Row, { AutoRow, RowBetween } from 'components/deprecated/Row'
import styled from 'lib/styled-components'
@@ -8,7 +9,6 @@ import { ArrowDown, Info, X } from 'react-feather'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { ExternalLink, ThemedText } from 'theme/components'
-import { AdaptiveWebModal } from 'ui/src'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send'
import { Trans } from 'uniswap/src/i18n'
@@ -89,7 +89,7 @@ export function PrivacyPolicyModal() {
}, [open])
return (
- closeModal()} p={0}>
+ closeModal()}>
@@ -101,7 +101,7 @@ export function PrivacyPolicyModal() {
-
+
)
}
diff --git a/apps/web/src/components/TokenSafety/TokenSafetyModal.tsx b/apps/web/src/components/TokenSafety/TokenSafetyModal.tsx
index 2b3016b8fc2..b1bf5b03ef9 100644
--- a/apps/web/src/components/TokenSafety/TokenSafetyModal.tsx
+++ b/apps/web/src/components/TokenSafety/TokenSafetyModal.tsx
@@ -1,4 +1,4 @@
-import { AdaptiveWebModal } from 'ui/src'
+import Modal from 'components/Modal'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import TokenWarningModal from 'uniswap/src/features/tokens/TokenWarningModal'
@@ -47,7 +47,7 @@ export default function TokenSafetyModal({
onToken1BlockAcknowledged={onToken1BlockAcknowledged}
/>
) : (
-
+
-
+
)
}
diff --git a/apps/web/src/components/TopLevelModals/LaunchModal.tsx b/apps/web/src/components/TopLevelModals/LaunchModal.tsx
index 8a16d0f69d3..68e0b8222a0 100644
--- a/apps/web/src/components/TopLevelModals/LaunchModal.tsx
+++ b/apps/web/src/components/TopLevelModals/LaunchModal.tsx
@@ -1,6 +1,7 @@
// Remove the following line when LaunchModal is used again:
/* eslint-disable import/no-unused-modules */
import { InterfaceElementName } from '@uniswap/analytics-events'
+import Modal from 'components/Modal'
import {
LAUNCH_MODAL_DESKTOP_MAX_HEIGHT,
LAUNCH_MODAL_DESKTOP_MAX_WIDTH,
@@ -11,7 +12,7 @@ import { useIsLandingPage } from 'hooks/useIsLandingPage'
import { useAtom } from 'jotai'
import { atomWithStorage } from 'jotai/utils'
import { useMemo } from 'react'
-import { AdaptiveWebModal, Button, Flex, Image, ImageProps, Text, TouchableArea, useMedia } from 'ui/src'
+import { Button, Flex, Image, ImageProps, Text, TouchableArea, useMedia } from 'ui/src'
import { X } from 'ui/src/components/icons/X'
import { iconSizes } from 'ui/src/theme'
import Trace from 'uniswap/src/features/telemetry/Trace'
@@ -46,12 +47,12 @@ export function LaunchModal({
return (
- setShowModal(false)}
- p={0}
+ onDismiss={() => setShowModal(false)}
+ hideBorder
>
-
+
)
}
diff --git a/apps/web/src/components/TopLevelModals/UkDisclaimerModal.tsx b/apps/web/src/components/TopLevelModals/UkDisclaimerModal.tsx
index af2a8b60999..ef5d9934dc1 100644
--- a/apps/web/src/components/TopLevelModals/UkDisclaimerModal.tsx
+++ b/apps/web/src/components/TopLevelModals/UkDisclaimerModal.tsx
@@ -1,12 +1,12 @@
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button/buttons'
import Column from 'components/deprecated/Column'
+import Modal from 'components/Modal'
import { bannerText } from 'components/TopLevelBanners/UkBanner'
import styled from 'lib/styled-components'
import { X } from 'react-feather'
import { useCloseModal, useModalIsOpen } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import { ButtonText, ThemedText } from 'theme/components'
-import { AdaptiveWebModal } from 'ui/src'
import { Trans } from 'uniswap/src/i18n'
const Wrapper = styled(Column)`
@@ -37,7 +37,7 @@ export function UkDisclaimerModal() {
const closeModal = useCloseModal()
return (
-
+
closeModal()}>
@@ -56,6 +56,6 @@ export function UkDisclaimerModal() {
-
+
)
}
diff --git a/apps/web/src/components/TransactionConfirmationModal/index.tsx b/apps/web/src/components/TransactionConfirmationModal/index.tsx
index 41d2ff41df0..daacce5aa7b 100644
--- a/apps/web/src/components/TransactionConfirmationModal/index.tsx
+++ b/apps/web/src/components/TransactionConfirmationModal/index.tsx
@@ -5,6 +5,7 @@ import { TransactionSummary } from 'components/AccountDetails/TransactionSummary
import Badge from 'components/Badge/Badge'
import { ButtonLight, ButtonPrimary } from 'components/Button/buttons'
import { ChainLogo } from 'components/Logo/ChainLogo'
+import Modal from 'components/Modal'
import AnimatedConfirmation from 'components/TransactionConfirmationModal/AnimatedConfirmation'
import { AutoColumn, ColumnCenter } from 'components/deprecated/Column'
import Row, { RowBetween, RowFixed } from 'components/deprecated/Row'
@@ -16,7 +17,6 @@ import { AlertCircle, ArrowUpCircle, CheckCircle } from 'react-feather'
import { useTransaction } from 'state/transactions/hooks'
import { isConfirmedTx } from 'state/transactions/utils'
import { CloseIcon, CustomLightSpinner, ExternalLink, ThemedText } from 'theme/components'
-import { AdaptiveWebModal } from 'ui/src'
import { TransactionStatus } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { getChainInfo } from 'uniswap/src/features/chains/chainInfo'
import { useIsSupportedChainId } from 'uniswap/src/features/chains/hooks/useSupportedChainId'
@@ -340,7 +340,7 @@ export default function TransactionConfirmationModal({
// confirmation screen
return (
-
+
{isL2ChainId(chainId) && (hash || attemptingTxn) ? (
) : attemptingTxn ? (
@@ -355,6 +355,6 @@ export default function TransactionConfirmationModal({
) : (
reviewContent()
)}
-
+
)
}
diff --git a/apps/web/src/components/WalletModal/ConnectionErrorView.tsx b/apps/web/src/components/WalletModal/ConnectionErrorView.tsx
index 1fedd7f923a..e54dfd3b9c2 100644
--- a/apps/web/src/components/WalletModal/ConnectionErrorView.tsx
+++ b/apps/web/src/components/WalletModal/ConnectionErrorView.tsx
@@ -1,11 +1,11 @@
import { ButtonEmpty, ButtonPrimary } from 'components/Button/buttons'
+import Modal from 'components/Modal'
import { useConnect } from 'hooks/useConnect'
import styled from 'lib/styled-components'
import { useCallback } from 'react'
import { AlertTriangle } from 'react-feather'
import { ThemedText } from 'theme/components'
import { flexColumnNoWrap } from 'theme/styles'
-import { AdaptiveWebModal } from 'ui/src'
import { Trans } from 'uniswap/src/i18n'
const Wrapper = styled.div`
@@ -39,7 +39,7 @@ export default function ConnectionErrorView() {
}, [connection])
return (
-
+
@@ -57,6 +57,6 @@ export default function ConnectionErrorView() {
-
+
)
}
diff --git a/apps/web/src/components/claim/AddressClaimModal.tsx b/apps/web/src/components/claim/AddressClaimModal.tsx
index d4b9a62037a..c98b8e25b57 100644
--- a/apps/web/src/components/claim/AddressClaimModal.tsx
+++ b/apps/web/src/components/claim/AddressClaimModal.tsx
@@ -4,6 +4,7 @@ import Circle from 'assets/images/blue-loader.svg'
import tokenLogo from 'assets/images/token-logo.png'
import AddressInputPanel from 'components/AddressInputPanel'
import { ButtonPrimary } from 'components/Button/buttons'
+import Modal from 'components/Modal'
import { AutoColumn, ColumnCenter } from 'components/deprecated/Column'
import { RowBetween } from 'components/deprecated/Row'
import { Break, CardBGImage, CardBGImageSmaller, CardNoise, CardSection, DataCard } from 'components/earn/styled'
@@ -14,7 +15,7 @@ import { useState } from 'react'
import { useClaimCallback, useUserHasAvailableClaim, useUserUnclaimedAmount } from 'state/claim/hooks'
import { useIsTransactionPending } from 'state/transactions/hooks'
import { CloseIcon, CustomLightSpinner, ExternalLink, ThemedText, UniTokenAnimated } from 'theme/components'
-import { AdaptiveWebModal, Text } from 'ui/src'
+import { Text } from 'ui/src'
import { ExplorerDataType, getExplorerLink } from 'uniswap/src/utils/linking'
import { shortenAddress } from 'utilities/src/addresses'
import { logger } from 'utilities/src/logger/logger'
@@ -97,7 +98,7 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
// Avoiding translating because the structure for "Claiming UNI for address" is wrong but this modal is rarely used
// and ran into difficulties with testing it
return (
-
+
{!attempting && (
@@ -193,6 +194,6 @@ export default function AddressClaimModal({ isOpen, onDismiss }: { isOpen: boole
)}
-
+
)
}
diff --git a/apps/web/src/components/swap/PriceImpactModal.tsx b/apps/web/src/components/swap/PriceImpactModal.tsx
index 97f57e6cb78..47e0305cf86 100644
--- a/apps/web/src/components/swap/PriceImpactModal.tsx
+++ b/apps/web/src/components/swap/PriceImpactModal.tsx
@@ -1,6 +1,7 @@
import { Percent } from '@uniswap/sdk-core'
+import Modal from 'components/Modal'
import { GetHelpHeader } from 'components/Modal/GetHelpHeader'
-import { AdaptiveWebModal, Button, Flex, Text } from 'ui/src'
+import { Button, Flex, Text } from 'ui/src'
import { AlertTriangleFilled } from 'ui/src/components/icons/AlertTriangleFilled'
import { Trans } from 'uniswap/src/i18n'
import { useFormatter } from 'utils/formatNumbers'
@@ -16,7 +17,7 @@ export default function PriceImpactModal({ priceImpact, onDismiss, onContinue }:
const impact = `${formatPercent(priceImpact)}`
return (
-
+
@@ -78,6 +79,6 @@ export default function PriceImpactModal({ priceImpact, onDismiss, onContinue }:
-
+
)
}
diff --git a/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx b/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx
index 371c9b8d401..0060621d2da 100644
--- a/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx
+++ b/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx
@@ -9,7 +9,6 @@ import { act, render, screen, waitForElementToBeRemoved, within } from 'test-uti
import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
import { getExplorerLink } from 'uniswap/src/utils/linking'
-import { shortenAddress } from 'utilities/src/addresses'
const unsupportedTokenAddress = '0x4e83b6287588a96321B2661c5E041845fF7814af'
const unsupportedTokenSymbol = 'ALTDOM-MAR2021'
@@ -45,7 +44,7 @@ describe('UnsupportedCurrencyFooter.tsx with unsupported tokens', () => {
expect(screen.getAllByTestId('unsupported-token-card').length).toBe(1)
const unsupportedCard = screen.getByTestId('unsupported-token-card')
expect(within(unsupportedCard).getByText(unsupportedTokenSymbol)).toBeInTheDocument()
- expect(within(unsupportedCard).getByText(shortenAddress(unsupportedTokenAddress)).closest('a')).toHaveAttribute(
+ expect(within(unsupportedCard).getByText(unsupportedTokenAddress).closest('a')).toHaveAttribute(
'href',
unsupportedTokenExplorerLink,
)
diff --git a/apps/web/src/components/swap/UnsupportedCurrencyFooter.tsx b/apps/web/src/components/swap/UnsupportedCurrencyFooter.tsx
index 6e65e450dba..f010a603c15 100644
--- a/apps/web/src/components/swap/UnsupportedCurrencyFooter.tsx
+++ b/apps/web/src/components/swap/UnsupportedCurrencyFooter.tsx
@@ -2,6 +2,7 @@ import { Currency, Token } from '@uniswap/sdk-core'
import { ButtonEmpty } from 'components/Button/buttons'
import Card, { OutlineCard } from 'components/Card/cards'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
+import Modal from 'components/Modal'
import { AutoColumn } from 'components/deprecated/Column'
import { AutoRow, RowBetween } from 'components/deprecated/Row'
import { useCurrencyInfo } from 'hooks/Tokens'
@@ -10,12 +11,11 @@ import styled from 'lib/styled-components'
import { useState } from 'react'
import { CloseIcon, ExternalLink, ThemedText } from 'theme/components'
import { Z_INDEX } from 'theme/zIndex'
-import { AdaptiveWebModal, Text } from 'ui/src'
+import { Text } from 'ui/src'
import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
import { Trans } from 'uniswap/src/i18n'
import { ExplorerDataType, getExplorerLink } from 'uniswap/src/utils/linking'
-import { shortenAddress } from 'utilities/src/addresses'
const DetailsFooter = styled.div<{ show: boolean }>`
padding-top: calc(16px + 2rem);
@@ -68,7 +68,7 @@ export default function UnsupportedCurrencyFooter({
return (
- setShowDetails(false)} p={0}>
+ setShowDetails(false)}>
@@ -89,7 +89,7 @@ export default function UnsupportedCurrencyFooter({
-
+
setShowDetails(true)} data-testid="read-more-button">
@@ -115,7 +115,7 @@ function UnsupportedTokenCard({ token, chainId }: { token?: Token; chainId?: Uni
{chainId && (
- {shortenAddress(token.address)}
+ {token.address}
)}
diff --git a/apps/web/src/components/swap/__snapshots__/UnsupportedCurrencyFooter.test.tsx.snap b/apps/web/src/components/swap/__snapshots__/UnsupportedCurrencyFooter.test.tsx.snap
index 17f539240d2..8d6acf4a129 100644
--- a/apps/web/src/components/swap/__snapshots__/UnsupportedCurrencyFooter.test.tsx.snap
+++ b/apps/web/src/components/swap/__snapshots__/UnsupportedCurrencyFooter.test.tsx.snap
@@ -160,16 +160,9 @@ exports[`UnsupportedCurrencyFooter.tsx with unsupported tokens renders 1`] = `
-
-
-
+