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`
border: 1px solid ${({ theme }) => theme.container};
border-radius: ${({ theme }) => theme.borderRadius}em;
cursor: text;
padding: calc(0.75em - 1px);
padding: calc(0.5em - 1px);
:hover:not(:focus-within) {
background-color: ${({ theme }) => theme.onHover(theme.container)};
......
......@@ -8,7 +8,7 @@ const Row = styled.div<{
pad?: number
gap?: number
flex?: true
grow?: true
grow?: true | 'first' | 'last'
children?: ReactNode
theme: Theme
}>`
......@@ -19,7 +19,12 @@ const Row = styled.div<{
flex-grow: ${({ grow }) => grow && 1};
gap: ${({ gap }) => gap && `${gap}em`};
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'};
padding: ${({ pad }) => pad && `0 ${pad}em`};
`
......
......@@ -4,7 +4,7 @@ import { useAtom } from 'jotai'
import { Check, LargeIcon } from 'lib/icons'
import { maxSlippageAtom } from 'lib/state/settings'
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 Column from '../../Column'
......@@ -16,71 +16,72 @@ const tooltip = (
<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)}
`
const StyledInputOption = styled(BaseButton)<{ selected: boolean }>`
const Custom = styled(BaseButton)<{ selected: boolean }>`
${({ selected }) => optionCss(selected)}
${inputCss}
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> {
value: T
interface OptionProps {
wrapper: typeof Button | typeof Custom
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 (
<StyledOption selected={selected} onClick={() => onSelect(value)}>
<Row>
<ThemedText.Subhead2>{value}%</ThemedText.Subhead2>
{selected && <LargeIcon icon={Check} />}
<Wrapper selected={selected} onClick={onSelect}>
<Row gap={0.5}>
{children}
<span style={{ width: '1.2em' }}>{selected && <LargeIcon icon={Check} />}</span>
</Row>
</StyledOption>
)
}
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>
</Wrapper>
)
}
export default function MaxSlippageSelect() {
const [maxSlippage, setMaxSlippage] = useAtom(maxSlippageAtom)
const [custom, setCustom] = useState('')
const input = useRef<HTMLInputElement>(null)
const focus = useCallback(() => input.current?.focus(), [input])
//@TODO(ianlapham): hook up inputs to either set custom slippage or update to auto
//@TODO(ianlapham): update UI to match designs in spec
const onInputSelect = useCallback(
(custom: Percent | 'auto') => {
focus()
if (custom !== undefined) {
setMaxSlippage(custom)
const onInputChange = useCallback(
(custom: string) => {
setCustom(custom)
const numerator = Math.floor(+custom * 100)
if (numerator) {
setMaxSlippage(new Percent(numerator, 10_000))
} else {
setMaxSlippage('auto')
}
},
[focus, setMaxSlippage]
[setMaxSlippage]
)
const onInputSelect = useCallback(() => {
focus()
onInputChange(custom)
}, [custom, focus, onInputChange])
return (
<Column gap={0.75}>
<Label name={<Trans>Max slippage</Trans>} tooltip={tooltip} />
<Row gap={0.5} grow>
<Option value={'auto'} onSelect={setMaxSlippage} selected={maxSlippage === 'auto'} />
<InputOption value={maxSlippage} onSelect={onInputSelect} selected={maxSlippage !== 'auto'}>
<DecimalInput size={5} value={''} onChange={() => null} placeholder={t`Custom`} ref={input} />%
</InputOption>
<Row gap={0.5} grow="last">
<Option wrapper={Button} selected={maxSlippage === 'auto'} onSelect={() => setMaxSlippage('auto')}>
<ThemedText.ButtonMedium>
<Trans>Auto</Trans>
</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>
</Column>
)
......
......@@ -12,7 +12,7 @@ export const optionCss = (selected: boolean) => css`
color: ${({ theme }) => theme.primary} !important;
display: grid;
grid-gap: 0.25em;
padding: 0.5em 0.625em;
padding: calc(0.75em - 1px) 0.625em;
:enabled:hover {
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