Skip to content

Commit

Permalink
Replace network select
Browse files Browse the repository at this point in the history
  • Loading branch information
lubej committed Jul 7, 2023
1 parent cc147a9 commit 6fbeff0
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 100 deletions.
1 change: 1 addition & 0 deletions .changelog/667.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Graph network dropdown
85 changes: 66 additions & 19 deletions src/app/components/Select/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import SelectUnstyled, { SelectProps, selectClasses, SelectRootSlotProps } from '@mui/base/Select'
import Option, { optionClasses } from '@mui/base/Option'
import Popper from '@mui/base/Popper'
import Popper, { PopperPlacementType } from '@mui/base/Popper'
import { styled } from '@mui/material/styles'
import Box from '@mui/material/Box'
import {
Expand All @@ -21,8 +21,11 @@ import ExpandLessIcon from '@mui/icons-material/ExpandLess'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import { COLORS } from '../../../styles/theme/colors'
import { useTranslation } from 'react-i18next'
import { WithOptionalOwnerState } from '@mui/base/utils'
import { SelectPopperSlotProps, SelectSlots } from '@mui/base/Select/Select.types'
import * as React from 'react'

const StyledButton = styled(Button)(({ theme }) => ({
export const StyledSelectButton = styled(Button)(({ theme }) => ({
height: '36px',
minWidth: '135px',
padding: `0 ${theme.spacing(4)}`,
Expand All @@ -35,7 +38,7 @@ const StyledButton = styled(Button)(({ theme }) => ({
},
}))

const StyledListbox = styled('ul')(({ theme }) => ({
export const StyledSelectListbox = styled('ul')(({ theme }) => ({
boxSizing: 'border-box',
padding: theme.spacing(0),
margin: theme.spacing(3, 0),
Expand All @@ -47,7 +50,7 @@ const StyledListbox = styled('ul')(({ theme }) => ({
filter: 'drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25))',
}))

const StyledOption = styled(Option)(({ theme }) => ({
export const StyledSelectOption = styled(Option)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
boxSizing: 'border-box',
Expand Down Expand Up @@ -85,47 +88,85 @@ const TertiaryButton = forwardRef(
const { t } = useTranslation()

return (
<StyledButton {...restProps} ref={ref} color="tertiary">
<StyledSelectButton {...restProps} ref={ref} color="tertiary">
<Typography variant="select">{children ? children : t('select.placeholder')}</Typography>
{ownerState.open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</StyledButton>
</StyledSelectButton>
)
},
)

const CustomSelect = forwardRef(function CustomSelect<TValue extends string | number>(
props: SelectProps<TValue, false>,
{
root,
listbox,
popper,
placement,
...restProps
}: SelectProps<TValue, false> & SelectSlots<TValue, false> & Partial<SelectPopperSlotProps<TValue, false>>,
ref: ForwardedRef<HTMLButtonElement>,
) {
const slots: SelectProps<TValue, false>['slots'] = {
root: TertiaryButton,
listbox: StyledListbox,
popper: StyledPopper,
...props.slots,
root,
listbox,
popper,
...restProps.slots,
}

return <SelectUnstyled {...props} ref={ref} slots={slots} />
return (
<SelectUnstyled
{...restProps}
ref={ref}
slots={slots}
slotProps={{
popper: {
placement,
},
}}
/>
)
}) as <TValue extends string | number>(
props: SelectProps<TValue, false> & RefAttributes<HTMLButtonElement>,
props: SelectProps<TValue, false> &
SelectSlots<TValue, false> &
Partial<SelectPopperSlotProps<TValue, false>> &
RefAttributes<HTMLButtonElement>,
) => JSX.Element

export interface SelectOptionBase {
label: string | number
value: string | number
}

const SelectOption = <T extends SelectOptionBase>({ value, label }: T): ReactElement => (
<StyledSelectOption key={value} value={value}>
<Typography variant="select">{label}</Typography>
</StyledSelectOption>
)

interface SelectCmpProps<T extends SelectOptionBase> {
label?: string
options: T[]
defaultValue?: T['value']
handleChange?: (selectedOption: T['value'] | null) => void
placement?: PopperPlacementType
className?: string
root?: React.ElementType
Option?: typeof SelectOption<T> | undefined
listbox?: React.ElementType
popper?: React.ComponentType<WithOptionalOwnerState<SelectPopperSlotProps<T['value'], false>>>
}

const SelectCmp = <T extends SelectOptionBase>({
label,
options,
defaultValue,
handleChange,
placement = 'bottom-start',
className,
root = TertiaryButton,
listbox = StyledSelectListbox,
popper = StyledPopper,
Option = SelectOption,
}: SelectCmpProps<T>): ReactElement => {
const selectId = useId()

Expand All @@ -137,17 +178,23 @@ const SelectCmp = <T extends SelectOptionBase>({
)

return (
<Box>
<Box className={className}>
{label && (
<label htmlFor={selectId}>
<Typography variant="body2">{label}</Typography>
</label>
)}
<CustomSelect<T['value']> id={selectId} defaultValue={defaultValue} onChange={onChange}>
{options.map(({ label, value }) => (
<StyledOption key={value} value={value}>
<Typography variant="select">{label}</Typography>
</StyledOption>
<CustomSelect<T['value']>
id={selectId}
defaultValue={defaultValue}
onChange={onChange}
root={root}
listbox={listbox}
popper={popper}
placement={placement}
>
{options.map((props: T) => (
<Option key={props.value.toString()} {...props} />
))}
</CustomSelect>
</Box>
Expand Down
180 changes: 108 additions & 72 deletions src/app/pages/HomePage/Graph/NetworkSelector/index.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,136 @@
import { FC, useState } from 'react'
import { FC, ForwardedRef, forwardRef, ReactElement } from 'react'
import { useTranslation } from 'react-i18next'
import { useTheme } from '@mui/material/styles'
import { useScreenSize } from '../../../../hooks/useScreensize'
import AddIcon from '@mui/icons-material/Add'
import Box from '@mui/material/Box'
import Button from '@mui/material/Button'
import IconButton from '@mui/material/IconButton'
import RemoveIcon from '@mui/icons-material/Remove'
import Typography from '@mui/material/Typography'
import { styled } from '@mui/material/styles'
import { COLORS } from '../../../../../styles/theme/colors'
import { getNetworkNames, Network } from '../../../../../types/network'
import Collapse from '@mui/material/Collapse'
import { RouteUtils } from '../../../../utils/route-utils'
import {
Select,
SelectOptionBase,
StyledSelectButton,
StyledSelectListbox,
StyledSelectOption,
} from '../../../../components/Select'
import { selectClasses, SelectRootSlotProps } from '@mui/base/Select'
import ExpandLessIcon from '@mui/icons-material/ExpandLess'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import { getNetworkIcons } from '../../../../utils/content'
import { optionClasses } from '@mui/base/Option'

const StyledNetworkSelector = styled(Box)(() => ({
interface NetworkOption extends SelectOptionBase {
label: Network
value: Network
}

const StyledNetworkSelector = styled(Select<NetworkOption>)(({ theme }) => ({
position: 'absolute',
bottom: 0,
bottom: theme.spacing(5),
display: 'flex',
width: '100%',
justifyContent: 'center',
}))

const StyledBox = styled(Box)(({ theme }) => ({
const StyledButton = styled(StyledSelectButton)(({ theme }) => ({
height: '44px',
minWidth: '200px',
backgroundColor: theme.palette.layout.primaryBackground,
border: `solid 3px ${theme.palette.layout.lightBorder}`,
borderRadius: '45px',
padding: theme.spacing(3, 4),
display: 'inline-flex',
alignItems: 'center',
border: `2px solid ${theme.palette.layout.lightBorder}`,
color: theme.palette.layout.main,
'&:hover': {
backgroundColor: theme.palette.layout.primaryBackground,
},
[`&.${selectClasses.focusVisible}`]: {
backgroundColor: theme.palette.layout.primaryBackground,
outline: 'none',
border: `2px solid ${theme.palette.layout.main}`,
},
}))

export const SelectNetworkButton = styled(Button, {
shouldForwardProp: prop => prop !== 'isSelectedNetwork',
})<{ isSelectedNetwork: boolean }>(({ isSelectedNetwork, theme }) => ({
height: '30px',
padding: theme.spacing(2, 3),
textTransform: 'capitalize',
fontSize: '16px',
borderRadius: '9px',
backgroundColor: isSelectedNetwork
? theme.palette.layout.primaryBackground
: theme.palette.layout.secondary,
borderColor: isSelectedNetwork ? theme.palette.layout.hoverBorder : theme.palette.layout.secondary,
borderWidth: theme.spacing(1),
color: theme.palette.layout.main,
'&:hover, &:focus-visible': {
backgroundColor: theme.palette.layout.secondary,
borderWidth: theme.spacing(1),
borderColor: COLORS.white,
const StyledListbox = styled(StyledSelectListbox)(() => ({
minWidth: '200px',
background: COLORS.white,
color: COLORS.grayDark,
}))

const StyledOption = styled(StyledSelectOption)(() => ({
height: '44px',
color: COLORS.grayDark,
svg: {
color: COLORS.grayExtraDark,
},
[`&:hover:not(.${optionClasses.disabled}),
&.${optionClasses.highlighted}`]: {
backgroundColor: COLORS.grayLight,
},
}))
SelectNetworkButton.defaultProps = {
size: 'small',
variant: 'outlined',
sx: { ml: 4 },

const SelectOption = ({ value }: NetworkOption): ReactElement => {
const { t } = useTranslation()

const labels = getNetworkNames(t)
const icons = getNetworkIcons()

return (
<StyledOption key={value} value={value}>
<Box
sx={theme => ({ display: 'flex', gap: theme.spacing(3), pl: theme.spacing(3), alignItems: 'center' })}
>
{icons[value]}
<Typography variant="inherit">{labels[value]}</Typography>
</Box>
</StyledOption>
)
}

type NetworkSelectorProps = {
const NetworkSelectorButton = forwardRef(
(props: SelectRootSlotProps<Network, false>, ref: ForwardedRef<HTMLButtonElement>) => {
const { ownerState, ...restProps } = props
const { open, value } = ownerState
const { t } = useTranslation()
const label = getNetworkNames(t)
const icons = getNetworkIcons()

return (
<StyledButton {...restProps} ref={ref} color="inherit">
<Box
sx={theme => ({
display: 'flex',
gap: theme.spacing(3),
pl: theme.spacing(3),
alignItems: 'center',
})}
>
{icons[value!]}
<Typography variant="inherit">{label[value!]}</Typography>
</Box>
{open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</StyledButton>
)
},
)

interface NetworkSelectProps {
network: Network
setNetwork: (network: Network) => void
setNetwork: (network: Network | null) => void
}

export const NetworkSelector: FC<NetworkSelectorProps> = ({ network, setNetwork }) => {
const { t } = useTranslation()
const theme = useTheme()
const { isMobile } = useScreenSize()
const [open, setOpen] = useState(false)
const options: Network[] = RouteUtils.getEnabledNetworks()
const labels = getNetworkNames(t)
export const NetworkSelector: FC<NetworkSelectProps> = ({ network, setNetwork }) => {
const options = RouteUtils.getEnabledNetworks().map(network => ({
label: network,
value: network,
}))

return (
<StyledNetworkSelector>
<StyledBox>
{!isMobile && (
<Typography component="span" sx={{ fontSize: '12px', color: theme.palette.layout.main }}>
{t('home.selectNetwork')}
</Typography>
)}
<Box sx={{ height: 30, display: 'flex' }}>
{options.map(option => (
<Collapse orientation="horizontal" in={open || network === option} key={option}>
<SelectNetworkButton onClick={() => setNetwork(option)} isSelectedNetwork={option === network}>
{labels[option]}
</SelectNetworkButton>
</Collapse>
))}
</Box>
<IconButton aria-label={t('home.selectNetworkAria')} onClick={() => setOpen(!open)}>
{open ? (
<RemoveIcon fontSize="medium" sx={{ color: theme.palette.layout.main, fontSize: '18px' }} />
) : (
<AddIcon fontSize="medium" sx={{ color: theme.palette.layout.main, fontSize: '18px' }} />
)}
</IconButton>
</StyledBox>
</StyledNetworkSelector>
<StyledNetworkSelector
defaultValue={network}
handleChange={setNetwork}
options={options}
placement="top-start"
root={NetworkSelectorButton}
Option={SelectOption}
listbox={StyledListbox}
/>
)
}
2 changes: 1 addition & 1 deletion src/app/pages/HomePage/Graph/ParaTimeSelector/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ const ParaTimeSelectorCmp: FC<ParaTimeSelectorProps> = ({
)}
</ParaTimeSelectorGlobe>
{step === ParaTimeSelectorStep.Explore && (
<NetworkSelector network={network} setNetwork={setNetwork} />
<NetworkSelector network={network} setNetwork={network => setNetwork(network ?? Network.mainnet)} />
)}
</ParaTimeSelectorGlow>
{activeMobileGraphTooltip.current && (
Expand Down

0 comments on commit 6fbeff0

Please sign in to comment.