Skip to content

Commit

Permalink
Create a basic TokenBalanceInput component
Browse files Browse the repository at this point in the history
  • Loading branch information
kkosiorowska committed Dec 18, 2023
1 parent 38fd08c commit a26798b
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 6 deletions.
3 changes: 2 additions & 1 deletion dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"@ledgerhq/wallet-api-client-react": "^1.1.2",
"framer-motion": "^10.16.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-number-format": "^5.3.1"
},
"devDependencies": {
"@thesis-co/eslint-config": "^0.6.1",
Expand Down
9 changes: 5 additions & 4 deletions dapp/src/components/shared/CurrencyBalance/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useMemo } from "react"
import { Box, useMultiStyleConfig } from "@chakra-ui/react"
import { Box, useMultiStyleConfig, TextProps } from "@chakra-ui/react"
import { formatTokenAmount, toLocaleString } from "../../../utils"
import { CurrencyType } from "../../../types"
import { CURRENCIES_BY_TYPE } from "../../../constants"
Expand All @@ -11,7 +11,7 @@ export type CurrencyBalanceProps = {
desiredDecimals?: number
size?: string
variant?: "greater-balance"
}
} & TextProps

export function CurrencyBalance({
currencyType,
Expand All @@ -20,6 +20,7 @@ export function CurrencyBalance({
desiredDecimals = 2,
size,
variant,
...textProps
}: CurrencyBalanceProps) {
const styles = useMultiStyleConfig("CurrencyBalance", { size, variant })

Expand All @@ -38,10 +39,10 @@ export function CurrencyBalance({

return (
<Box>
<Box as="span" __css={styles.balance}>
<Box as="span" __css={styles.balance} {...textProps}>
{balance}
</Box>
<Box as="span" __css={styles.symbol}>
<Box as="span" __css={styles.symbol} {...textProps}>
{currency.symbol}
</Box>
</Box>
Expand Down
47 changes: 47 additions & 0 deletions dapp/src/components/shared/NumberFormatInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from "react"
import { NumericFormat } from "react-number-format"
import { InputProps, chakra, useMultiStyleConfig } from "@chakra-ui/react"

const ChakraWrapper = chakra(NumericFormat)

export type NumberFormatInputValues = {
formattedValue: string
value: string
floatValue: number
}

type NumberFormatInputProps = {
onValueChange: (values: NumberFormatInputValues) => void
decimalScale?: number
} & InputProps

/**
* Component is from the Threshold Network React Components repository.
* It has been used because it supports the thousandth separator
* and can be easily integrated with Chakra UI.
*
* More info:
* https://github.com/threshold-network/components/blob/main/src/components/NumberFormatInput/index.tsx
*/
const NumberFormatInput = React.forwardRef<
HTMLInputElement,
NumberFormatInputProps
>((props, ref) => {
const { field: css } = useMultiStyleConfig("Input", props)

const { decimalScale, isDisabled, ...restProps } = props

return (
<ChakraWrapper
allowLeadingZeros={false}
thousandSeparator
decimalScale={decimalScale}
__css={css}
disabled={isDisabled}
getInputRef={ref}
{...restProps}
/>
)
})

export default NumberFormatInput
170 changes: 170 additions & 0 deletions dapp/src/components/shared/TokenBalanceInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React, { useMemo } from "react"
import {
Box,
Button,
HStack,
Icon,
InputGroup,
InputProps,
InputRightElement,
TypographyProps,
createStylesContext,
useMultiStyleConfig,
} from "@chakra-ui/react"
import { fixedPointNumberToString } from "../../../utils"
import { CurrencyType } from "../../../types"
import { CURRENCIES_BY_TYPE } from "../../../constants"
import NumberFormatInput, {
NumberFormatInputValues,
} from "../NumberFormatInput"
import { CurrencyBalance } from "../CurrencyBalance"
import { Alert } from "../../../static/icons"

const VARIANT = "balance"
const [StylesProvider, useStyles] = createStylesContext("TokenBalanceInput")

type HelperErrorTextProps = {
errorMsgText?: string | JSX.Element
hasError?: boolean
helperText?: string | JSX.Element
}

function HelperErrorText({
helperText,
errorMsgText,
hasError,
}: HelperErrorTextProps) {
const styles = useStyles()

if (hasError) {
return (
<Box as="span" __css={styles.errorMsgText}>
{errorMsgText || "Please enter a valid value"}
</Box>
)
}

if (helperText) {
return (
<HStack __css={styles.helperText}>
<Icon as={Alert} />
<Box as="span">{helperText}</Box>
</HStack>
)
}

return null
}

type FiatCurrencyBalanceProps = {
fiatAmount?: string
fiatCurrencyType?: CurrencyType
}

function FiatCurrencyBalance({
fiatAmount,
fiatCurrencyType,
}: FiatCurrencyBalanceProps) {
const { helperText } = useStyles()
const textProps = helperText as TypographyProps

if (fiatAmount && fiatCurrencyType) {
return (
<CurrencyBalance
currencyType={fiatCurrencyType}
amount={fiatAmount}
shouldBeFormatted={false}
{...textProps}
/>
)
}

return null
}

type TokenBalanceInputProps = {
amount?: string
currencyType: CurrencyType
tokenBalance: string | number
placeholder?: string
size?: "lg" | "md"
setAmount: (value: string) => void
} & InputProps &
HelperErrorTextProps &
FiatCurrencyBalanceProps

export default function TokenBalanceInput({
amount,
currencyType,
tokenBalance,
placeholder,
size = "lg",
setAmount,
errorMsgText,
helperText,
hasError = false,
fiatAmount,
fiatCurrencyType,
...inputProps
}: TokenBalanceInputProps) {
const styles = useMultiStyleConfig("TokenBalanceInput", { size })

const tokenBalanceAmount = useMemo(
() =>
fixedPointNumberToString(
BigInt(tokenBalance || 0),
CURRENCIES_BY_TYPE[currencyType].decimals,
),
[currencyType, tokenBalance],
)

return (
<Box __css={styles.container}>
<Box __css={styles.labelContainer}>
<Box as="span" __css={styles.label}>
Amount
</Box>
<HStack>
<Box as="span" __css={styles.balance}>
Balance
</Box>
<CurrencyBalance
size={size === "lg" ? "md" : "sm"}
amount={tokenBalance}
currencyType={currencyType}
/>
</HStack>
</Box>
<InputGroup variant={VARIANT}>
<NumberFormatInput
size={size}
value={amount}
variant={VARIANT}
placeholder={placeholder}
onValueChange={(values: NumberFormatInputValues) =>
setAmount(values.value)
}
{...inputProps}
/>
<InputRightElement>
<Button h="70%" onClick={() => setAmount(tokenBalanceAmount)}>
Max
</Button>
</InputRightElement>
</InputGroup>
<StylesProvider value={styles}>
<HelperErrorText
helperText={helperText}
errorMsgText={errorMsgText}
hasError={hasError}
/>
{!hasError && !helperText && (
<FiatCurrencyBalance
fiatAmount={fiatAmount}
fiatCurrencyType={fiatCurrencyType}
/>
)}
</StylesProvider>
</Box>
)
}
2 changes: 1 addition & 1 deletion dapp/src/constants/currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ export const CURRENCY_ID_ETHEREUM =
export const CURRENCIES_BY_TYPE: Record<CurrencyType, Currency> = {
bitcoin: BITCOIN,
ethereum: ETHEREUM,
usd: ETHEREUM,
usd: USD,
}
24 changes: 24 additions & 0 deletions dapp/src/static/icons/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react"
import { createIcon } from "@chakra-ui/react"

export const Alert = createIcon({
displayName: "Alert",
viewBox: "0 0 16 16",
path: [
<g clipPath="url(#clip0_3178_2454)">
<path
d="M7.99967 5.33325V7.99992M7.99967 10.6666H8.00634M14.6663 7.99992C14.6663 11.6818 11.6816 14.6666 7.99967 14.6666C4.31778 14.6666 1.33301 11.6818 1.33301 7.99992C1.33301 4.31802 4.31778 1.33325 7.99967 1.33325C11.6816 1.33325 14.6663 4.31802 14.6663 7.99992Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
</g>,
<defs>
<clipPath id="clip0_3178_2454">
<rect width="16" height="16" fill="currentColor" />
</clipPath>
</defs>,
],
})
1 change: 1 addition & 0 deletions dapp/src/static/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./Bitcoin"
export * from "./Ethereum"
export * from "./ArrowUpRight"
export * from "./AcreLogo"
export * from "./Alert"
40 changes: 40 additions & 0 deletions dapp/src/theme/Input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { inputAnatomy as parts } from "@chakra-ui/anatomy"
import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react"

const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys)

const variantBalanceField = defineStyle({
border: "1px solid",
borderColor: "gold.300",
color: "grey.700",
fontWeight: "bold",
bg: "opacity.white.5",
paddingRight: 20,
// TODO: Set the color correctly without using the chakra variable.
caretColor: "var(--chakra-colors-brand-400)",

_placeholder: {
color: "grey.300",
fontWeight: "medium",
},
})

const variantBalanceElement = defineStyle({
h: "100%",
width: 14,
mr: 2,
})

const variantBalance = definePartsStyle({
field: variantBalanceField,
element: variantBalanceElement,
})

const variants = {
balance: variantBalance,
}

const Input = defineMultiStyleConfig({ variants })

export default Input
Loading

0 comments on commit a26798b

Please sign in to comment.