fix: add liquidity ux polish (#2024)
* zoom out on intiial * adjust initial zoom ranges * remove unneeded reactga event * adjust full range warning copy * update zoom * adjust zoom ranges and label around current price
This commit is contained in:
parent
2bc2a2c76e
commit
bf30013b6c
@ -40,6 +40,8 @@ const Tooltip = styled.text`
|
||||
// flips the handles draggers when close to the container edges
|
||||
const FLIP_HANDLE_THRESHOLD_PX = 20
|
||||
|
||||
const compare = (a1: [number, number], a2: [number, number]): boolean => a1[0] !== a2[0] || a1[1] !== a2[1]
|
||||
|
||||
export const Brush = ({
|
||||
id,
|
||||
xScale,
|
||||
@ -49,7 +51,8 @@ export const Brush = ({
|
||||
setBrushExtent,
|
||||
innerWidth,
|
||||
innerHeight,
|
||||
colors,
|
||||
westHandleColor,
|
||||
eastHandleColor,
|
||||
}: {
|
||||
id: string
|
||||
xScale: ScaleLinear<number, number>
|
||||
@ -59,10 +62,8 @@ export const Brush = ({
|
||||
setBrushExtent: (extent: [number, number]) => void
|
||||
innerWidth: number
|
||||
innerHeight: number
|
||||
colors: {
|
||||
west: string
|
||||
east: string
|
||||
}
|
||||
westHandleColor: string
|
||||
eastHandleColor: string
|
||||
}) => {
|
||||
const brushRef = useRef<SVGGElement | null>(null)
|
||||
const brushBehavior = useRef<BrushBehavior<SVGGElement> | null>(null)
|
||||
@ -84,13 +85,13 @@ export const Brush = ({
|
||||
const scaled = (selection as [number, number]).map(xScale.invert) as [number, number]
|
||||
|
||||
// avoid infinite render loop by checking for change
|
||||
if (type === 'end' && (brushExtent[0] !== scaled[0] || brushExtent[1] !== scaled[1])) {
|
||||
if (type === 'end' && compare(brushExtent, scaled)) {
|
||||
setBrushExtent(scaled)
|
||||
}
|
||||
|
||||
setLocalBrushExtent(scaled)
|
||||
},
|
||||
[xScale.invert, brushExtent, setBrushExtent]
|
||||
[xScale, brushExtent, setBrushExtent]
|
||||
)
|
||||
|
||||
// keep local and external brush extent in sync
|
||||
@ -114,10 +115,7 @@ export const Brush = ({
|
||||
|
||||
brushBehavior.current(select(brushRef.current))
|
||||
|
||||
if (
|
||||
previousBrushExtent &&
|
||||
(brushExtent[0] !== previousBrushExtent[0] || brushExtent[1] !== previousBrushExtent[1])
|
||||
) {
|
||||
if (previousBrushExtent && compare(brushExtent, previousBrushExtent)) {
|
||||
select(brushRef.current)
|
||||
.transition()
|
||||
.call(brushBehavior.current.move as any, brushExtent.map(xScale))
|
||||
@ -136,9 +134,7 @@ export const Brush = ({
|
||||
if (!brushRef.current || !brushBehavior.current) return
|
||||
|
||||
brushBehavior.current.move(select(brushRef.current) as any, brushExtent.map(xScale) as any)
|
||||
// dependency on brushExtent would start an update loop
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [xScale])
|
||||
}, [brushExtent, xScale])
|
||||
|
||||
useEffect(() => {
|
||||
setShowLabels(true)
|
||||
@ -154,8 +150,8 @@ export const Brush = ({
|
||||
<>
|
||||
<defs>
|
||||
<linearGradient id={`${id}-gradient-selection`} x1="0%" y1="100%" x2="100%" y2="100%">
|
||||
<stop stopColor={colors.west} />
|
||||
<stop stopColor={colors.east} offset="1" />
|
||||
<stop stopColor={westHandleColor} />
|
||||
<stop stopColor={eastHandleColor} offset="1" />
|
||||
</linearGradient>
|
||||
|
||||
{/* clips at exactly the svg area */}
|
||||
@ -186,7 +182,7 @@ export const Brush = ({
|
||||
}, 1)`}
|
||||
>
|
||||
<g clipPath={`url(#${id}-handles-clip)`}>
|
||||
<Handle color={colors.west} d={brushHandlePath(innerHeight)} />
|
||||
<Handle color={westHandleColor} d={brushHandlePath(innerHeight)} />
|
||||
<HandleAccent d={brushHandleAccentPath()} />
|
||||
</g>
|
||||
|
||||
@ -208,7 +204,7 @@ export const Brush = ({
|
||||
}, 1)`}
|
||||
>
|
||||
<g clipPath={`url(#${id}-handles-clip)`}>
|
||||
<Handle color={colors.east} d={brushHandlePath(innerHeight)} />
|
||||
<Handle color={eastHandleColor} d={brushHandlePath(innerHeight)} />
|
||||
<HandleAccent d={brushHandleAccentPath()} />
|
||||
</g>
|
||||
|
||||
@ -228,8 +224,7 @@ export const Brush = ({
|
||||
),
|
||||
[
|
||||
brushLabelValue,
|
||||
colors.east,
|
||||
colors.west,
|
||||
eastHandleColor,
|
||||
flipEastHandle,
|
||||
flipWestHandle,
|
||||
hovering,
|
||||
@ -238,6 +233,7 @@ export const Brush = ({
|
||||
innerWidth,
|
||||
localBrushExtent,
|
||||
showLabels,
|
||||
westHandleColor,
|
||||
xScale,
|
||||
]
|
||||
)
|
||||
|
@ -24,7 +24,7 @@ export function Chart({
|
||||
}: LiquidityChartRangeInputProps) {
|
||||
const svgRef = useRef<SVGSVGElement | null>(null)
|
||||
|
||||
const [zoom, setZoom] = useState<ZoomTransform>()
|
||||
const [zoom, setZoom] = useState<ZoomTransform | null>(null)
|
||||
|
||||
const [innerHeight, innerWidth] = useMemo(
|
||||
() => [height - margins.top - margins.bottom, width - margins.left - margins.right],
|
||||
@ -34,7 +34,7 @@ export function Chart({
|
||||
const { xScale, yScale } = useMemo(() => {
|
||||
const scales = {
|
||||
xScale: scaleLinear()
|
||||
.domain([(1 - zoomLevels.initial) * current, (1 + zoomLevels.initial) * current] as number[])
|
||||
.domain([current * zoomLevels.initialMin, current * zoomLevels.initialMax] as number[])
|
||||
.range([0, innerWidth]),
|
||||
yScale: scaleLinear()
|
||||
.domain([0, max(series, yAccessor)] as number[])
|
||||
@ -47,7 +47,12 @@ export function Chart({
|
||||
}
|
||||
|
||||
return scales
|
||||
}, [zoomLevels.initial, current, innerWidth, series, innerHeight, zoom])
|
||||
}, [current, zoomLevels.initialMin, zoomLevels.initialMax, innerWidth, series, innerHeight, zoom])
|
||||
|
||||
useEffect(() => {
|
||||
// reset zoom as necessary
|
||||
setZoom(null)
|
||||
}, [zoomLevels])
|
||||
|
||||
useEffect(() => {
|
||||
if (!brushDomain) {
|
||||
@ -121,10 +126,8 @@ export function Chart({
|
||||
innerWidth={innerWidth}
|
||||
innerHeight={innerHeight}
|
||||
setBrushExtent={onBrushDomainChange}
|
||||
colors={{
|
||||
west: styles.brush.handle.west,
|
||||
east: styles.brush.handle.east,
|
||||
}}
|
||||
westHandleColor={styles.brush.handle.west}
|
||||
eastHandleColor={styles.brush.handle.east}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
@ -45,7 +45,7 @@ export default function Zoom({
|
||||
}) {
|
||||
const zoomBehavior = useRef<ZoomBehavior<Element, unknown>>()
|
||||
|
||||
const [zoomIn, zoomOut, reset] = useMemo(
|
||||
const [zoomIn, zoomOut, reset, initial] = useMemo(
|
||||
() => [
|
||||
() =>
|
||||
svg &&
|
||||
@ -65,6 +65,12 @@ export default function Zoom({
|
||||
select(svg as Element)
|
||||
.transition()
|
||||
.call(zoomBehavior.current.scaleTo, 1),
|
||||
() =>
|
||||
svg &&
|
||||
zoomBehavior.current &&
|
||||
select(svg as Element)
|
||||
.transition()
|
||||
.call(zoomBehavior.current.scaleTo, 0.5),
|
||||
],
|
||||
[svg, zoomBehavior]
|
||||
)
|
||||
@ -72,7 +78,6 @@ export default function Zoom({
|
||||
useEffect(() => {
|
||||
if (!svg) return
|
||||
|
||||
// zoom
|
||||
zoomBehavior.current = zoom()
|
||||
.scaleExtent([zoomLevels.min, zoomLevels.max])
|
||||
.translateExtent([
|
||||
@ -88,7 +93,12 @@ export default function Zoom({
|
||||
select(svg as Element)
|
||||
.call(zoomBehavior.current)
|
||||
.on('mousedown.zoom', null)
|
||||
}, [innerHeight, innerWidth, setZoom, svg, xScale, zoomBehavior, zoomLevels.max, zoomLevels.min])
|
||||
}, [innerHeight, innerWidth, setZoom, svg, xScale, zoomBehavior, zoomLevels, zoomLevels.max, zoomLevels.min])
|
||||
|
||||
useEffect(() => {
|
||||
// reset zoom to initial on zoomLevel chang
|
||||
initial()
|
||||
}, [initial, zoomLevels])
|
||||
|
||||
return (
|
||||
<Wrapper count={showClear ? 3 : 2}>
|
||||
|
@ -20,17 +20,20 @@ import { ZoomLevels } from './types'
|
||||
|
||||
const ZOOM_LEVELS: Record<FeeAmount, ZoomLevels> = {
|
||||
[FeeAmount.LOW]: {
|
||||
initial: 0.002,
|
||||
initialMin: 0.999,
|
||||
initialMax: 1.001,
|
||||
min: 0.001,
|
||||
max: 2,
|
||||
max: 1.5,
|
||||
},
|
||||
[FeeAmount.MEDIUM]: {
|
||||
initial: 0.3,
|
||||
initialMin: 0.5,
|
||||
initialMax: 2,
|
||||
min: 0.01,
|
||||
max: 20,
|
||||
},
|
||||
[FeeAmount.HIGH]: {
|
||||
initial: 0.3,
|
||||
initialMin: 0.5,
|
||||
initialMax: 2,
|
||||
min: 0.01,
|
||||
max: 20,
|
||||
},
|
||||
@ -95,11 +98,6 @@ export default function LiquidityChartRangeInput({
|
||||
let leftRangeValue = Number(domain[0])
|
||||
const rightRangeValue = Number(domain[1])
|
||||
|
||||
ReactGA.event({
|
||||
category: 'Liquidity',
|
||||
action: 'Chart brushed',
|
||||
})
|
||||
|
||||
if (leftRangeValue <= 0) {
|
||||
leftRangeValue = 1 / 10 ** 6
|
||||
}
|
||||
@ -133,7 +131,9 @@ export default function LiquidityChartRangeInput({
|
||||
if (d === 'w' && ticksAtLimit[Bound.LOWER]) return '0'
|
||||
if (d === 'e' && ticksAtLimit[Bound.UPPER]) return '∞'
|
||||
|
||||
const percent = (((x < price ? -1 : 1) * (Math.max(x, price) - Math.min(x, price))) / Math.min(x, price)) * 100
|
||||
//const percent = (((x < price ? -1 : 1) * (Math.max(x, price) - Math.min(x, price))) / Math.min(x, price)) * 100
|
||||
|
||||
const percent = (x < price ? -1 : 1) * ((Math.max(x, price) - Math.min(x, price)) / price) * 100
|
||||
|
||||
return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : ''
|
||||
},
|
||||
|
@ -16,7 +16,8 @@ export interface Margins {
|
||||
}
|
||||
|
||||
export interface ZoomLevels {
|
||||
initial: number
|
||||
initialMin: number
|
||||
initialMax: number
|
||||
min: number
|
||||
max: number
|
||||
}
|
||||
|
@ -842,8 +842,7 @@ export default function AddLiquidity({
|
||||
<RowFixed>
|
||||
<TYPE.yellow ml="12px" fontSize="13px" margin={0} fontWeight={400}>
|
||||
<Trans>
|
||||
On Uniswap V3, setting a range across all prices like V2 is less capital efficient
|
||||
than a concentrated one. Learn more{' '}
|
||||
Full range positions may earn less fees than concentrated positions. Learn more{' '}
|
||||
<ExternalLink
|
||||
style={{ color: theme.yellow3, textDecoration: 'underline' }}
|
||||
href={''}
|
||||
|
Loading…
Reference in New Issue
Block a user