Commit 47aff6ff authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

feat: max slippage ui (#3190)

* style: input padding

* feat: expand Row grow

* style: polish max slippage
parent 56717005
...@@ -163,7 +163,7 @@ export const inputCss = css` ...@@ -163,7 +163,7 @@ export const inputCss = css`
border: 1px solid ${({ theme }) => theme.container}; border: 1px solid ${({ theme }) => theme.container};
border-radius: ${({ theme }) => theme.borderRadius}em; border-radius: ${({ theme }) => theme.borderRadius}em;
cursor: text; cursor: text;
padding: calc(0.75em - 1px); padding: calc(0.5em - 1px);
:hover:not(:focus-within) { :hover:not(:focus-within) {
background-color: ${({ theme }) => theme.onHover(theme.container)}; background-color: ${({ theme }) => theme.onHover(theme.container)};
......
...@@ -8,7 +8,7 @@ const Row = styled.div<{ ...@@ -8,7 +8,7 @@ const Row = styled.div<{
pad?: number pad?: number
gap?: number gap?: number
flex?: true flex?: true
grow?: true grow?: true | 'first' | 'last'
children?: ReactNode children?: ReactNode
theme: Theme theme: Theme
}>` }>`
...@@ -19,7 +19,12 @@ const Row = styled.div<{ ...@@ -19,7 +19,12 @@ const Row = styled.div<{
flex-grow: ${({ grow }) => grow && 1}; flex-grow: ${({ grow }) => grow && 1};
gap: ${({ gap }) => gap && `${gap}em`}; gap: ${({ gap }) => gap && `${gap}em`};
grid-auto-flow: column; grid-auto-flow: column;
grid-template-columns: ${({ grow, children }) => (grow ? `repeat(${Children.count(children)}, 1fr)` : '')}; grid-template-columns: ${({ grow, children }) => {
if (grow === 'first') return '1fr'
if (grow === 'last') return `repeat(${Children.count(children) - 1}, auto) 1fr`
if (grow) return `repeat(${Children.count(children)}, 1fr)`
return undefined
}};
justify-content: ${({ justify }) => justify ?? 'space-between'}; justify-content: ${({ justify }) => justify ?? 'space-between'};
padding: ${({ pad }) => pad && `0 ${pad}em`}; padding: ${({ pad }) => pad && `0 ${pad}em`};
` `
......
...@@ -4,7 +4,7 @@ import { useAtom } from 'jotai' ...@@ -4,7 +4,7 @@ import { useAtom } from 'jotai'
import { Check, LargeIcon } from 'lib/icons' import { Check, LargeIcon } from 'lib/icons'
import { maxSlippageAtom } from 'lib/state/settings' import { maxSlippageAtom } from 'lib/state/settings'
import styled, { ThemedText } from 'lib/theme' import styled, { ThemedText } from 'lib/theme'
import { ReactNode, useCallback, useRef } from 'react' import { PropsWithChildren, useCallback, useRef, useState } from 'react'
import { BaseButton, TextButton } from '../../Button' import { BaseButton, TextButton } from '../../Button'
import Column from '../../Column' import Column from '../../Column'
...@@ -16,71 +16,72 @@ const tooltip = ( ...@@ -16,71 +16,72 @@ const tooltip = (
<Trans>Your transaction will revert if the price changes unfavorably by more than this percentage.</Trans> <Trans>Your transaction will revert if the price changes unfavorably by more than this percentage.</Trans>
) )
const StyledOption = styled(TextButton)<{ selected: boolean }>` const Button = styled(TextButton)<{ selected: boolean }>`
${({ selected }) => optionCss(selected)} ${({ selected }) => optionCss(selected)}
` `
const StyledInputOption = styled(BaseButton)<{ selected: boolean }>` const Custom = styled(BaseButton)<{ selected: boolean }>`
${({ selected }) => optionCss(selected)} ${({ selected }) => optionCss(selected)}
${inputCss} ${inputCss}
border-color: ${({ selected, theme }) => (selected ? theme.active : 'transparent')} !important; border-color: ${({ selected, theme }) => (selected ? theme.active : 'transparent')} !important;
padding: calc(0.5em - 1px) 0.625em; padding: calc(0.75em - 3px) 0.625em;
` `
interface OptionProps<T> { interface OptionProps {
value: T wrapper: typeof Button | typeof Custom
selected: boolean selected: boolean
onSelect: (value: T) => void onSelect: () => void
} }
function Option<T>({ value, selected, onSelect }: OptionProps<T>) { function Option({ wrapper: Wrapper, children, selected, onSelect }: PropsWithChildren<OptionProps>) {
return ( return (
<StyledOption selected={selected} onClick={() => onSelect(value)}> <Wrapper selected={selected} onClick={onSelect}>
<Row> <Row gap={0.5}>
<ThemedText.Subhead2>{value}%</ThemedText.Subhead2> {children}
{selected && <LargeIcon icon={Check} />} <span style={{ width: '1.2em' }}>{selected && <LargeIcon icon={Check} />}</span>
</Row> </Row>
</StyledOption> </Wrapper>
)
}
function InputOption<T>({ value, children, selected, onSelect }: OptionProps<T> & { children: ReactNode }) {
return (
<StyledInputOption color="container" selected={selected} onClick={() => onSelect(value)}>
<ThemedText.Subhead2>
<Row>{children}</Row>
</ThemedText.Subhead2>
</StyledInputOption>
) )
} }
export default function MaxSlippageSelect() { export default function MaxSlippageSelect() {
const [maxSlippage, setMaxSlippage] = useAtom(maxSlippageAtom) const [maxSlippage, setMaxSlippage] = useAtom(maxSlippageAtom)
const [custom, setCustom] = useState('')
const input = useRef<HTMLInputElement>(null) const input = useRef<HTMLInputElement>(null)
const focus = useCallback(() => input.current?.focus(), [input]) const focus = useCallback(() => input.current?.focus(), [input])
//@TODO(ianlapham): hook up inputs to either set custom slippage or update to auto const onInputChange = useCallback(
//@TODO(ianlapham): update UI to match designs in spec (custom: string) => {
setCustom(custom)
const onInputSelect = useCallback( const numerator = Math.floor(+custom * 100)
(custom: Percent | 'auto') => { if (numerator) {
focus() setMaxSlippage(new Percent(numerator, 10_000))
if (custom !== undefined) { } else {
setMaxSlippage(custom) setMaxSlippage('auto')
} }
}, },
[focus, setMaxSlippage] [setMaxSlippage]
) )
const onInputSelect = useCallback(() => {
focus()
onInputChange(custom)
}, [custom, focus, onInputChange])
return ( return (
<Column gap={0.75}> <Column gap={0.75}>
<Label name={<Trans>Max slippage</Trans>} tooltip={tooltip} /> <Label name={<Trans>Max slippage</Trans>} tooltip={tooltip} />
<Row gap={0.5} grow> <Row gap={0.5} grow="last">
<Option value={'auto'} onSelect={setMaxSlippage} selected={maxSlippage === 'auto'} /> <Option wrapper={Button} selected={maxSlippage === 'auto'} onSelect={() => setMaxSlippage('auto')}>
<InputOption value={maxSlippage} onSelect={onInputSelect} selected={maxSlippage !== 'auto'}> <ThemedText.ButtonMedium>
<DecimalInput size={5} value={''} onChange={() => null} placeholder={t`Custom`} ref={input} />% <Trans>Auto</Trans>
</InputOption> </ThemedText.ButtonMedium>
</Option>
<Option wrapper={Custom} onSelect={onInputSelect} selected={maxSlippage !== 'auto'}>
<Row>
<DecimalInput value={custom} onChange={onInputChange} placeholder={t`Custom`} ref={input} />%
</Row>
</Option>
</Row> </Row>
</Column> </Column>
) )
......
...@@ -12,7 +12,7 @@ export const optionCss = (selected: boolean) => css` ...@@ -12,7 +12,7 @@ export const optionCss = (selected: boolean) => css`
color: ${({ theme }) => theme.primary} !important; color: ${({ theme }) => theme.primary} !important;
display: grid; display: grid;
grid-gap: 0.25em; grid-gap: 0.25em;
padding: 0.5em 0.625em; padding: calc(0.75em - 1px) 0.625em;
:enabled:hover { :enabled:hover {
border-color: ${({ theme }) => theme.onHover(selected ? theme.active : theme.outline)}; border-color: ${({ theme }) => theme.onHover(selected ? theme.active : theme.outline)};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment