Skip to content

Commit

Permalink
Update new tab kyberai (#2324)
Browse files Browse the repository at this point in the history
* add liquidity profile tab

* add markets table

* add api for liquidity market

* update get data from cgk

* update api

* update UI

* update UI

* update UI

* hide perpetual chart on cgk api

* update fundingrate color

* show action button on both dex and cex table

* increase limit to 50

* update api param and icon

* update pagination and missing icon

* improve mobile UI

* revert env
  • Loading branch information
XiaoYhun authored Oct 31, 2023
1 parent 5262af4 commit 6cf5cf8
Show file tree
Hide file tree
Showing 18 changed files with 653 additions and 83 deletions.
37 changes: 28 additions & 9 deletions src/assets/svg/sprite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 8 additions & 7 deletions src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -393,34 +393,35 @@ export const ButtonApprove = ({
)
}

const StyledButtonAction = styled(RebassButton)`
const StyledButtonAction = styled(RebassButton)<{ $color?: string }>`
cursor: pointer;
appearance: none;
padding: 2px;
background-color: transparent;
display: flex;
align-items: center;
outline: none;
border-radius: 50%;
border: none;
color: unset;
transition: all 0.1s;
background-color: ${({ $color }) => $color + '32' || 'transparent'};
color: ${({ $color }) => $color || 'unset'};
:hover {
background-color: ${({ theme }) => theme.subText + '20'};
background-color: ${({ theme, $color }) => ($color ? $color + '20' : theme.subText + '20')};
}
:active {
background-color: ${({ theme }) => theme.subText + '10'};
background-color: ${({ theme, $color }) => ($color ? $color + '10' : theme.subText + '10')};
transform: translateY(2px);
}
`

export const ButtonAction = ({
onClick,
children,
color,
...rest
}: { onClick?: () => void; children: ReactNode } & ButtonProps) => {
}: { onClick?: () => void; children: ReactNode } & ButtonProps & { color?: string }) => {
return (
<StyledButtonAction onClick={onClick} {...rest}>
<StyledButtonAction onClick={onClick} $color={color} {...rest}>
{children}
</StyledButtonAction>
)
Expand Down
102 changes: 77 additions & 25 deletions src/components/Select/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { t } from '@lingui/macro'
import { Placement } from '@popperjs/core'
import { Portal } from '@reach/portal'
import { AnimatePresence, motion } from 'framer-motion'
import { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react'
import { usePopper } from 'react-popper'
import styled from 'styled-components'

import Icon from 'components/Icons/Icon'
import { Z_INDEXS } from 'constants/styles'
import { useOnClickOutside } from 'hooks/useOnClickOutside'

Expand All @@ -27,17 +29,18 @@ const SelectWrapper = styled.div`
`

const SelectMenu = styled(motion.div)`
padding: 8px;
border-radius: 16px;
overflow: hidden;
filter: drop-shadow(0px 4px 12px rgba(0, 0, 0, 0.36));
z-index: 2;
background: ${({ theme }) => theme.tabActive};
padding: 10px 0px;
width: max-content;
`

const Option = styled.div<{ $selected: boolean }>`
padding: 10px 18px;
padding: 8px;
border-radius: 8px;
cursor: pointer;
font-size: 12px;
color: ${({ theme }) => theme.subText};
Expand All @@ -55,6 +58,33 @@ const SelectedWrap = styled.div`
flex: 1;
user-select: none;
`

const SearchWrapper = styled.div`
position: relative;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
background-color: ${({ theme }) => theme.buttonGray};
margin-bottom: 8px;
transition: all 0.1s ease;
transition-property: background-color, color;
color: ${({ theme }) => theme.subText};
:hover {
background-color: ${({ theme }) => theme.buttonBlack};
color: ${({ theme }) => theme.text};
}
:focus-within {
background-color: ${({ theme }) => theme.buttonBlack};
color: ${({ theme }) => theme.text};
}
input {
width: 100%;
padding-inline-start: 40px;
line-height: 32px;
color: ${({ theme }) => theme.text};
}
`
export type SelectOption = { value?: string | number; label: ReactNode; onSelect?: () => void }

const getOptionValue = (option: SelectOption | undefined) => {
Expand All @@ -81,6 +111,7 @@ function Select({
arrowColor,
dropdownRender,
onHideMenu,
withSearch,
placement = 'bottom',
}: {
value?: string | number
Expand All @@ -96,10 +127,12 @@ function Select({
forceMenuPlacementTop?: boolean
arrowColor?: string
placement?: string
withSearch?: boolean
onHideMenu?: () => void // hide without changes
}) {
const [selected, setSelected] = useState(getOptionValue(options?.[0]))
const [showMenu, setShowMenu] = useState(false)
const [searchValue, setSearchValue] = useState('')
const [menuPlacementTop] = useState(forceMenuPlacementTop)

useEffect(() => {
Expand All @@ -116,30 +149,36 @@ function Select({
const selectedInfo = options.find(item => getOptionValue(item) === selected)

const renderMenu = () => {
return options.map(item => {
const value = getOptionValue(item)
const onClick = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.preventDefault()
setShowMenu(false)
if (item.onSelect) item.onSelect?.()
else {
setSelected(value)
onChange?.(value)
return options
.filter(item => {
if (!withSearch) return true
return item.label?.toString().toLowerCase().includes(searchValue.toLowerCase())
})
.map(item => {
const value = getOptionValue(item)
const onClick = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.preventDefault()
setShowMenu(false)
setSearchValue('')
if (item.onSelect) item.onSelect?.()
else {
setSelected(value)
onChange?.(value)
}
}
}
return (
<Option
key={value}
role="button"
$selected={value === selectedValue || value === getOptionValue(selectedInfo)}
onClick={onClick}
style={optionStyle}
>
{optionRender ? optionRender(item) : getOptionLabel(item)}
</Option>
)
})
return (
<Option
key={value}
role="button"
$selected={value === selectedValue || value === getOptionValue(selectedInfo)}
onClick={onClick}
style={optionStyle}
>
{optionRender ? optionRender(item) : getOptionLabel(item)}
</Option>
)
})
}

const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
Expand Down Expand Up @@ -180,6 +219,19 @@ function Select({
transition={{ duration: 0.1 }}
style={menuStyle}
>
{withSearch && (
<SearchWrapper onClick={e => e.stopPropagation()}>
<span style={{ position: 'absolute', left: '8px' }}>
<Icon id="search" />
</span>
<input
placeholder={t`Search...`}
style={{ background: 'transparent', outline: 'none', border: 'none' }}
value={searchValue}
onChange={e => setSearchValue(e.target.value)}
/>
</SearchWrapper>
)}
<div>{dropdownRender ? dropdownRender(renderMenu()) : renderMenu()}</div>
</SelectMenu>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ export const ICON_IDS = [
'alarm',
'on-chain',
'technical-analysis',
'liquidity-analysis',
'news',
'arrow',
'chart',
Expand Down
1 change: 1 addition & 0 deletions src/pages/TrueSightV2/components/TokenFilter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ export default function TokenFilter({
onChange={value => onChangeFilter(queryKey, value)}
optionStyle={{ fontSize: '14px' }}
menuStyle={menuStyle}
withSearch={queryKey === 'categories'}
/>
))}
{isWatchlistTab && (
Expand Down
55 changes: 55 additions & 0 deletions src/pages/TrueSightV2/components/chart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2814,3 +2814,58 @@ export const Prochart = ({
</ProLiveChartWrapper>
)
}

/* IN DEVELOPMENT */
export const LiquidityProfile = () => {
const theme = useTheme()

const { state, dispatch } = useChartStatesContext(KYBERAI_CHART_ID.LIQUIDITY_PROFILE, {
showOptions: ['showPriceImpact', 'showBuy', 'showSell'],
noData: true,
})

const showPriceImpact = state?.showOptions?.includes('showPriceImpact')
const showBuy = state?.showOptions?.includes('showBuy')
const showSell = state?.showOptions?.includes('showSell')
const above768 = useMedia(`(min-width: ${MEDIA_WIDTHS.upToSmall}px)`)

return (
<ChartWrapper>
<LegendWrapper>
<LegendButton
text="Price Impact"
iconStyle={{ backgroundColor: rgba(theme.warning, 0.6) }}
enabled={showPriceImpact}
onClick={() =>
dispatch({ type: CHART_STATES_ACTION_TYPE.TOGGLE_OPTION, payload: { option: 'showPriceImpact' } })
}
/>
<LegendButton
text="Buy"
iconStyle={{ backgroundColor: rgba(theme.primary, 0.6) }}
enabled={showBuy}
onClick={() => dispatch({ type: CHART_STATES_ACTION_TYPE.TOGGLE_OPTION, payload: { option: 'showBuy' } })}
/>
<LegendButton
text="Sell"
iconStyle={{ backgroundColor: rgba(theme.red, 0.6) }}
enabled={showSell}
onClick={() => dispatch({ type: CHART_STATES_ACTION_TYPE.TOGGLE_OPTION, payload: { option: 'showSell' } })}
/>
</LegendWrapper>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
width={500}
height={400}
data={[]}
stackOffset="sign"
margin={above768 ? { top: 80, left: 20, right: 20 } : { top: 100, left: 10, right: 10, bottom: 10 }}
>
<CartesianGrid vertical={false} strokeWidth={1} stroke={rgba(theme.border, 0.5)} />
<Customized component={KyberLogo} />
{/* IN DEVELOPMENT */}
</ComposedChart>
</ResponsiveContainer>
</ChartWrapper>
)
}
Loading

0 comments on commit 6cf5cf8

Please sign in to comment.