mirror of
https://github.com/kremalicious/asi-calculator.git
synced 2024-12-22 01:13:17 +01:00
loading ui
This commit is contained in:
parent
e2c21cf497
commit
4850c19417
@ -1,6 +1,7 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Hanken_Grotesk } from 'next/font/google'
|
||||
import '@/styles/globals.css'
|
||||
import '@/styles/loading-ui.css'
|
||||
|
||||
const hankenGrotesk = Hanken_Grotesk({
|
||||
subsets: ['latin'],
|
||||
|
@ -6,34 +6,48 @@ import { usePrices } from '@/hooks'
|
||||
import { Label } from '@/components/Label'
|
||||
|
||||
export function CalculationBase() {
|
||||
const { prices, isValidating } = usePrices()
|
||||
const { prices, isValidating, isLoading } = usePrices()
|
||||
|
||||
const feedbackClasses = isLoading
|
||||
? 'isLoading'
|
||||
: isValidating
|
||||
? 'isValidating'
|
||||
: ''
|
||||
|
||||
return (
|
||||
<ul className={styles.calculationBase}>
|
||||
<li>
|
||||
<p>1 ASI</p>
|
||||
<p className={isValidating ? 'isValidating' : ''}>= ${prices.asi}</p>
|
||||
<p>
|
||||
= <span className={feedbackClasses}>${prices.asi}</span>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
1 Fet = {ratioFetToAsi} ASI
|
||||
<Label>fixed</Label>
|
||||
</p>
|
||||
<p className={isValidating ? 'isValidating' : ''}>= ${prices.fet}</p>
|
||||
<p>
|
||||
= <span className={feedbackClasses}>${prices.fet}</span>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
1 OCEAN = {ratioOceanToAsi} ASI
|
||||
<Label>fixed</Label>
|
||||
</p>
|
||||
<p className={isValidating ? 'isValidating' : ''}>= ${prices.ocean}</p>
|
||||
<p>
|
||||
= <span className={feedbackClasses}>${prices.ocean}</span>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
1 AGIX = {ratioAgixToAsi} ASI
|
||||
<Label>fixed</Label>
|
||||
</p>
|
||||
<p className={isValidating ? 'isValidating' : ''}>= ${prices.agix}</p>
|
||||
<p>
|
||||
= <span className={feedbackClasses}>${prices.agix}</span>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
|
@ -3,4 +3,5 @@
|
||||
border: 1px solid rgba(var(--foreground-rgb), 0.15);
|
||||
border-radius: var(--border-radius);
|
||||
overflow: hidden;
|
||||
margin: 0 0.25rem;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
width: 60px;
|
||||
padding: 0 0.2rem;
|
||||
text-align: center;
|
||||
border-right: 1px solid rgba(var(--foreground-rgb), 0.15);
|
||||
}
|
||||
|
||||
.input:hover {
|
||||
|
@ -1,7 +1,26 @@
|
||||
.select {
|
||||
display: inline-block;
|
||||
all: unset;
|
||||
padding: 0 0.75rem 0 0;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.select:hover:not(.select[disabled]) {
|
||||
background-color: rgba(var(--background-rgb), 0.5);
|
||||
}
|
||||
|
||||
.select:focus-within {
|
||||
outline: none;
|
||||
background-color: rgba(var(--background-rgb), 0.9);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.select:hover:not(.select[disabled]) {
|
||||
background-color: rgba(var(--foreground-rgb), 0.1);
|
||||
}
|
||||
|
||||
.select:focus-within {
|
||||
background-color: rgba(var(--foreground-rgb), 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.selectWrapper {
|
||||
@ -10,7 +29,8 @@
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
right: 0.175rem;
|
||||
right: 0.1rem;
|
||||
width: 1em;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
.resultLine {
|
||||
display: inline-grid;
|
||||
grid-template-columns: 24px 2fr 3fr;
|
||||
grid-template-columns: 24px 40% 40%;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
@ -11,6 +11,7 @@ type Props = {
|
||||
amountFiat: number
|
||||
amountOriginalFiat?: number
|
||||
isValidating: boolean
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export function Result({
|
||||
@ -19,36 +20,48 @@ export function Result({
|
||||
amountAsi,
|
||||
amountFiat,
|
||||
amountOriginalFiat,
|
||||
isValidating
|
||||
isValidating,
|
||||
isLoading
|
||||
}: Props) {
|
||||
const feedbackClasses = isLoading
|
||||
? 'isLoading'
|
||||
: isValidating
|
||||
? 'isValidating'
|
||||
: ''
|
||||
|
||||
return (
|
||||
<div className={styles.result}>
|
||||
<div className={styles.resultLine}>
|
||||
<TokenLogo token={token} />
|
||||
|
||||
<span className={isValidating ? 'isValidating' : ''}>
|
||||
{formatNumber(amount || 0, token?.symbol || '')}
|
||||
</span>
|
||||
<p>
|
||||
<span className={feedbackClasses}>
|
||||
{formatNumber(amount || 0, token?.symbol || '')}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
{amountOriginalFiat ? (
|
||||
<span className={styles.fiat}>
|
||||
{formatNumber(amountOriginalFiat || 0, 'USD')}
|
||||
</span>
|
||||
<p>
|
||||
<span className={`${styles.fiat} ${feedbackClasses}`}>
|
||||
{formatNumber(amountOriginalFiat || 0, 'USD')}
|
||||
</span>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className={styles.resultLine}>
|
||||
<ArrowRightIcon className={styles.iconArrow} />
|
||||
<strong
|
||||
title={`${amountAsi}`}
|
||||
className={isValidating ? 'isValidating' : ''}
|
||||
>
|
||||
{formatNumber(amountAsi || 0, 'ASI')}
|
||||
</strong>
|
||||
<strong
|
||||
className={`${styles.fiat} ${isValidating ? 'isValidating' : ''}`}
|
||||
>
|
||||
{formatNumber(amountFiat || 0, 'USD')}
|
||||
</strong>
|
||||
|
||||
<p>
|
||||
<strong title={`${amountAsi}`} className={feedbackClasses}>
|
||||
{formatNumber(amountAsi || 0, 'ASI')}
|
||||
</strong>
|
||||
</p>
|
||||
<p>
|
||||
<strong className={`${styles.fiat} ${feedbackClasses}`}>
|
||||
{formatNumber(amountFiat || 0, 'USD')}
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ import { FormAmount } from '@/components/FormAmount'
|
||||
import { getTokenBySymbol } from '@/utils'
|
||||
|
||||
export function Buy() {
|
||||
const { prices, isValidating } = usePrices()
|
||||
const { prices, isValidating, isLoading } = usePrices()
|
||||
const [amount, setAmount] = useState(100)
|
||||
const [debouncedAmount] = useDebounce(amount, 500)
|
||||
|
||||
@ -33,6 +33,7 @@ export function Buy() {
|
||||
: 0
|
||||
}
|
||||
isValidating={isValidating}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Result
|
||||
token={getTokenBySymbol('AGIX')}
|
||||
@ -46,6 +47,7 @@ export function Buy() {
|
||||
: 0
|
||||
}
|
||||
isValidating={isValidating}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<Result
|
||||
token={getTokenBySymbol('FET')}
|
||||
@ -59,6 +61,7 @@ export function Buy() {
|
||||
: 0
|
||||
}
|
||||
isValidating={isValidating}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
@ -5,6 +5,10 @@ import { fetcher, getTokenAddressBySymbol, getTokenBySymbol } from '@/utils'
|
||||
import useSWR from 'swr'
|
||||
import { TokenSymbol } from '@/types'
|
||||
|
||||
const options = {
|
||||
keepPreviousData: true // so loading UI can kick in properly
|
||||
}
|
||||
|
||||
export function SwapResults({
|
||||
tokenSymbol,
|
||||
amount
|
||||
@ -12,30 +16,49 @@ export function SwapResults({
|
||||
tokenSymbol: TokenSymbol
|
||||
amount: number
|
||||
}) {
|
||||
const { prices, isValidating: isValidatingPrices } = usePrices()
|
||||
const {
|
||||
prices,
|
||||
isValidating: isValidatingPrices,
|
||||
isLoading: isLoadingPrices
|
||||
} = usePrices()
|
||||
|
||||
// -> AGIX
|
||||
const { data: dataSwapToAgix, isValidating: isValidatingToAgix } = useSWR(
|
||||
const {
|
||||
data: dataSwapToAgix,
|
||||
isValidating: isValidatingToAgix,
|
||||
isLoading: isLoadingToAgix
|
||||
} = useSWR(
|
||||
`/api/quote/?tokenIn=${getTokenAddressBySymbol(
|
||||
tokenSymbol
|
||||
)}&tokenOut=${getTokenAddressBySymbol('AGIX')}&amountIn=${amount}`,
|
||||
fetcher
|
||||
fetcher,
|
||||
options
|
||||
)
|
||||
|
||||
// -> FET
|
||||
const { data: dataSwapToFet, isValidating: isValidatingToFet } = useSWR(
|
||||
const {
|
||||
data: dataSwapToFet,
|
||||
isValidating: isValidatingToFet,
|
||||
isLoading: isLoadingToFet
|
||||
} = useSWR(
|
||||
`/api/quote/?tokenIn=${getTokenAddressBySymbol(
|
||||
tokenSymbol
|
||||
)}&tokenOut=${getTokenAddressBySymbol('FET')}&amountIn=${amount}`,
|
||||
fetcher
|
||||
fetcher,
|
||||
options
|
||||
)
|
||||
|
||||
// -> OCEAN
|
||||
const { data: dataSwapToOcean, isValidating: isValidatingToOcean } = useSWR(
|
||||
const {
|
||||
data: dataSwapToOcean,
|
||||
isValidating: isValidatingToOcean,
|
||||
isLoading: isLoadingToOcean
|
||||
} = useSWR(
|
||||
`/api/quote/?tokenIn=${getTokenAddressBySymbol(
|
||||
tokenSymbol
|
||||
)}&tokenOut=${getTokenAddressBySymbol('OCEAN')}&amountIn=${amount}`,
|
||||
fetcher
|
||||
fetcher,
|
||||
options
|
||||
)
|
||||
|
||||
return (
|
||||
@ -66,6 +89,7 @@ export function SwapResults({
|
||||
: undefined
|
||||
}
|
||||
isValidating={isValidatingToOcean || isValidatingPrices}
|
||||
isLoading={isLoadingToOcean || isLoadingPrices}
|
||||
/>
|
||||
|
||||
<Result
|
||||
@ -90,6 +114,7 @@ export function SwapResults({
|
||||
prices.agix
|
||||
}
|
||||
isValidating={isValidatingToAgix || isValidatingPrices}
|
||||
isLoading={isLoadingToAgix || isLoadingPrices}
|
||||
/>
|
||||
|
||||
<Result
|
||||
@ -110,6 +135,7 @@ export function SwapResults({
|
||||
prices.asi
|
||||
}
|
||||
isValidating={isValidatingToFet || isValidatingPrices}
|
||||
isLoading={isLoadingToFet || isLoadingPrices}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
@ -1,15 +0,0 @@
|
||||
.isValidating {
|
||||
animation: flicker 2s infinite ease-out;
|
||||
}
|
||||
|
||||
@keyframes flicker {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
@ -40,5 +40,3 @@ ul {
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
@import './_animations.css';
|
||||
|
53
styles/loading-ui.css
Normal file
53
styles/loading-ui.css
Normal file
@ -0,0 +1,53 @@
|
||||
.isLoading {
|
||||
background-color: rgba(0, 0, 0, 0) !important;
|
||||
background-image: linear-gradient(
|
||||
-45deg,
|
||||
rgba(var(--foreground-rgb), 0) 0,
|
||||
rgba(var(--foreground-rgb), 0.1) 50%,
|
||||
rgba(var(--foreground-rgb), 0) 100%
|
||||
) !important;
|
||||
background-size: 800% 800% !important;
|
||||
color: rgba(0, 0, 0, 0) !important;
|
||||
border-color: rgba(0, 0, 0, 0) !important;
|
||||
border-radius: var(--border-radius) !important;
|
||||
user-select: none;
|
||||
cursor: wait;
|
||||
animation: loading 3s infinite ease-out !important;
|
||||
}
|
||||
|
||||
.isLoading * {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.isLoading:empty::after,
|
||||
.isLoading *:empty::after {
|
||||
content: '\00a0';
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.isValidating {
|
||||
animation: flicker 2s infinite ease-out;
|
||||
}
|
||||
|
||||
@keyframes flicker {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user