diff --git a/package.json b/package.json index 3447031..ee4f773 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "homepage": "https://github.com/project-serum/swap-ui", "license": "Apache-2.0", "dependencies": { + "@fontsource/roboto": "^4.3.0", "@project-serum/serum": "^0.13.34", "@project-serum/swap": "^0.1.0-alpha.14", "@solana/spl-token": "^0.1.4" diff --git a/src/components/Info.tsx b/src/components/Info.tsx index 01d4843..f776c91 100644 --- a/src/components/Info.tsx +++ b/src/components/Info.tsx @@ -13,24 +13,18 @@ import { useSwapContext, useSwapFair } from "../context/Swap"; import { useMint } from "../context/Token"; import { useRoute, useMarketName, useBbo } from "../context/Dex"; -const useStyles = makeStyles((theme) => ({ +const useStyles = makeStyles(() => ({ infoLabel: { - marginTop: "10px", - marginBottom: "10px", + marginTop: "20px", + marginBottom: "20px", display: "flex", - justifyContent: "space-between", - marginLeft: "5px", - marginRight: "5px", - }, - fairPriceLabel: { - marginRight: "10px", - display: "flex", - justifyContent: "center", - flexDirection: "column", - color: theme.palette.text.secondary, + justifyContent: "flex-end", + alignItems: "center", }, infoButton: { + marginLeft: "5px", padding: 0, + fontSize: "14px", }, })); @@ -47,17 +41,14 @@ export function InfoLabel() { return (
- -
-
- {fair !== undefined && toTokenInfo && fromTokenInfo - ? `1 ${toTokenInfo.symbol} = ${fair.toFixed( - fromMintInfo?.decimals - )} ${fromTokenInfo.symbol}` - : `-`} -
- -
+ + {fair !== undefined && toTokenInfo && fromTokenInfo + ? `1 ${toTokenInfo.symbol} = ${fair.toFixed( + fromMintInfo?.decimals + )} ${fromTokenInfo.symbol}` + : `-`} + +
); } @@ -74,7 +65,7 @@ function InfoButton() { {...bindTrigger(popupState)} className={styles.infoButton} > - + ({ table: {}, - closeAccountSwitchLabel: { - color: theme.palette.text.secondary, + closeAccount: { + color: theme.palette.error.main, }, })); @@ -40,6 +40,8 @@ export default function OpenOrdersDialog({ open: boolean; onClose: () => void; }) { + const styles = useStyles(); + return ( -
-
+ - - - -
- - - + +
+ + +
); } @@ -126,6 +122,8 @@ function OpenOrdersRow({ market: PublicKey; openOrders: Array; }) { + const styles = useStyles(); + const [ooAccount, setOoAccount] = useState(openOrders[0]); const { swapClient } = useDexContext(); const marketClient = useMarket(market); @@ -235,9 +233,9 @@ function OpenOrdersRow({ diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index eb6cdf9..0a51b2e 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -11,7 +11,6 @@ import { FormControlLabel, FormGroup, } from "@material-ui/core"; -import { ToggleButton } from "@material-ui/lab"; import { SettingsOutlined as Settings } from "@material-ui/icons"; import PopupState, { bindTrigger, bindPopover } from "material-ui-popup-state"; import { useSwapContext, useSwapFair } from "../context/Swap"; @@ -25,10 +24,27 @@ const useStyles = makeStyles((theme) => ({ table: {}, settingsButton: { padding: 0, + color: theme.palette.primary.main, }, closeAccountSwitchLabel: { color: theme.palette.text.secondary, }, + fairAutoSelected: { + backgroundColor: theme.palette.primary.main, + padding: "3px 5px", + borderRadius: "10px", + color: theme.palette.primary.contrastText, + fontWeight: 700, + }, + fairAuto: { + backgroundColor: + theme.palette.type === "dark" + ? theme.palette.secondary.light + : theme.palette.secondary.main, + padding: "3px 5px", + borderRadius: "10px", + boxShadow: "none", + }, })); export function SettingsButton() { @@ -56,7 +72,12 @@ export function SettingsButton() { vertical: "top", horizontal: "right", }} - PaperProps={{ style: { borderRadius: "10px" } }} + PaperProps={{ + style: { + borderRadius: "10px", + boxShadow: "0px 0px 30px 5px rgba(0,0,0,0.075)", + }, + }} >
@@ -68,6 +89,8 @@ export function SettingsButton() { } function SettingsDetails() { + const styles = useStyles(); + const { slippage, setSlippage, fairOverride, setFairOverride } = useSwapContext(); const [showSettingsDialog, setShowSettingsDialog] = useState(false); @@ -80,12 +103,12 @@ function SettingsDetails() { return (
- - Settings - -
-
- Slippage tolerance + Settings +
+
+ + Slippage tolerance +
-
- Fair price +
+ + Fair price +
- { if (fair === undefined) { console.error("Fair is undefined"); @@ -132,27 +157,22 @@ function SettingsDetails() { setFairOverride(null); } }} - style={{ - paddingTop: "3px", - paddingBottom: "3px", - paddingLeft: "5px", - paddingRight: "5px", - borderRadius: "20px", - }} + className={ + fairOverride === null + ? styles.fairAutoSelected + : styles.fairAuto + } > Auto - +
-
+
@@ -97,18 +140,24 @@ function SwapHeader() { export function ArrowButton() { const styles = useStyles(); + const theme = useTheme(); const { swapToFromMints } = useSwapContext(); return ( - + ); } -function SwapFromForm() { +function SwapFromForm({ style }: { style?: any }) { const { fromMint, setFromMint, fromAmount, setFromAmount } = useSwapContext(); return ( void; amount: number; setAmount: (a: number) => void; }) { + const styles = useStyles(); + const [showTokenDialog, setShowTokenDialog] = useState(false); const tokenAccount = useOwnedTokenAccount(mint); const mintAccount = useMint(mint); + const balance = + tokenAccount && + mintAccount && + tokenAccount.account.amount.toNumber() / 10 ** mintAccount.decimals; + + const formattedAmount = + mintAccount && amount + ? amount.toLocaleString("fullwide", { + maximumFractionDigits: mintAccount.decimals, + useGrouping: false, + }) + : amount; + return ( - -
+
+
setShowTokenDialog(true)} /> - setAmount(parseFloat(e.target.value))} - style={{ - display: "flex", - justifyContent: "center", - flexDirection: "column", - }} - /> -
-
- + {tokenAccount && mintAccount - ? `Balance: ${( - tokenAccount.account.amount.toNumber() / - 10 ** mintAccount.decimals - ).toFixed(mintAccount.decimals)}` + ? `Balance: ${balance?.toFixed(mintAccount.decimals)}` : `-`} + {from && !!balance ? ( + setAmount(balance)} + > + MAX + + ) : null}
+ setAmount(parseFloat(e.target.value))} + InputProps={{ + disableUnderline: true, + classes: { + root: styles.amountInput, + input: styles.input, + }, + }} + /> setShowTokenDialog(false)} /> - +
); } @@ -191,12 +260,14 @@ function TokenButton({ mint: PublicKey; onClick: () => void; }) { + const styles = useStyles(); + return ( - +
); } @@ -220,11 +291,13 @@ export function TokenIcon({ mint, style }: { mint: PublicKey; style: any }) { ); } -function TokenName({ mint }: { mint: PublicKey }) { +function TokenName({ mint, style }: { mint: PublicKey; style: any }) { const tokenMap = useTokenMap(); let tokenInfo = tokenMap.get(mint.toString()); return ( - {tokenInfo?.symbol} + + {tokenInfo?.symbol} + ); } diff --git a/src/components/TokenDialog.tsx b/src/components/TokenDialog.tsx index 8cbe867..8e1bc18 100644 --- a/src/components/TokenDialog.tsx +++ b/src/components/TokenDialog.tsx @@ -17,20 +17,25 @@ import { import { TokenIcon } from "./Swap"; import { useSwappableTokens } from "../context/TokenList"; -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles((theme) => ({ dialogContent: { - paddingTop: 0, - paddingBottom: 0, + padding: 0, }, textField: { - width: "100%", - border: "solid 1pt #ccc", - borderRadius: "10px", marginBottom: "8px", }, tab: { minWidth: "134px", }, + tabSelected: { + color: theme.palette.primary.contrastText, + fontWeight: 700, + backgroundColor: theme.palette.primary.main, + borderRadius: "10px", + }, + tabIndicator: { + opacity: 0, + }, })); export default function TokenDialog({ @@ -83,11 +88,9 @@ export default function TokenDialog({ className={styles.textField} placeholder={"Search name"} value={tokenFilter} + fullWidth + variant="outlined" onChange={(e) => setTokenFilter(e.target.value)} - InputProps={{ - disableUnderline: true, - style: { padding: "10px" }, - }} /> @@ -105,10 +108,31 @@ export default function TokenDialog({ - setTabSelection(v)}> - - - + setTabSelection(v)} + classes={{ + indicator: styles.tabIndicator, + }} + > + + + @@ -124,7 +148,11 @@ function TokenListItem({ }) { const mint = new PublicKey(tokenInfo.address); return ( - onClick(mint)}> + onClick(mint)} + style={{ padding: "10px 20px" }} + > diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..dd3d92d --- /dev/null +++ b/src/index.css @@ -0,0 +1 @@ +@import url("https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;700&display=swap"); diff --git a/src/index.tsx b/src/index.tsx index ab33429..9e8ee73 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,3 +1,5 @@ +import "@fontsource/roboto"; +import "./index.css"; import { ReactElement } from "react"; import { PublicKey } from "@solana/web3.js"; import { TokenListContainer } from "@solana/spl-token-registry"; @@ -8,6 +10,11 @@ import { DexContextProvider } from "./context/Dex"; import { TokenListContextProvider } from "./context/TokenList"; import { TokenContextProvider } from "./context/Token"; import SwapCard from "./components/Swap"; +import { + createMuiTheme, + ThemeOptions, + ThemeProvider, +} from "@material-ui/core/styles"; /** * A`Swap` component that can be embedded into applications. To use, @@ -26,7 +33,10 @@ import SwapCard from "./components/Swap"; */ export function Swap(props: SwapProps): ReactElement { const { - style, + containerStyle, + contentStyle, + swapTokenContainerStyle, + materialTheme, provider, tokenList, fromMint, @@ -36,22 +46,45 @@ export function Swap(props: SwapProps): ReactElement { referral, } = props; const swapClient = new SwapClient(provider, tokenList); + const theme = createMuiTheme( + materialTheme || { + palette: { + primary: { + main: "#2196F3", + contrastText: "#FFFFFF", + }, + secondary: { + main: "#E0E0E0", + light: "#595959", + }, + error: { + main: "#ff6b6b", + }, + }, + } + ); return ( - - - - - - - - - + + + + + + + + + + + ); } @@ -101,9 +134,24 @@ export type SwapProps = { toAmount?: number; /** - * Style properties to pass through to the component. + * Provide custom material-ui theme. */ - style?: any; + materialTheme?: ThemeOptions; + + /** + * Styling properties for the main container. + */ + containerStyle?: any; + + /** + * Styling properties for the content container. + */ + contentStyle?: any; + + /** + * Styling properties for the from and to token containers. + */ + swapTokenContainerStyle?: any; }; export default Swap; diff --git a/yarn.lock b/yarn.lock index 01d5950..11c1928 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1255,6 +1255,11 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@fontsource/roboto@^4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-4.3.0.tgz#00f1cceca43eff85bb0e1d424311751ee41f6aa6" + integrity sha512-WeFWCWYutLWyEtRmBhn+bLbW4/km0l+HhTpR8wWDxJLiGiMOhVLO/Z0q5w6l20ZOkWnf6Z1rN3o3W2HjvYN6Rg== + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"