Add theme toggle and footer (#400)
This commit is contained in:
parent
80da6e0ff6
commit
c28884f44b
@ -24,6 +24,7 @@
|
||||
"react-router-dom": "^5.0.0",
|
||||
"react-scripts": "^3.0.1",
|
||||
"react-spring": "^8.0.20",
|
||||
"react-toggle": "^4.0.2",
|
||||
"styled-components": "^4.2.0",
|
||||
"web3-react": "^5.0.4"
|
||||
},
|
||||
|
@ -14,7 +14,7 @@ const SummaryWrapper = styled.div`
|
||||
`
|
||||
|
||||
const Details = styled.div`
|
||||
background-color: ${({ theme }) => theme.doveGray};
|
||||
background-color: ${({ theme }) => theme.concreteGray};
|
||||
padding: 1.5rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.75rem;
|
||||
|
120
src/components/Footer/index.js
Normal file
120
src/components/Footer/index.js
Normal file
@ -0,0 +1,120 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { Link } from '../../theme'
|
||||
import { darken } from 'polished'
|
||||
import { useDarkModeManager } from '../../contexts/LocalStorage'
|
||||
import Toggle from 'react-toggle'
|
||||
import { transparentize } from 'polished'
|
||||
|
||||
import 'react-toggle/style.css'
|
||||
|
||||
const FooterFrame = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const FooterElement = styled.div`
|
||||
margin: 1.25rem;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const Title = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.uniswapPink};
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
#link {
|
||||
text-decoration-color: ${({ theme }) => theme.uniswapPink};
|
||||
}
|
||||
|
||||
#title {
|
||||
display: inline;
|
||||
font-size: 0.825rem;
|
||||
margin-right: 12px;
|
||||
font-weight: 400;
|
||||
color: ${({ theme }) => theme.uniswapPink};
|
||||
:hover {
|
||||
color: ${({ theme }) => darken(0.2, theme.uniswapPink)};
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const EmojiToggle = styled.span`
|
||||
position: relative;
|
||||
font-size: 15px;
|
||||
font-family: 'Arial sans-serif';
|
||||
`
|
||||
|
||||
const ToggleComponent = styled(Toggle)`
|
||||
margin-right: 24px;
|
||||
.react-toggle-track {
|
||||
background-color: ${({ theme }) => theme.inputBackground} !important;
|
||||
border: 1px solid ${({ theme }) => theme.concreteGray};
|
||||
}
|
||||
|
||||
.react-toggle-track-x {
|
||||
line-height: unset;
|
||||
bottom: auto;
|
||||
right: 14px;
|
||||
}
|
||||
|
||||
.react-toggle-track-check {
|
||||
line-height: unset;
|
||||
bottom: auto;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
&&& .react-toggle-thumb {
|
||||
background-color: ${({ theme }) => theme.inputBackground};
|
||||
box-shadow: 0 4px 8px 0 ${({ theme }) => transparentize(0.93, theme.royalBlue)};
|
||||
border: 1px solid ${({ theme }) => theme.mercuryGray};
|
||||
border-color: ${({ theme }) => theme.mercuryGray} !important;
|
||||
/* border: none; */
|
||||
top: 2px;
|
||||
left: ${({ defaultChecked }) => (defaultChecked ? '28px' : '2px')};
|
||||
}
|
||||
`
|
||||
|
||||
function ToggleIcon(props) {
|
||||
return (
|
||||
<EmojiToggle role="img" aria-label="sun">
|
||||
{props.content}
|
||||
</EmojiToggle>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Footer() {
|
||||
const [isDark, toggleDarkMode] = useDarkModeManager()
|
||||
|
||||
return (
|
||||
<FooterFrame>
|
||||
<FooterElement>
|
||||
<Title>
|
||||
<Link id="link" href="https://uniswap.io/">
|
||||
<h1 id="title">About</h1>
|
||||
</Link>
|
||||
<Link id="link" href="https://docs.uniswap.io/">
|
||||
<h1 id="title">Docs</h1>
|
||||
</Link>
|
||||
<Link id="link" href="https://github.com/Uniswap">
|
||||
<h1 id="title">Code</h1>
|
||||
</Link>
|
||||
</Title>
|
||||
</FooterElement>
|
||||
<ToggleComponent
|
||||
defaultChecked={!isDark}
|
||||
icons={{ checked: <ToggleIcon content="☀️" />, unchecked: <ToggleIcon content="🌙️" /> }}
|
||||
onChange={toggleDarkMode}
|
||||
/>
|
||||
</FooterFrame>
|
||||
)
|
||||
}
|
@ -4,12 +4,29 @@ import styled from 'styled-components'
|
||||
import { Link } from '../../theme'
|
||||
import Web3Status from '../Web3Status'
|
||||
import { darken } from 'polished'
|
||||
import { useDarkModeManager } from '../../contexts/LocalStorage'
|
||||
|
||||
const HeaderFrame = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`
|
||||
|
||||
const HeaderElement = styled.div`
|
||||
margin: 1.25rem;
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const Nod = styled.span`
|
||||
transform: rotate(0deg);
|
||||
transition: transform 150ms ease-out;
|
||||
|
||||
:hover {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
`
|
||||
|
||||
const Title = styled.div`
|
||||
@ -20,19 +37,8 @@ const Title = styled.div`
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#image {
|
||||
font-size: 1.5rem;
|
||||
margin-right: 1rem;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 150ms ease-out;
|
||||
|
||||
:hover {
|
||||
transform: rotate(-10deg);
|
||||
}
|
||||
}
|
||||
|
||||
#link {
|
||||
text-decoration-color: ${({ theme }) => theme.wisteriaPurple};
|
||||
text-decoration-color: ${({ theme }) => theme.UniswapPink};
|
||||
}
|
||||
|
||||
#title {
|
||||
@ -47,25 +53,25 @@ const Title = styled.div`
|
||||
`
|
||||
|
||||
export default function Header() {
|
||||
const [, toggleDarkMode] = useDarkModeManager()
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderFrame>
|
||||
<HeaderElement>
|
||||
<Title>
|
||||
<span onClick={toggleDarkMode} id="image" role="img" aria-label="Unicorn Emoji">
|
||||
🦄
|
||||
</span>
|
||||
|
||||
<Nod>
|
||||
<Link id="link" href="https://uniswap.io">
|
||||
<span role="img" aria-label="unicorn">
|
||||
🦄{' '}
|
||||
</span>
|
||||
</Link>
|
||||
</Nod>
|
||||
<Link id="link" href="https://uniswap.io">
|
||||
<h1 id="title">Uniswap</h1>
|
||||
</Link>
|
||||
</Title>
|
||||
</HeaderElement>
|
||||
|
||||
<HeaderElement>
|
||||
<Web3Status />
|
||||
</HeaderElement>
|
||||
</>
|
||||
</HeaderFrame>
|
||||
)
|
||||
}
|
||||
|
@ -87,6 +87,8 @@ const Popup = styled(Flex)`
|
||||
padding: 0.6rem 1rem;
|
||||
line-height: 150%;
|
||||
background: ${({ theme }) => theme.inputBackground};
|
||||
border: 1px solid ${({ theme }) => theme.mercuryGray};
|
||||
|
||||
border-radius: 8px;
|
||||
|
||||
animation: ${fadeIn} 0.15s linear;
|
||||
|
139
src/pages/App.js
139
src/pages/App.js
@ -4,6 +4,8 @@ import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'
|
||||
|
||||
import Web3ReactManager from '../components/Web3ReactManager'
|
||||
import Header from '../components/Header'
|
||||
import Footer from '../components/Footer'
|
||||
|
||||
import NavigationTabs from '../components/NavigationTabs'
|
||||
import { isAddress } from '../utils'
|
||||
|
||||
@ -11,83 +13,102 @@ const Swap = lazy(() => import('./Swap'))
|
||||
const Send = lazy(() => import('./Send'))
|
||||
const Pool = lazy(() => import('./Pool'))
|
||||
|
||||
const AppWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: flex-start;
|
||||
height: 100vh;
|
||||
`
|
||||
|
||||
const HeaderWrapper = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
`
|
||||
const FooterWrapper = styled.div`
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
align-self: flex-end;
|
||||
`
|
||||
|
||||
const BodyWrapper = styled.div`
|
||||
${({ theme }) => theme.flexRowNoWrap}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
`
|
||||
|
||||
const Body = styled.div`
|
||||
width: 35rem;
|
||||
margin: 1.25rem;
|
||||
max-width: 35rem;
|
||||
width: 90%;
|
||||
/* margin: 0 1.25rem 1.25rem 1.25rem; */
|
||||
`
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
<Suspense fallback={null}>
|
||||
<HeaderWrapper>
|
||||
<Header />
|
||||
</HeaderWrapper>
|
||||
<BodyWrapper>
|
||||
<Body>
|
||||
<Web3ReactManager>
|
||||
<BrowserRouter>
|
||||
<NavigationTabs />
|
||||
{/* this Suspense is for route code-splitting */}
|
||||
<Suspense fallback={null}>
|
||||
<Switch>
|
||||
<Route exact strict path="/swap" component={Swap} />
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/swap/:tokenAddress?"
|
||||
render={({ match }) => {
|
||||
if (isAddress(match.params.tokenAddress)) {
|
||||
return <Swap initialCurrency={isAddress(match.params.tokenAddress)} />
|
||||
} else {
|
||||
return <Redirect to={{ pathname: '/swap' }} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Route exact strict path="/send" component={Send} />
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/send/:tokenAddress?"
|
||||
render={({ match }) => {
|
||||
if (isAddress(match.params.tokenAddress)) {
|
||||
return <Send initialCurrency={isAddress(match.params.tokenAddress)} />
|
||||
} else {
|
||||
return <Redirect to={{ pathname: '/send' }} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={[
|
||||
'/add-liquidity',
|
||||
'/remove-liquidity',
|
||||
'/create-exchange',
|
||||
'/create-exchange/:tokenAddress?'
|
||||
]}
|
||||
component={Pool}
|
||||
/>
|
||||
<Redirect to="/swap" />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</BrowserRouter>
|
||||
</Web3ReactManager>
|
||||
</Body>
|
||||
</BodyWrapper>
|
||||
<AppWrapper>
|
||||
<HeaderWrapper>
|
||||
<Header />
|
||||
</HeaderWrapper>
|
||||
<BodyWrapper>
|
||||
<Body>
|
||||
<Web3ReactManager>
|
||||
<BrowserRouter>
|
||||
<NavigationTabs />
|
||||
{/* this Suspense is for route code-splitting */}
|
||||
<Suspense fallback={null}>
|
||||
<Switch>
|
||||
<Route exact strict path="/swap" component={Swap} />
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/swap/:tokenAddress?"
|
||||
render={({ match }) => {
|
||||
if (isAddress(match.params.tokenAddress)) {
|
||||
return <Swap initialCurrency={isAddress(match.params.tokenAddress)} />
|
||||
} else {
|
||||
return <Redirect to={{ pathname: '/swap' }} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Route exact strict path="/send" component={Send} />
|
||||
<Route
|
||||
exact
|
||||
strict
|
||||
path="/send/:tokenAddress?"
|
||||
render={({ match }) => {
|
||||
if (isAddress(match.params.tokenAddress)) {
|
||||
return <Send initialCurrency={isAddress(match.params.tokenAddress)} />
|
||||
} else {
|
||||
return <Redirect to={{ pathname: '/send' }} />
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={[
|
||||
'/add-liquidity',
|
||||
'/remove-liquidity',
|
||||
'/create-exchange',
|
||||
'/create-exchange/:tokenAddress?'
|
||||
]}
|
||||
component={Pool}
|
||||
/>
|
||||
<Redirect to="/swap" />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</BrowserRouter>
|
||||
</Web3ReactManager>
|
||||
</Body>
|
||||
</BodyWrapper>
|
||||
<FooterWrapper>
|
||||
<Footer />
|
||||
</FooterWrapper>
|
||||
</AppWrapper>
|
||||
</Suspense>
|
||||
</>
|
||||
)
|
||||
|
@ -93,8 +93,17 @@ export const GlobalStyle = createGlobalStyle`
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body > div {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
font-variant: none;
|
||||
|
Loading…
Reference in New Issue
Block a user