From 55417f59e9a0672a4e2e468e1a765d63b17d046f Mon Sep 17 00:00:00 2001 From: nikhil arora Date: Mon, 31 Oct 2022 12:55:23 +0530 Subject: [PATCH 01/60] feat: TokenInput component --- packages/frontend/pages/_document.tsx | 5 + packages/frontend/pages/new-lp.tsx | 220 +++++++++++++++++++ packages/frontend/public/images/eth-logo.svg | 21 ++ packages/frontend/src/theme.ts | 96 ++++++-- 4 files changed, 323 insertions(+), 19 deletions(-) create mode 100644 packages/frontend/pages/new-lp.tsx create mode 100644 packages/frontend/public/images/eth-logo.svg diff --git a/packages/frontend/pages/_document.tsx b/packages/frontend/pages/_document.tsx index aaa8fff62..27fd470c1 100644 --- a/packages/frontend/pages/_document.tsx +++ b/packages/frontend/pages/_document.tsx @@ -11,6 +11,11 @@ export default class MyDocument extends Document { href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@100;300;400;500;700&display=swap" rel="stylesheet" /> + {/* new font introduced with the new lp page */} +
diff --git a/packages/frontend/pages/new-lp.tsx b/packages/frontend/pages/new-lp.tsx new file mode 100644 index 000000000..0a51febbe --- /dev/null +++ b/packages/frontend/pages/new-lp.tsx @@ -0,0 +1,220 @@ +import Nav from '@components/Nav' +import { createStyles, makeStyles } from '@material-ui/core/styles' +import { Grid, Typography, Box } from '@material-ui/core' +import React, { useState } from 'react' +import { ThemeProvider } from '@material-ui/core/styles' +import Image from 'next/image' + +import getTheme, { Mode } from '../src/theme' +import ethLogo from '../public/images/eth-logo.svg' + +const useStyles = makeStyles((theme) => + createStyles({ + container: { + padding: theme.spacing(5, 10), + maxWidth: '1500px', + width: '95%', + marginLeft: 'auto', + marginRight: 'auto', + }, + sectionHeader: { + fontSize: theme.typography.pxToRem(18), + fontWeight: 700, + marginBottom: theme.spacing(1), + }, + }), +) + +const useInputStyles = makeStyles((theme) => + createStyles({ + container: { + width: '340px', + backgroundColor: 'inherit', + textAlign: 'left', + position: 'relative', + marginBottom: '48px', + }, + inputContainer: { + display: 'flex', + justifyContent: 'space-between', + border: `2px solid ${theme.palette.background.lightStone}`, + borderRadius: '12px', + padding: theme.spacing(2), + marginTop: '1em', + backgroundColor: theme.palette.background.default, + }, + leftInputContainer: {}, + input: { + display: 'inline-block', + border: 'none', + backgroundColor: 'inherit', + outline: 'none', + fontSize: '22px', + width: '204px', + + color: theme.palette.text.primary, + fontWeight: theme.typography.fontWeightBold, + fontFamily: theme.typography.fontFamily, + }, + unitsContainer: {}, + rightInputContainer: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + padding: '0px 10px', + height: '28px', + backgroundColor: theme.palette.background.stone, + + borderRadius: '6px', + }, + tokenLogoContainer: { + width: '10px', + marginRight: theme.spacing(1), + display: 'flex', + alignItems: 'center', + }, + tokenLogo: { + height: '30px', + width: 10, + }, + tokenSymbol: { + opacity: 0.5, + fontWeight: 500, + }, + tokenBalanceContainer: { + position: 'absolute', + right: '0', + left: '0', + bottom: '-48px', + zIndex: -10, + display: 'flex', + justifyContent: 'space-between', + padding: '32px 16px 16px 16px', + + backgroundColor: theme.palette.background.stone, + borderRadius: '12px', + }, + tokenBalanceLabel: { + opacity: 0.5, + }, + }), +) + +type TokenInputType = { + id?: string + value: string + onChange: (value: string) => void + tokenPrice?: string + tokenSymbol: string + tokenLogo: string + tokenBalance: string +} + +const TokenInput: React.FC = ({ + id, + value, + onChange, + tokenPrice, + tokenSymbol, + tokenLogo, + tokenBalance, +}) => { + const classes = useInputStyles() + + const handleChange = (inputValue: string) => { + onChange(inputValue) + } + + return ( +
+
+
+ handleChange(event.target.value)} + onWheel={(e) => (e.target as any).blur()} + placeholder="0" + type="number" + min="0" + /> +
+ ${tokenPrice ? Number(value) * Number(tokenPrice) : 0} +
+
+
+
+ logo +
+ {tokenSymbol} +
+
+ +
+ + Available + + + {tokenBalance} {tokenSymbol} + +
+
+ ) +} + +const LPPage: React.FC = () => { + const classes = useStyles() + const [ethAmount, setEthAmount] = useState('0') + const [sqthAmount, setSqthAmount] = useState('0') + + return ( +
+
+ ) +} + +const ThemeWrapper: React.FC = () => ( + + + +) + +export default ThemeWrapper diff --git a/packages/frontend/public/images/eth-logo.svg b/packages/frontend/public/images/eth-logo.svg new file mode 100644 index 000000000..150889ff5 --- /dev/null +++ b/packages/frontend/public/images/eth-logo.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts index a65f9717e..cb79ebe0b 100644 --- a/packages/frontend/src/theme.ts +++ b/packages/frontend/src/theme.ts @@ -1,31 +1,17 @@ import { createTheme, ThemeOptions } from '@material-ui/core/styles' export enum Mode { - LIGHT = 'light', + LIGHT = 'LIGHT', DARK = 'DARK', + NEW_DARK = 'NEW_DARK', } const getTheme = (mode: Mode) => { - const palette = mode === 'light' ? lightPalette : darkPalete + const palette = mode === Mode.LIGHT ? lightPalette : mode === Mode.NEW_DARK ? newDarkPalette : darkPalette return createTheme({ ...palette, - typography: { - fontWeightBold: 500, - fontFamily: [ - 'Open Sans', - 'Mulish', - 'Inter', - 'Roboto Mono', - '-apple-system', - 'BlinkMacSystemFont', - '"Segoe UI"', - 'Roboto', - '"Helvetica Neue"', - 'Arial', - '"Segoe UI Emoji"', - ].join(','), - }, + breakpoints: { values: { xs: 0, @@ -76,7 +62,47 @@ const getTheme = (mode: Mode) => { }) } +const getTypography = (mode: Mode) => { + if (mode === Mode.NEW_DARK) { + return { + fontWeightBold: 500, + fontFamily: [ + 'DM Sans', + 'Open Sans', + 'Mulish', + 'Inter', + 'Roboto Mono', + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + '"Segoe UI Emoji"', + ].join(','), + } + } else { + return { + fontWeightBold: 500, + fontFamily: [ + 'Open Sans', + 'Mulish', + 'Inter', + 'Roboto Mono', + '-apple-system', + 'BlinkMacSystemFont', + '"Segoe UI"', + 'Roboto', + '"Helvetica Neue"', + 'Arial', + '"Segoe UI Emoji"', + ].join(','), + } + } +} + const lightPalette: ThemeOptions = { + typography: getTypography(Mode.LIGHT), palette: { type: 'light', primary: { @@ -111,7 +137,8 @@ const lightPalette: ThemeOptions = { }, } -const darkPalete: ThemeOptions = { +const darkPalette: ThemeOptions = { + typography: getTypography(Mode.DARK), palette: { type: 'dark', primary: { @@ -141,4 +168,35 @@ const darkPalete: ThemeOptions = { }, } +const newDarkPalette: ThemeOptions = { + typography: getTypography(Mode.NEW_DARK), + palette: { + type: 'dark', + primary: { + main: '#70E3F6', + contrastText: '#FFFFFF', + }, + secondary: { + main: '#00fff9', + }, + error: { + main: '#f5475c', + }, + warning: { + light: '#F5B07326', + main: '#F5B073', + }, + success: { + main: '#49D273', + light: '#B2F0C5', + }, + background: { + stone: '#242728', + lightStone: '#303436', + tooltip: 'rgba(255, 255, 255)', + default: '#181B1C', + }, + }, +} + export default getTheme From 439732916b0484d64d079f86c18aa6dcc382d47f Mon Sep 17 00:00:00 2001 From: nikhil arora Date: Wed, 2 Nov 2022 19:31:17 +0530 Subject: [PATCH 02/60] feat: styling done, still very messy --- packages/frontend/pages/new-lp.tsx | 695 +++++++++++++++++- packages/frontend/public/images/eth-logo.svg | 2 +- .../components/Lp/CollateralRatioSlider.tsx | 172 +++++ packages/frontend/src/theme.ts | 3 +- 4 files changed, 833 insertions(+), 39 deletions(-) create mode 100644 packages/frontend/src/components/Lp/CollateralRatioSlider.tsx diff --git a/packages/frontend/pages/new-lp.tsx b/packages/frontend/pages/new-lp.tsx index 0a51febbe..0529c8e71 100644 --- a/packages/frontend/pages/new-lp.tsx +++ b/packages/frontend/pages/new-lp.tsx @@ -1,10 +1,24 @@ import Nav from '@components/Nav' import { createStyles, makeStyles } from '@material-ui/core/styles' -import { Grid, Typography, Box } from '@material-ui/core' +import { + Grid, + Typography, + Box, + Modal, + Divider, + Checkbox, + FormControlLabel, + InputAdornment, + TextField, + BoxProps, +} from '@material-ui/core' +import { ToggleButtonGroup, ToggleButton } from '@material-ui/lab' import React, { useState } from 'react' import { ThemeProvider } from '@material-ui/core/styles' +import { PrimaryButton } from '@components/Button' import Image from 'next/image' +import CollateralRatioSlider from '@components/Lp/CollateralRatioSlider' import getTheme, { Mode } from '../src/theme' import ethLogo from '../public/images/eth-logo.svg' @@ -22,23 +36,29 @@ const useStyles = makeStyles((theme) => fontWeight: 700, marginBottom: theme.spacing(1), }, + depositBtn: { + fontWeight: 700, + textTransform: 'initial', + width: '100%', + marginTop: theme.spacing(4), + }, }), ) const useInputStyles = makeStyles((theme) => createStyles({ container: { - width: '340px', backgroundColor: 'inherit', textAlign: 'left', position: 'relative', - marginBottom: '48px', + marginBottom: '44px', + zIndex: 0, }, inputContainer: { display: 'flex', justifyContent: 'space-between', border: `2px solid ${theme.palette.background.lightStone}`, - borderRadius: '12px', + borderRadius: '10px', padding: theme.spacing(2), marginTop: '1em', backgroundColor: theme.palette.background.default, @@ -68,14 +88,16 @@ const useInputStyles = makeStyles((theme) => borderRadius: '6px', }, tokenLogoContainer: { - width: '10px', - marginRight: theme.spacing(1), + width: '16px', + height: '16px', + marginRight: theme.spacing(0.5), display: 'flex', alignItems: 'center', + justifyContent: 'center', }, tokenLogo: { - height: '30px', - width: 10, + height: '19px', + width: '11px', }, tokenSymbol: { opacity: 0.5, @@ -85,14 +107,14 @@ const useInputStyles = makeStyles((theme) => position: 'absolute', right: '0', left: '0', - bottom: '-48px', + bottom: '-44px', zIndex: -10, display: 'flex', justifyContent: 'space-between', - padding: '32px 16px 16px 16px', + padding: '36px 16px 12px 16px', backgroundColor: theme.palette.background.stone, - borderRadius: '12px', + borderRadius: '10px', }, tokenBalanceLabel: { opacity: 0.5, @@ -104,7 +126,7 @@ type TokenInputType = { id?: string value: string onChange: (value: string) => void - tokenPrice?: string + tokenPrice: string tokenSymbol: string tokenLogo: string tokenBalance: string @@ -140,13 +162,16 @@ const TokenInput: React.FC = ({ min="0" />
- ${tokenPrice ? Number(value) * Number(tokenPrice) : 0} + ${tokenPrice ? parseInt(value) * parseInt(tokenPrice) : 0}
- logo +
+ logo +
+ {tokenSymbol}
@@ -163,13 +188,609 @@ const TokenInput: React.FC = ({ ) } +const useTokenDepositStyles = makeStyles((theme) => + createStyles({ + container: { + backgroundColor: 'inherit', + textAlign: 'left', + position: 'relative', + marginBottom: '42px', + width: '50%', + zIndex: 0, + }, + + mainSection: { + display: 'flex', + alignItems: 'center', + border: `2px solid ${theme.palette.background.lightStone}`, + borderRadius: '10px', + padding: theme.spacing(2), + marginTop: '1em', + backgroundColor: theme.palette.background.default, + }, + + logoContainer: { + width: '40px', + height: '40px', + marginRight: theme.spacing(1), + backgroundColor: theme.palette.background.lightStone, + borderRadius: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + logo: { + height: '24px', + width: '14px', + }, + depositContainer: { + marginLeft: theme.spacing(1), + }, + amountContainer: { + display: 'flex', + }, + amount: { + fontWeight: 500, + }, + symbol: { + opacity: 0.5, + fontWeight: 400, + marginLeft: theme.spacing(0.5), + }, + usdValue: { + opacity: 0.5, + fontWeight: 400, + }, + subSection: { + position: 'absolute', + right: '0', + left: '0', + bottom: '-42px', + zIndex: -10, + display: 'flex', + justifyContent: 'space-between', + padding: '36px 16px 12px 16px', + backgroundColor: theme.palette.background.stone, + borderRadius: '10px', + }, + tokenBalanceLabel: { + opacity: 0.5, + }, + }), +) + +const useTokenLogoStyles = makeStyles((theme) => + createStyles({ + logoContainer: { + width: '40px', + height: '40px', + marginRight: theme.spacing(1), + backgroundColor: theme.palette.background.lightStone, + borderRadius: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + logo: { + height: '24px', + width: '14px', + }, + }), +) + +const TokenLogo: React.FC<{ logoSrc: string }> = ({ logoSrc }) => { + const classes = useTokenLogoStyles() + + return ( +
+
+ logo +
+
+ ) +} + +type TokenDepositType = { + amount: string + tokenPrice: string + tokenSymbol: string + tokenLogo: string + tokenBalance: string +} + +const TokenDeposit: React.FC = ({ amount, tokenPrice, tokenSymbol, tokenLogo, tokenBalance }) => { + const classes = useTokenDepositStyles() + + return ( +
+
+ +
+
+ {amount} + {tokenSymbol} +
+ + + ${tokenPrice ? parseInt(amount) * parseInt(tokenPrice) : 0} + +
+
+ +
+ + Available + + + {tokenBalance} {tokenSymbol} + +
+
+ ) +} + +type TokenPriceStyleProps = { + fontSize: string + color: string +} + +const useTokenPriceStyles = makeStyles((theme) => + createStyles({ + priceContainer: { + display: 'flex', + gap: theme.spacing(1), + }, + priceText: (props: TokenPriceStyleProps) => ({ + fontSize: props.fontSize, + color: props.color, + }), + }), +) + +const TokenPrice: React.FC<{ symbol: string; price: string; styleProps?: TokenPriceStyleProps }> = ({ + symbol, + price, + styleProps = { fontSize: '14px', color: 'rgba(255, 255, 255)' }, +}) => { + const classes = useTokenPriceStyles(styleProps) + + return ( +
+ {`1 ${symbol}`} + {'='} + {`$${price}`} +
+ ) +} + +const useModalStyles = makeStyles((theme) => + createStyles({ + container: { + margin: '5em auto 0px', + width: '80%', + maxWidth: '640px', + background: theme.palette.background.default, + padding: theme.spacing(6), + borderRadius: 20, + overflow: 'scroll', + height: '90%', + display: 'block', + }, + titleSection: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + modalTitle: { + fontSize: theme.typography.pxToRem(20), + fontWeight: 700, + }, + priceContainer: { + backgroundColor: theme.palette.background.lightStone, + padding: theme.spacing(0.75, 1.5), + borderRadius: '8px', + }, + + subSection: {}, + priceRangeSectionHeader: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + priceRangeSectionHeaderLeftColumn: { + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + }, + sectionTitle: { + fontSize: theme.typography.pxToRem(18), + fontWeight: 700, + }, + divider: { + height: '2px', + backgroundColor: theme.palette.background.lightStone, + margin: theme.spacing(4, 0), + display: 'inline-block', + width: '100%', + }, + priceRangeSection: { + marginTop: theme.spacing(3), + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + gap: theme.spacing(2), + }, + }), +) + +const useCheckboxStyles = makeStyles((theme) => + createStyles({ + root: { + padding: 0, + marginRight: theme.spacing(0.5), + }, + }), +) + +const useFormControlLabelStyles = makeStyles({ + root: { + marginRight: 0, + }, + label: { + fontWeight: 500, + }, +}) + +const useSimpleInputStyles = makeStyles((theme) => + createStyles({ + label: { + opacity: 0.5, + '& ~ $input': { + marginTop: '24px', + }, + }, + labelFocused: { + color: theme.palette.primary.main, + opacity: 0.8, + }, + input: { + padding: theme.spacing(0.75, 1.5), + border: '2px solid', + borderColor: theme.palette.background.lightStone, + borderRadius: '12px', + fontSize: '14px', + }, + inputFocused: { + borderColor: theme.palette.primary.main, + }, + }), +) + +const useToggleButtonStyles = makeStyles((theme) => ({ + root: { + textTransform: 'none', + padding: theme.spacing(0.25, 1.25), + color: theme.palette.primary.contrastText, + + '&.Mui-selected, &.Mui-selected:hover': { + color: theme.palette.background.default, + backgroundColor: theme.palette.primary.main, + }, + }, +})) + +const useTextStyles = makeStyles({ + light: { + color: 'rgba(255, 255, 255, 0.8)', + }, +}) + +const useButtonStyles = makeStyles({ + primary: { + textTransform: 'initial', + fontWeight: 700, + }, +}) + +const useStatsBoxStyles = makeStyles((theme) => + createStyles({ + container: { + backgroundColor: theme.palette.background.stone, + borderRadius: '12px', + padding: theme.spacing(2, 2.5), + width: '100%', + }, + }), +) + +const StatsBox: React.FC = (props) => { + const classes = useStatsBoxStyles() + return +} + +type PreviewModalType = { + isOpen: boolean + onClose: () => void +} + +const PreviewModal: React.FC = ({ isOpen, onClose }) => { + const [useDefaultPriceChange, setUseDefaultPriceRange] = useState(false) + const [useUniswapNftAsCollateral, setUseUniswapNftAsCollateral] = useState(true) + const [useDefaultCollateralRatio, setUseDefaultCollateralRatio] = useState(true) + const [collateralRatio, setCollateralRatio] = useState(225) + + const classes = useModalStyles() + const checkboxClasses = useCheckboxStyles() + const formControlLabelClasses = useFormControlLabelStyles() + const inputClasses = useSimpleInputStyles() + const toggleButtonClasses = useToggleButtonStyles() + const textClasses = useTextStyles() + const buttonClasses = useButtonStyles() + + return ( + + +
+ + Mint and LP Preview + + +
+ +
+
+ +
+ + Deposit amounts + + + +
+ + + +
+
+
+ + +
+ + Price range + + +
+
+ + setUseDefaultPriceRange(event.target.checked)} + name="priceRangeDefault" + color="primary" + /> + } + label="Default" + /> +
+ +
+ {}} + InputLabelProps={{ + classes: { + root: inputClasses.label, + focused: inputClasses.labelFocused, + }, + }} + InputProps={{ + endAdornment: ( + + Per ETH + + ), + disableUnderline: true, + classes: { + root: inputClasses.input, + focused: inputClasses.inputFocused, + }, + }} + /> + + + + + + {}} + InputLabelProps={{ + classes: { + root: inputClasses.label, + focused: inputClasses.labelFocused, + }, + }} + InputProps={{ + endAdornment: ( + + Per ETH + + ), + disableUnderline: true, + classes: { + root: inputClasses.input, + focused: inputClasses.inputFocused, + }, + }} + /> +
+
+ + + +
+ + Use Uniswap LP NFT as collateral + + setUseUniswapNftAsCollateral(value)} + exclusive + > + + Yes + + + No + + + +
+ + + +
+ + + Collateralization ratio + + + + setUseDefaultCollateralRatio(event.target.checked)} + name="priceRangeDefault" + color="primary" + /> + } + label="Default" + /> + + setCollateralRatio(parseInt(event.target.value))} + InputProps={{ + endAdornment: ( + + % + + ), + disableUnderline: true, + classes: { + root: inputClasses.input, + focused: inputClasses.inputFocused, + }, + }} + style={{ width: '80px' }} + /> + + + +
+ setCollateralRatio(val)} + /> +
+
+ + + + Liquidation price + + $3,018.29 + per ETH + + + + + + + Projected APY + 26.08 % + + + + + +
+ + + Total Deposit + = 126.5 ETH (including 186 oSQTH) + + + + + + + {'To be LP’ed'} + + + 94.2 + ETH + + + + + + {'Vault'} + + + 32.3 + ETH + + + + +
+ + + {}} + > + {'Confirm deposit'} + + +
+
+ ) +} + const LPPage: React.FC = () => { const classes = useStyles() const [ethAmount, setEthAmount] = useState('0') - const [sqthAmount, setSqthAmount] = useState('0') + const [isPreviewModalOpen, setPreviewModalOpen] = useState(false) return ( -
+ <>
+ + setPreviewModalOpen(false)} /> + ) } diff --git a/packages/frontend/public/images/eth-logo.svg b/packages/frontend/public/images/eth-logo.svg index 150889ff5..e50ef1fbc 100644 --- a/packages/frontend/public/images/eth-logo.svg +++ b/packages/frontend/public/images/eth-logo.svg @@ -1,7 +1,7 @@ - diff --git a/packages/frontend/src/components/Lp/CollateralRatioSlider.tsx b/packages/frontend/src/components/Lp/CollateralRatioSlider.tsx new file mode 100644 index 000000000..72e9450bd --- /dev/null +++ b/packages/frontend/src/components/Lp/CollateralRatioSlider.tsx @@ -0,0 +1,172 @@ +import { createStyles, makeStyles, Tooltip } from '@material-ui/core' +import React from 'react' + +import Slider from '@components/CustomSlider' + +const COLORS = { + DANGER: { + LIGHT: '#FA7B67', + DARK: '#452C28', + }, + RISKY: { + LIGHT: '#F3FF6C', + DARK: '#61662b', + }, + SAFE: { + LIGHT: '#67FABF', + DARK: '#284539', + }, +} + +const MIN_COLLATERAL_RATIO = 150 +const MAX_COLLATERAL_RATIO = 300 + +const useStyles = makeStyles((theme) => + createStyles({ + container: { + width: '100%%', + marginLeft: 'auto', + marginRight: 'auto', + }, + danger: { + backgroundColor: COLORS.DANGER.LIGHT, + }, + warning: { + backgroundColor: COLORS.RISKY.LIGHT, + }, + safe: { + backgroundColor: COLORS.SAFE.LIGHT, + }, + rail: { + background: `linear-gradient(to right, ${COLORS.DANGER.LIGHT}, ${COLORS.DANGER.LIGHT} 33.4%, ${COLORS.RISKY.LIGHT} 33.4%, ${COLORS.RISKY.LIGHT} 50%, ${COLORS.SAFE.LIGHT} 50%, ${COLORS.SAFE.LIGHT})`, + opacity: 0.9, + }, + track: { + background: 'transparent', + }, + mark: { + background: theme.palette.background.lightStone, + }, + markLabel: { + marginTop: '6px', + '&[data-index="0"]': { + transform: 'none', + }, + '&[data-index="3"]': { + transform: 'translateX(-100%)', + }, + }, + thumb: { + visibility: 'hidden', + '&:focus, &:hover, &.Mui-active': { + visibility: 'hidden', + }, + }, + }), +) + +type CollateralRatioSliderType = { + id?: string + collateralRatio: number + onCollateralRatioChange: (val: number) => void + className?: string +} + +const MARKS = [ + { + value: 150, + label: 'Min 150%', + }, + { + value: 200, + label: '200%', + }, + { + value: 225, + label: '225%', + }, + { + value: 300, + label: 'Max 300%', + }, +] + +const useValueLabelStyles = makeStyles(() => + createStyles({ + tooltip: (value: any) => ({ + backgroundColor: value < 200 ? COLORS.DANGER.DARK : value < 225 ? COLORS.RISKY.DARK : COLORS.SAFE.DARK, + color: value < 200 ? COLORS.DANGER.LIGHT : value < 225 ? COLORS.RISKY.LIGHT : COLORS.SAFE.LIGHT, + padding: '4px 8px', + fontSize: '12px', + fontWeight: 500, + }), + }), +) + +function ValueLabelComponent(props: any) { + const { children, open, value } = props + + const classes = useValueLabelStyles(value) + + const title = value < 200 ? 'Danger' : value < 225 ? 'Risky' : 'Safe' + + return ( + + {children} + + ) +} + +const CollateralRatioSlider: React.FC = ({ + id, + collateralRatio, + onCollateralRatioChange, + className, +}) => { + const classes = useStyles() + + const changeSlider = (val: number) => { + if (val < MIN_COLLATERAL_RATIO) return + + onCollateralRatioChange(val) + } + + return ( +
+ changeSlider(val as number)} + step={1} + classes={{ + rail: classes.rail, + track: classes.track, + mark: classes.mark, + markLabel: classes.markLabel, + thumb: classes.thumb, + }} + className={className} + marks={MARKS} + min={MIN_COLLATERAL_RATIO} + max={MAX_COLLATERAL_RATIO} + id={id + '-slider'} + /> +
+ ) +} + +export default CollateralRatioSlider diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts index cb79ebe0b..aee06bfda 100644 --- a/packages/frontend/src/theme.ts +++ b/packages/frontend/src/theme.ts @@ -175,6 +175,7 @@ const newDarkPalette: ThemeOptions = { primary: { main: '#70E3F6', contrastText: '#FFFFFF', + dark: '#0ebcd8', // todo: not sure what this should be since its not specified in figma }, secondary: { main: '#00fff9', @@ -194,7 +195,7 @@ const newDarkPalette: ThemeOptions = { stone: '#242728', lightStone: '#303436', tooltip: 'rgba(255, 255, 255)', - default: '#181B1C', + default: '#191B1C', }, }, } From 8b4cab21495f041899d1b4b80565de7b6ebe3d54 Mon Sep 17 00:00:00 2001 From: nikhil arora Date: Thu, 3 Nov 2022 18:15:32 +0530 Subject: [PATCH 03/60] refactor: separated into individual components --- packages/frontend/pages/new-lp.tsx | 797 +----------------- .../public/images/header-background.svg | 220 +++++ .../src/components/Lp/MintAndLp/Checkbox.tsx | 51 ++ .../{ => MintAndLp}/CollateralRatioSlider.tsx | 0 .../Lp/MintAndLp/DepositPreviewModal.tsx | 331 ++++++++ .../src/components/Lp/MintAndLp/InfoBox.tsx | 21 + .../src/components/Lp/MintAndLp/Input.tsx | 73 ++ .../components/Lp/MintAndLp/PageHeader.tsx | 56 ++ .../components/Lp/MintAndLp/TokenDeposit.tsx | 117 +++ .../components/Lp/MintAndLp/TokenInput.tsx | 161 ++++ .../src/components/Lp/MintAndLp/TokenLogo.tsx | 36 + .../components/Lp/MintAndLp/TokenPrice.tsx | 39 + .../src/components/Lp/MintAndLp/index.tsx | 5 + 13 files changed, 1135 insertions(+), 772 deletions(-) create mode 100644 packages/frontend/public/images/header-background.svg create mode 100644 packages/frontend/src/components/Lp/MintAndLp/Checkbox.tsx rename packages/frontend/src/components/Lp/{ => MintAndLp}/CollateralRatioSlider.tsx (100%) create mode 100644 packages/frontend/src/components/Lp/MintAndLp/DepositPreviewModal.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/InfoBox.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/Input.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/PageHeader.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/TokenDeposit.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/TokenInput.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/TokenLogo.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/TokenPrice.tsx create mode 100644 packages/frontend/src/components/Lp/MintAndLp/index.tsx diff --git a/packages/frontend/pages/new-lp.tsx b/packages/frontend/pages/new-lp.tsx index 0529c8e71..cba621081 100644 --- a/packages/frontend/pages/new-lp.tsx +++ b/packages/frontend/pages/new-lp.tsx @@ -1,42 +1,34 @@ import Nav from '@components/Nav' -import { createStyles, makeStyles } from '@material-ui/core/styles' -import { - Grid, - Typography, - Box, - Modal, - Divider, - Checkbox, - FormControlLabel, - InputAdornment, - TextField, - BoxProps, -} from '@material-ui/core' -import { ToggleButtonGroup, ToggleButton } from '@material-ui/lab' +import { createStyles, makeStyles, ThemeProvider } from '@material-ui/core/styles' +import { Grid, Typography, Box } from '@material-ui/core' import React, { useState } from 'react' -import { ThemeProvider } from '@material-ui/core/styles' -import { PrimaryButton } from '@components/Button' -import Image from 'next/image' -import CollateralRatioSlider from '@components/Lp/CollateralRatioSlider' +import { PrimaryButton } from '@components/Button' +import { DepositPreviewModal, TokenInput, PageHeader } from '@components/Lp/MintAndLp' import getTheme, { Mode } from '../src/theme' import ethLogo from '../public/images/eth-logo.svg' const useStyles = makeStyles((theme) => createStyles({ container: { - padding: theme.spacing(5, 10), + padding: theme.spacing(9, 10), maxWidth: '1500px', width: '95%', marginLeft: 'auto', marginRight: 'auto', }, - sectionHeader: { - fontSize: theme.typography.pxToRem(18), + title: { + fontSize: '24px', fontWeight: 700, - marginBottom: theme.spacing(1), + letterSpacing: '-0.02em', + }, + subtitle: { + fontSize: '18px', + fontWeight: 400, + color: theme.palette.grey[400], }, depositBtn: { + fontSize: '16px', fontWeight: 700, textTransform: 'initial', width: '100%', @@ -45,763 +37,29 @@ const useStyles = makeStyles((theme) => }), ) -const useInputStyles = makeStyles((theme) => - createStyles({ - container: { - backgroundColor: 'inherit', - textAlign: 'left', - position: 'relative', - marginBottom: '44px', - zIndex: 0, - }, - inputContainer: { - display: 'flex', - justifyContent: 'space-between', - border: `2px solid ${theme.palette.background.lightStone}`, - borderRadius: '10px', - padding: theme.spacing(2), - marginTop: '1em', - backgroundColor: theme.palette.background.default, - }, - leftInputContainer: {}, - input: { - display: 'inline-block', - border: 'none', - backgroundColor: 'inherit', - outline: 'none', - fontSize: '22px', - width: '204px', - - color: theme.palette.text.primary, - fontWeight: theme.typography.fontWeightBold, - fontFamily: theme.typography.fontFamily, - }, - unitsContainer: {}, - rightInputContainer: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - padding: '0px 10px', - height: '28px', - backgroundColor: theme.palette.background.stone, - - borderRadius: '6px', - }, - tokenLogoContainer: { - width: '16px', - height: '16px', - marginRight: theme.spacing(0.5), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - tokenLogo: { - height: '19px', - width: '11px', - }, - tokenSymbol: { - opacity: 0.5, - fontWeight: 500, - }, - tokenBalanceContainer: { - position: 'absolute', - right: '0', - left: '0', - bottom: '-44px', - zIndex: -10, - display: 'flex', - justifyContent: 'space-between', - padding: '36px 16px 12px 16px', - - backgroundColor: theme.palette.background.stone, - borderRadius: '10px', - }, - tokenBalanceLabel: { - opacity: 0.5, - }, - }), -) - -type TokenInputType = { - id?: string - value: string - onChange: (value: string) => void - tokenPrice: string - tokenSymbol: string - tokenLogo: string - tokenBalance: string -} - -const TokenInput: React.FC = ({ - id, - value, - onChange, - tokenPrice, - tokenSymbol, - tokenLogo, - tokenBalance, -}) => { - const classes = useInputStyles() - - const handleChange = (inputValue: string) => { - onChange(inputValue) - } - - return ( -
-
-
- handleChange(event.target.value)} - onWheel={(e) => (e.target as any).blur()} - placeholder="0" - type="number" - min="0" - /> -
- ${tokenPrice ? parseInt(value) * parseInt(tokenPrice) : 0} -
-
-
-
-
- logo -
-
- - {tokenSymbol} -
-
- -
- - Available - - - {tokenBalance} {tokenSymbol} - -
-
- ) -} - -const useTokenDepositStyles = makeStyles((theme) => - createStyles({ - container: { - backgroundColor: 'inherit', - textAlign: 'left', - position: 'relative', - marginBottom: '42px', - width: '50%', - zIndex: 0, - }, - - mainSection: { - display: 'flex', - alignItems: 'center', - border: `2px solid ${theme.palette.background.lightStone}`, - borderRadius: '10px', - padding: theme.spacing(2), - marginTop: '1em', - backgroundColor: theme.palette.background.default, - }, - - logoContainer: { - width: '40px', - height: '40px', - marginRight: theme.spacing(1), - backgroundColor: theme.palette.background.lightStone, - borderRadius: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - logo: { - height: '24px', - width: '14px', - }, - depositContainer: { - marginLeft: theme.spacing(1), - }, - amountContainer: { - display: 'flex', - }, - amount: { - fontWeight: 500, - }, - symbol: { - opacity: 0.5, - fontWeight: 400, - marginLeft: theme.spacing(0.5), - }, - usdValue: { - opacity: 0.5, - fontWeight: 400, - }, - subSection: { - position: 'absolute', - right: '0', - left: '0', - bottom: '-42px', - zIndex: -10, - display: 'flex', - justifyContent: 'space-between', - padding: '36px 16px 12px 16px', - backgroundColor: theme.palette.background.stone, - borderRadius: '10px', - }, - tokenBalanceLabel: { - opacity: 0.5, - }, - }), -) - -const useTokenLogoStyles = makeStyles((theme) => - createStyles({ - logoContainer: { - width: '40px', - height: '40px', - marginRight: theme.spacing(1), - backgroundColor: theme.palette.background.lightStone, - borderRadius: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - logo: { - height: '24px', - width: '14px', - }, - }), -) - -const TokenLogo: React.FC<{ logoSrc: string }> = ({ logoSrc }) => { - const classes = useTokenLogoStyles() - - return ( -
-
- logo -
-
- ) -} - -type TokenDepositType = { - amount: string - tokenPrice: string - tokenSymbol: string - tokenLogo: string - tokenBalance: string -} - -const TokenDeposit: React.FC = ({ amount, tokenPrice, tokenSymbol, tokenLogo, tokenBalance }) => { - const classes = useTokenDepositStyles() - - return ( -
-
- -
-
- {amount} - {tokenSymbol} -
- - - ${tokenPrice ? parseInt(amount) * parseInt(tokenPrice) : 0} - -
-
- -
- - Available - - - {tokenBalance} {tokenSymbol} - -
-
- ) -} - -type TokenPriceStyleProps = { - fontSize: string - color: string -} - -const useTokenPriceStyles = makeStyles((theme) => - createStyles({ - priceContainer: { - display: 'flex', - gap: theme.spacing(1), - }, - priceText: (props: TokenPriceStyleProps) => ({ - fontSize: props.fontSize, - color: props.color, - }), - }), -) - -const TokenPrice: React.FC<{ symbol: string; price: string; styleProps?: TokenPriceStyleProps }> = ({ - symbol, - price, - styleProps = { fontSize: '14px', color: 'rgba(255, 255, 255)' }, -}) => { - const classes = useTokenPriceStyles(styleProps) - - return ( -
- {`1 ${symbol}`} - {'='} - {`$${price}`} -
- ) -} - -const useModalStyles = makeStyles((theme) => - createStyles({ - container: { - margin: '5em auto 0px', - width: '80%', - maxWidth: '640px', - background: theme.palette.background.default, - padding: theme.spacing(6), - borderRadius: 20, - overflow: 'scroll', - height: '90%', - display: 'block', - }, - titleSection: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - }, - modalTitle: { - fontSize: theme.typography.pxToRem(20), - fontWeight: 700, - }, - priceContainer: { - backgroundColor: theme.palette.background.lightStone, - padding: theme.spacing(0.75, 1.5), - borderRadius: '8px', - }, - - subSection: {}, - priceRangeSectionHeader: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - }, - priceRangeSectionHeaderLeftColumn: { - display: 'flex', - alignItems: 'center', - gap: theme.spacing(1), - }, - sectionTitle: { - fontSize: theme.typography.pxToRem(18), - fontWeight: 700, - }, - divider: { - height: '2px', - backgroundColor: theme.palette.background.lightStone, - margin: theme.spacing(4, 0), - display: 'inline-block', - width: '100%', - }, - priceRangeSection: { - marginTop: theme.spacing(3), - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - gap: theme.spacing(2), - }, - }), -) - -const useCheckboxStyles = makeStyles((theme) => - createStyles({ - root: { - padding: 0, - marginRight: theme.spacing(0.5), - }, - }), -) - -const useFormControlLabelStyles = makeStyles({ - root: { - marginRight: 0, - }, - label: { - fontWeight: 500, - }, -}) - -const useSimpleInputStyles = makeStyles((theme) => - createStyles({ - label: { - opacity: 0.5, - '& ~ $input': { - marginTop: '24px', - }, - }, - labelFocused: { - color: theme.palette.primary.main, - opacity: 0.8, - }, - input: { - padding: theme.spacing(0.75, 1.5), - border: '2px solid', - borderColor: theme.palette.background.lightStone, - borderRadius: '12px', - fontSize: '14px', - }, - inputFocused: { - borderColor: theme.palette.primary.main, - }, - }), -) - -const useToggleButtonStyles = makeStyles((theme) => ({ - root: { - textTransform: 'none', - padding: theme.spacing(0.25, 1.25), - color: theme.palette.primary.contrastText, - - '&.Mui-selected, &.Mui-selected:hover': { - color: theme.palette.background.default, - backgroundColor: theme.palette.primary.main, - }, - }, -})) - -const useTextStyles = makeStyles({ - light: { - color: 'rgba(255, 255, 255, 0.8)', - }, -}) - -const useButtonStyles = makeStyles({ - primary: { - textTransform: 'initial', - fontWeight: 700, - }, -}) - -const useStatsBoxStyles = makeStyles((theme) => - createStyles({ - container: { - backgroundColor: theme.palette.background.stone, - borderRadius: '12px', - padding: theme.spacing(2, 2.5), - width: '100%', - }, - }), -) - -const StatsBox: React.FC = (props) => { - const classes = useStatsBoxStyles() - return -} - -type PreviewModalType = { - isOpen: boolean - onClose: () => void -} - -const PreviewModal: React.FC = ({ isOpen, onClose }) => { - const [useDefaultPriceChange, setUseDefaultPriceRange] = useState(false) - const [useUniswapNftAsCollateral, setUseUniswapNftAsCollateral] = useState(true) - const [useDefaultCollateralRatio, setUseDefaultCollateralRatio] = useState(true) - const [collateralRatio, setCollateralRatio] = useState(225) - - const classes = useModalStyles() - const checkboxClasses = useCheckboxStyles() - const formControlLabelClasses = useFormControlLabelStyles() - const inputClasses = useSimpleInputStyles() - const toggleButtonClasses = useToggleButtonStyles() - const textClasses = useTextStyles() - const buttonClasses = useButtonStyles() - - return ( - - -
- - Mint and LP Preview - - -
- -
-
- -
- - Deposit amounts - - - -
- - - -
-
-
- - -
- - Price range - - -
-
- - setUseDefaultPriceRange(event.target.checked)} - name="priceRangeDefault" - color="primary" - /> - } - label="Default" - /> -
- -
- {}} - InputLabelProps={{ - classes: { - root: inputClasses.label, - focused: inputClasses.labelFocused, - }, - }} - InputProps={{ - endAdornment: ( - - Per ETH - - ), - disableUnderline: true, - classes: { - root: inputClasses.input, - focused: inputClasses.inputFocused, - }, - }} - /> - - - - - - {}} - InputLabelProps={{ - classes: { - root: inputClasses.label, - focused: inputClasses.labelFocused, - }, - }} - InputProps={{ - endAdornment: ( - - Per ETH - - ), - disableUnderline: true, - classes: { - root: inputClasses.input, - focused: inputClasses.inputFocused, - }, - }} - /> -
-
- - - -
- - Use Uniswap LP NFT as collateral - - setUseUniswapNftAsCollateral(value)} - exclusive - > - - Yes - - - No - - - -
- - - -
- - - Collateralization ratio - - - - setUseDefaultCollateralRatio(event.target.checked)} - name="priceRangeDefault" - color="primary" - /> - } - label="Default" - /> - - setCollateralRatio(parseInt(event.target.value))} - InputProps={{ - endAdornment: ( - - % - - ), - disableUnderline: true, - classes: { - root: inputClasses.input, - focused: inputClasses.inputFocused, - }, - }} - style={{ width: '80px' }} - /> - - - -
- setCollateralRatio(val)} - /> -
-
- - - - Liquidation price - - $3,018.29 - per ETH - - - - - - - Projected APY - 26.08 % - - - - - -
- - - Total Deposit - = 126.5 ETH (including 186 oSQTH) - - - - - - - {'To be LP’ed'} - - - 94.2 - ETH - - - - - - {'Vault'} - - - 32.3 - ETH - - - - -
- - - {}} - > - {'Confirm deposit'} - - -
-
- ) -} - const LPPage: React.FC = () => { const classes = useStyles() - const [ethAmount, setEthAmount] = useState('0') + const [ethAmount, setEthAmount] = useState(0) const [isPreviewModalOpen, setPreviewModalOpen] = useState(false) return ( <>