-
Notifications
You must be signed in to change notification settings - Fork 41
952/set slippage #1011
952/set slippage #1011
Changes from all commits
fba1d9a
a8012fd
70de7e8
7ab9e6a
1f8a042
d079ad0
53b290f
401a453
fc4dc3a
0d61e03
1ca750d
3c63674
a3b0c92
ca162bd
033d273
87f5e37
39a8843
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import React from 'react' | ||
import styled from 'styled-components' | ||
|
||
import { MEDIA, SLIPPAGE_MAP } from 'const' | ||
import { PriceSlippageState } from 'reducers-actions/priceSlippage' | ||
|
||
const Wrapper = styled.div` | ||
font-size: 1.6rem; | ||
width: 100%; | ||
|
||
> strong { | ||
display: flex; | ||
align-items: center; | ||
text-transform: capitalize; | ||
color: var(--color-text-primary); | ||
width: 100%; | ||
margin: 0 0 1rem; | ||
padding: 0; | ||
box-sizing: border-box; | ||
font-size: 1.5rem; | ||
|
||
@media ${MEDIA.mobile} { | ||
font-size: 1.3rem; | ||
} | ||
} | ||
|
||
> div { | ||
display: flex; | ||
flex-flow: row wrap; | ||
align-items: center; | ||
justify-content: space-evenly; | ||
|
||
margin: 1rem auto 0.2rem; | ||
height: 4.4rem; | ||
width: 100%; | ||
|
||
> button { | ||
border-radius: 3rem; | ||
|
||
> small { | ||
font-size: x-small; | ||
margin-left: 0.4rem; | ||
} | ||
} | ||
|
||
> button, | ||
> label > input { | ||
display: flex; | ||
flex: 1; | ||
align-items: center; | ||
justify-content: space-evenly; | ||
|
||
background: var(--color-background-input); | ||
color: var(--color-text-primary); | ||
font-size: inherit; | ||
font-weight: normal; | ||
|
||
height: 100%; | ||
|
||
padding: 0.65rem 1.5rem; | ||
&:not(:last-child) { | ||
margin-right: 1rem; | ||
} | ||
|
||
white-space: nowrap; | ||
|
||
&.selected, | ||
&.selected ~ small, | ||
&:hover:not(input), | ||
&:focus, | ||
&:focus ~ small { | ||
color: var(--color-text-button-hover); | ||
} | ||
|
||
&:hover:not(input):not(.selected), | ||
&:focus { | ||
background-color: var(--color-background-button-hover); | ||
} | ||
|
||
&.selected { | ||
background: var(--color-text-active); | ||
} | ||
|
||
transition: all 0.2s ease-in-out; | ||
} | ||
|
||
> label { | ||
position: relative; | ||
flex: 1.6; | ||
height: 100%; | ||
|
||
> small { | ||
position: absolute; | ||
right: 2rem; | ||
top: 0; | ||
bottom: 0; | ||
margin: auto; | ||
|
||
display: flex; | ||
align-items: center; | ||
|
||
opacity: 0.75; | ||
color: var(--color-text-primary); | ||
|
||
letter-spacing: -0.05rem; | ||
text-align: right; | ||
font-weight: var(--font-weight-bold); | ||
|
||
@media ${MEDIA.mobile} { | ||
font-size: 1rem; | ||
letter-spacing: 0.03rem; | ||
} | ||
} | ||
|
||
> input { | ||
border-radius: var(--border-radius-top); | ||
margin: 0; | ||
padding-right: 3.4rem; | ||
width: 100%; | ||
|
||
&::placeholder { | ||
opacity: 0.6; | ||
} | ||
} | ||
} | ||
} | ||
` | ||
|
||
interface MaximumSlippageProps { | ||
priceSlippage: PriceSlippageState | ||
setNewSlippage: (customSlippage: string | React.ChangeEvent<HTMLInputElement>) => void | ||
} | ||
|
||
const slippagePercentages = Array.from(SLIPPAGE_MAP.keys()) | ||
|
||
const checkCustomPriceSlippage = (slippagePercentage: string): boolean => | ||
!!slippagePercentage && !SLIPPAGE_MAP.has(slippagePercentage) | ||
|
||
const MaximumSlippage: React.FC<MaximumSlippageProps> = ({ setNewSlippage, priceSlippage }) => { | ||
return ( | ||
<Wrapper> | ||
<strong>Limit additional price slippage</strong> | ||
<div> | ||
{slippagePercentages.map((slippage, index) => ( | ||
<button | ||
key={index} | ||
type="button" | ||
onClick={(): void => setNewSlippage(slippage)} | ||
className={slippage === priceSlippage ? 'selected' : ''} | ||
> | ||
{slippage}%{SLIPPAGE_MAP.get(slippage) && <small>(suggested)</small>} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SLIPPAGE_MAP.get(slippage) is always true as you construct |
||
</button> | ||
))} | ||
<label> | ||
<input | ||
type="number" | ||
step="0.1" | ||
placeholder="Custom" | ||
value={priceSlippage} | ||
className={checkCustomPriceSlippage(priceSlippage) ? 'selected' : ''} | ||
onChange={(e): void => setNewSlippage(e.target.value)} | ||
/> | ||
<small>%</small> | ||
</label> | ||
</div> | ||
</Wrapper> | ||
) | ||
} | ||
|
||
/* | ||
// PRICE SLIPPAGE | ||
const dispatchNewSlippage = (payload: string): void => dispatch(setPriceSlippage(payload)) | ||
// to add in Price component to show current selected slippage | ||
|
||
{priceSlippage && ( | ||
<FormMessage className="warning"> | ||
<small>{priceSlippage}% slippage</small> | ||
</FormMessage> | ||
)} | ||
*/ | ||
|
||
export default MaximumSlippage |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ import { OrderBookBtn } from 'components/OrderBookBtn' | |
|
||
// TradeWidget: subcomponents | ||
import { TradeFormData } from 'components/TradeWidget' | ||
import { FormInputError } from 'components/TradeWidget/FormMessage' | ||
import FormMessage, { FormInputError } from 'components/TradeWidget/FormMessage' | ||
import { useNumberInput } from 'components/TradeWidget/useNumberInput' | ||
|
||
const Wrapper = styled.div` | ||
|
@@ -25,17 +25,26 @@ const Wrapper = styled.div` | |
|
||
> strong { | ||
display: flex; | ||
align-items: center; | ||
text-transform: capitalize; | ||
color: var(--color-text-primary); | ||
width: 100%; | ||
margin: 0 0 1rem; | ||
padding: 0; | ||
box-sizing: border-box; | ||
font-size: 1.5rem; | ||
|
||
@media ${MEDIA.mobile} { | ||
font-size: 1.3rem; | ||
} | ||
|
||
> ${FormMessage} { | ||
width: min-content; | ||
white-space: nowrap; | ||
font-size: x-small; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we ever use font-size keywords? We usually do |
||
margin: 0 0.5rem; | ||
} | ||
|
||
> button { | ||
background: none; | ||
border: 0; | ||
|
@@ -235,6 +244,7 @@ const Price: React.FC<Props> = ({ sellToken, receiveToken, priceInputId, priceIn | |
</label> | ||
<FormInputError errorMessage={errorPriceInverse?.message} /> | ||
</PriceInputBox> | ||
{/* MAX SLIPPAGE CONTROL */} | ||
</Wrapper> | ||
) | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -21,6 +21,10 @@ export interface Actions<T, P> { | |||||
payload: P | ||||||
} | ||||||
|
||||||
export type ActionCreator<T, P> = (payload: P) => Actions<T, P> | ||||||
|
||||||
export type ReducerCreator<S, A> = (state: S, action: A) => S | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
maybe |
||||||
|
||||||
export interface GlobalState { | ||||||
tokens: TokenLocalState | ||||||
pendingOrders: PendingOrdersState | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { Actions, ActionCreator, ReducerCreator } from 'reducers-actions' | ||
import { DEFAULT_SUGGESTED_SLIPPAGE } from 'const' | ||
|
||
const ActionsList = ['SET_PRICE_SLIPPAGE'] as const | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i know right!? found it while thinking why can't i infer types from an array? then i can use directly AND have them inferred/autocompleted. looks a bit weird tho |
||
|
||
type PriceSlippageActions = typeof ActionsList[number] | ||
type PriceSlippagePayload = string | ||
|
||
type PriceSlippageState = PriceSlippagePayload | ||
|
||
const setPriceSlippage: ActionCreator<PriceSlippageActions, PriceSlippagePayload> = payload => ({ | ||
type: ActionsList[0], | ||
payload, | ||
}) | ||
|
||
const PRICE_SLIPPAGE_INITIAL_STATE = DEFAULT_SUGGESTED_SLIPPAGE | ||
|
||
const reducer: ReducerCreator<PriceSlippageState, Actions<PriceSlippageActions, PriceSlippagePayload>> = ( | ||
state, | ||
action, | ||
) => { | ||
switch (action.type) { | ||
case ActionsList[0]: | ||
return action.payload | ||
default: | ||
return state | ||
} | ||
} | ||
|
||
export { setPriceSlippage, reducer, PriceSlippageState, PRICE_SLIPPAGE_INITIAL_STATE } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to do something similar. Probably better to use the type:
here https://github.com/gnosis/dex-react/pull/1025/files#diff-493194965e4afe11bf4448365069f0aeR9