fix slider, submit and edit contribution screens

This commit is contained in:
saml33 2021-04-29 23:51:16 +10:00
parent 0ecbe23f41
commit a55ea4ed74
6 changed files with 261 additions and 104 deletions

View File

@ -1,4 +1,37 @@
import { FunctionComponent } from 'react'
import styled from '@emotion/styled'
import tw from 'twin.macro'
type StyledButtonProps = {
secondary: boolean
}
const StyledButton = styled.button<StyledButtonProps>`
:before {
${tw`absolute left-0 top-0 opacity-0 h-full w-full block bg-gradient-to-tl from-secondary-1-light via-secondary-1-dark to-secondary-2-light transition-opacity duration-500`}
border-radius: inherit;
content: '';
z-index: -10;
}
:hover {
:before {
${tw`opacity-100`};
${({ disabled, secondary }) => (disabled || secondary) && tw`hidden`}
}
}
:focus {
${tw`ring-4 ring-secondary-2-light ring-opacity-40`}
${({ secondary }) => secondary && tw`ring-0`}
}
:active {
:before {
${tw`ring-4 ring-secondary-2-light ring-opacity-40`}
}
}
`
interface ButtonProps {
onClick?: (x?) => void
@ -16,20 +49,21 @@ const Button: FunctionComponent<ButtonProps> = ({
...props
}) => {
return (
<button
<StyledButton
onClick={onClick}
disabled={disabled}
className={`${className} ${
secondary || disabled
? 'bg-bkg-4'
: 'bg-gradient-to-br from-secondary-1-light via-secondary-1-dark to-secondary-2-light'
} bg-bkg-4 border-none default-transition px-6 py-2 rounded-lg text-fgd-1
active:border-primary hover:bg-bkg-3 focus:outline-none
} relative z-10 bg-bkg-4 border-none default-transition px-6 py-2 rounded-lg text-fgd-1
active:border-primary hover:bg-bkg-3 focus:outline-none
disabled:cursor-not-allowed`}
secondary={secondary}
{...props}
>
{children}
</button>
</StyledButton>
)
}

View File

@ -4,6 +4,7 @@ import { WALLET_PROVIDERS, DEFAULT_PROVIDER } from '../hooks/useWallet'
import useLocalStorageState from '../hooks/useLocalStorageState'
import WalletSelect from './WalletSelect'
import WalletIcon from './WalletIcon'
import Button from './Button'
const StyledWalletTypeLabel = styled.div`
font-size: 0.6rem;
@ -43,3 +44,15 @@ const ConnectWalletButton = () => {
}
export default ConnectWalletButton
export const ConnectWalletButtonSmall = ({ children, onClick }) => (
<div className="relative">
<Button
className="rounded-full h-9 w-44 z-30 relative"
onClick={() => onClick()}
>
<div className="flex items-center justify-center text-sm">{children}</div>
</Button>
<div className="absolute animate-ping-small bg-secondary-2-light top-0 rounded-full h-9 w-44 z-20" />
</div>
)

View File

@ -1,24 +1,43 @@
import { useEffect, useState } from 'react'
import { LinkIcon } from '@heroicons/react/solid'
import styled from '@emotion/styled'
import tw from 'twin.macro'
import {
LinkIcon,
LockClosedIcon,
LockOpenIcon,
} from '@heroicons/react/outline'
import useWalletStore from '../stores/useWalletStore'
import { getUsdcBalance } from '../utils'
import Input from './Input'
import Button from './Button'
import { ConnectWalletButtonSmall } from './ConnectWalletButton'
import Slider from './Slider'
import Loading from './Loading'
import WalletIcon from './WalletIcon'
const StyledModalWrapper = styled.div`
height: 414px;
${tw`bg-bkg-2 border border-bkg-3 flex flex-col items-center rounded-lg shadow-lg p-7 w-96`}
}
`
const ContributionModal = () => {
const connected = useWalletStore((s) => s.connected)
const wallet = useWalletStore((s) => s.current)
const usdcBalance = getUsdcBalance()
const [contributionAmount, setContributionAmount] = useState(null)
const [sliderPercentage, setSliderPercentage] = useState(null)
const [submitting, setSubmitting] = useState(false)
const [submitted, setSubmitted] = useState(false)
const [editContribution, setEditContribution] = useState(false)
const [loading, setLoading] = useState(true)
const handleConnectDisconnect = () => {
if (connected) {
setContributionAmount(null)
setSubmitted(false)
setEditContribution(false)
wallet.disconnect()
} else {
wallet.connect()
@ -27,6 +46,27 @@ const ContributionModal = () => {
const handleSetContribution = () => {
setSubmitting(true)
setEditContribution(false)
}
const handleEditContribution = () => {
setEditContribution(true)
setSubmitted(false)
}
const onChangeAmountInput = (amount) => {
setContributionAmount(amount)
setSliderPercentage((amount / usdcBalance) * 100)
}
const onChangeSlider = (percentage) => {
setContributionAmount((percentage / 100) * usdcBalance)
setSliderPercentage(percentage)
}
const handleMax = () => {
setContributionAmount(usdcBalance)
setSliderPercentage(100)
}
useEffect(() => {
@ -37,107 +77,164 @@ const ContributionModal = () => {
}, [usdcBalance])
useEffect(() => {
const submitTimer = setTimeout(() => {
setSubmitting(false)
}, 1000)
return () => clearTimeout(submitTimer)
if (submitting) {
const submitTimer = setTimeout(() => {
setSubmitted(true)
setSubmitting(false)
}, 2000)
return () => clearTimeout(submitTimer)
}
}, [submitting])
return (
<div className="bg-bkg-2 border border-bkg-3 flex flex-col items-center rounded-lg shadow-lg p-7 w-96">
<StyledModalWrapper>
<div className="pb-4 text-center">
<h2>Plant your seed</h2>
<p>This is the start of something big.</p>
{!submitted && !submitting && !editContribution ? (
<>
<h2>Plant your seed</h2>
<p>This is the start of something big.</p>
</>
) : null}
{!submitted && submitting ? (
<>
<h2>Approve the transaction</h2>
<p>Almost there...</p>
</>
) : null}
{submitted && !submitting ? (
<>
<h2>Your contribution amount</h2>
<p>A new seed planted...</p>
</>
) : null}
{editContribution && !submitting ? (
<>
<h2>Funds unlocked</h2>
<p>Increase or reduce your contribution...</p>
</>
) : null}
</div>
<div
className={`${
connected ? 'opacity-100' : 'opacity-30'
} pb-6 transiton-all duration-1000 w-full`}
>
<div className="pb-20">
<div className="flex justify-between pb-2">
<div className="flex items-center text-xs text-fgd-4">
<WalletIcon className="w-4 h-4 mr-1 text-secondary-1-dark fill-current" />
{connected ? (
loading ? (
<div className="bg-bkg-4 rounded w-10 h-4 animate-pulse" />
{submitting ? (
<div className="flex items-center h-full">
<Loading className="h-6 w-6 mb-3 text-primary-light" />
</div>
) : (
<>
<div
className={`${
connected ? 'opacity-100' : 'opacity-30'
} pb-6 transiton-all duration-1000 w-full`}
>
<div className="flex justify-between pb-2">
<div className="flex items-center text-xs text-fgd-4">
<WalletIcon className="w-4 h-4 mr-1 text-fgd-3 fill-current" />
{connected ? (
loading ? (
<div className="bg-bkg-4 rounded w-10 h-4 animate-pulse" />
) : (
<span className="font-display text-fgd-1 ml-1">
{usdcBalance}
</span>
)
) : (
<span className="font-display text-fgd-1 ml-1">
{usdcBalance}
</span>
)
) : (
'----'
)}
<img
alt=""
width="16"
height="16"
src="/icons/usdc.svg"
className={`ml-1`}
'----'
)}
<img
alt=""
width="16"
height="16"
src="/icons/usdc.svg"
className={`ml-1`}
/>
</div>
<div className="flex">
{submitted ? (
<Button
className="bg-secondary-2-light hover:bg-secondary-2-dark font-normal rounded text-fgd-1 text-xs py-0.5 px-1.5 mr-2"
disabled={!connected}
onClick={() => handleEditContribution()}
secondary
>
Unlock
</Button>
) : null}
<Button
className={`${
submitted && 'opacity-30'
} bg-bkg-4 font-normal rounded text-fgd-3 text-xs py-0.5 px-1.5`}
disabled={!connected || submitted}
onClick={() => handleMax()}
secondary
>
Max
</Button>
</div>
</div>
<div className="flex items-center pb-4 relative">
{submitted ? (
<LockClosedIcon className="absolute text-secondary-2-light h-4 w-4 mb-0.5 left-2 z-10" />
) : null}
{editContribution ? (
<LockOpenIcon className="absolute text-secondary-1-light h-4 w-4 mb-0.5 left-2 z-10" />
) : null}
<Input
className={(submitted || editContribution) && 'pl-7'}
disabled={!connected || submitted || loading}
type="text"
onChange={(e) => onChangeAmountInput(e.target.value)}
value={loading ? '' : contributionAmount}
suffix="USDC"
/>
</div>
<div
className={`${
!submitted ? 'opacity-100' : 'opacity-30'
} transiton-all duration-1000`}
>
<div className="pb-20">
<Slider
disabled={submitted || !connected || loading}
value={sliderPercentage}
onChange={(v) => onChangeSlider(v)}
step={1}
/>
</div>
<Button
onClick={() => handleSetContribution()}
className="w-full py-2.5"
disabled={!connected || submitted}
>
<div className={`flex items-center justify-center`}>
Set Contribution
</div>
</Button>
</div>
</div>
{connected ? (
<Button
className="bg-bkg-4 font-normal rounded text-fgd-3 text-xs py-0.5 px-2"
disabled={!connected}
onClick={() => setContributionAmount(100)}
className="rounded-full bg-bkg-4 text-fgd-3 font-normal"
onClick={() => handleConnectDisconnect()}
secondary
>
Max
<div className="flex items-center text-sm">
<LinkIcon className="h-4 w-4 mr-1" />
Disconnect
</div>
</Button>
</div>
<div className="pb-4">
<Input
disabled={!connected}
type="text"
onChange={(e) => setContributionAmount(e.target.value)}
value={loading ? '' : usdcBalance * (contributionAmount / 100)}
suffix="USDC"
/>
</div>
<Slider
value={contributionAmount}
onChange={(v) => setContributionAmount(v)}
step={usdcBalance / 100}
/>
</div>
<Button
onClick={() => handleSetContribution()}
className="w-full py-2.5"
disabled={!connected}
>
<div className={`flex items-center justify-center`}>
{submitting && <Loading />}
Set Contribution
</div>
</Button>
</div>
{connected ? (
<Button
className="rounded-full bg-bkg-4 text-fgd-3 font-normal"
onClick={() => handleConnectDisconnect()}
secondary
>
<div className="flex items-center text-sm">
<LinkIcon className="h-4 w-4 mr-1" />
Disconnect
</div>
</Button>
) : (
<div className="relative">
<Button
className="rounded-full h-9 w-44 z-20 relative"
onClick={() => handleConnectDisconnect()}
>
<div className="flex items-center justify-center text-sm">
<LinkIcon className="h-4 w-4 mr-1" />
Connect Wallet
</div>
</Button>
<div className="absolute animate-ping-small bg-secondary-2-light top-0 rounded-full h-9 w-44 z-10" />
</div>
) : (
<ConnectWalletButtonSmall onClick={handleConnectDisconnect}>
<div className="flex items-center justify-center text-sm">
<LinkIcon className="h-4 w-4 mr-1" />
Connect Wallet
</div>
</ConnectWalletButtonSmall>
)}
</>
)}
</div>
</StyledModalWrapper>
)
}

View File

@ -35,7 +35,7 @@ const Input = ({
className={`${className} font-display px-2 py-2 w-full bg-bkg-1 rounded text-fgd-1
border border-fgd-4 default-transition hover:border-primary-dark
focus:border-primary-light focus:outline-none
${disabled ? 'cursor-not-allowed hover:border-fgd-4' : ''}
${disabled ? 'cursor-not-allowed hover:border-fgd-4 text-fgd-3' : ''}
${prefix ? 'rounded-l-none' : ''}`}
disabled={disabled}
{...props}

View File

@ -1,7 +1,13 @@
const Loading = () => {
import { FunctionComponent } from 'react'
interface LoadingProps {
className?: string
}
const Loading: FunctionComponent<LoadingProps> = ({ className }) => {
return (
<svg
className={`animate-spin -ml-1 mr-3 h-5 w-5`}
className={`${className} animate-spin h-5 w-5`}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
@ -15,7 +21,7 @@ const Loading = () => {
strokeWidth="4"
></circle>
<path
className={`opacity-75`}
className={`opacity-90`}
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>

View File

@ -5,7 +5,8 @@ import Slider from 'rc-slider'
import 'rc-slider/assets/index.css'
type StyledSliderProps = {
enableTransition: boolean
enableTransition?: boolean
disabled?: boolean
}
const StyledSlider = styled(Slider)<StyledSliderProps>`
@ -14,12 +15,10 @@ const StyledSlider = styled(Slider)<StyledSliderProps>`
}
.rc-slider-track {
${tw`bg-gradient-to-r from-secondary-1-light via-primary-light to-secondary-2-light h-2.5 rounded-full`}
${tw`bg-gradient-to-r from-secondary-1-dark via-primary-light to-secondary-2-light h-2.5 rounded-full ring-1 ring-primary-light ring-inset`}
${({ enableTransition }) =>
enableTransition && tw`transition-all duration-500`}
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.3);
}
.rc-slider-step {
@ -29,11 +28,11 @@ const StyledSlider = styled(Slider)<StyledSliderProps>`
.rc-slider-handle {
${tw`bg-fgd-1 border-4 border-primary-dark h-4 w-4`}
${({ enableTransition }) =>
enableTransition && tw`transition-all duration-500`}
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.3);
margin-top: -3px;
${({ enableTransition }) =>
enableTransition && tw`transition-all duration-500`}
}
.rc-slider-mark-text {
@ -52,12 +51,16 @@ const StyledSlider = styled(Slider)<StyledSliderProps>`
.rc-slider-mark-text:last-of-type {
padding-right: 24px;
}
${({ disabled }) => disabled && 'background-color: transparent'}
`
type SliderProps = {
onChange: (...args: any[]) => any
step: number
value: number
disabled: boolean
max?: number
}
const marks = {
@ -72,12 +75,15 @@ const AmountSlider: FunctionComponent<SliderProps> = ({
onChange,
step,
value,
disabled,
max,
}) => {
const [enableTransition, setEnableTransition] = useState(true)
return (
<StyledSlider
min={0}
max={max}
value={value || 0}
onChange={onChange}
step={step}
@ -85,6 +91,7 @@ const AmountSlider: FunctionComponent<SliderProps> = ({
enableTransition={enableTransition}
onBeforeChange={() => setEnableTransition(false)}
onAfterChange={() => setEnableTransition(true)}
disabled={disabled}
/>
)
}