Commit 1de38065 authored by Max Alekseenko's avatar Max Alekseenko

replace old sort component

parent e2a32ee8
import {
Box,
useColorModeValue,
Button,
Skeleton,
} from '@chakra-ui/react';
import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile';
import IconSvg from 'ui/shared/IconSvg';
import SortButtonMobile from 'ui/shared/sort/SortButton';
type ButtonProps = {
isMenuOpen: boolean;
onClick: () => void;
isLoading?: boolean;
children: React.ReactNode;
};
const SortButton = ({ children, isMenuOpen, onClick, isLoading }: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
const isMobile = useIsMobile();
const primaryColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
const secondaryColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
return (
<Skeleton isLoaded={ !isLoading }>
{ isMobile ? (
<SortButtonMobile ref={ ref } isActive={ isMenuOpen } onClick={ onClick }/>
) : (
<Button
ref={ ref }
size="sm"
variant="outline"
onClick={ onClick }
color={ primaryColor }
fontWeight="600"
borderColor="transparent"
px={ 2 }
data-selected={ isMenuOpen }
>
<Box
as={ isMenuOpen ? 'div' : 'span' }
color={ isMenuOpen ? 'inherit' : secondaryColor }
fontWeight="400"
mr={ 1 }
transition={ isMenuOpen ? 'none' : 'inherit' }
>Sort by</Box>
{ children }
<IconSvg
name="arrows/east-mini"
boxSize={ 5 }
ml={ 1 }
transform={ isMenuOpen ? 'rotate(90deg)' : 'rotate(-90deg)' }
/>
</Button>
) }
</Skeleton>
);
};
export default React.forwardRef(SortButton);
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverBody,
useDisclosure,
useRadioGroup,
} from '@chakra-ui/react';
import React from 'react';
import SortButton from './Button';
import Option from './Option';
import type { TOption } from './Option';
interface Props {
name: string;
options: Array<TOption>;
defaultValue?: string;
isLoading?: boolean;
onChange: (nextValue: 'default' | 'security_score') => void;
}
const SortMenu = ({ name, options, isLoading, onChange, defaultValue }: Props) => {
const { isOpen, onToggle, onClose } = useDisclosure();
const handleChange = (nextValue: 'default' | 'security_score') => {
onChange(nextValue);
onClose();
};
const { value, getRootProps, getRadioProps } = useRadioGroup({
name,
defaultValue,
onChange: handleChange,
});
const root = getRootProps();
return (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
<SortButton isMenuOpen={ isOpen } isLoading={ isLoading } onClick={ onToggle }>
{ options.find(option => option.value === value)?.label }
</SortButton>
</PopoverTrigger>
<PopoverContent w="fit-content" minW="165px">
<PopoverBody { ...root } py={ 2 } px={ 0 } display="flex" flexDir="column">
{ options.map((option) => {
const radio = getRadioProps({ value: option.value });
return (
<Option key={ option.value } { ...radio }>
{ option.label }
</Option>
);
}) }
</PopoverBody>
</PopoverContent>
</Popover>
);
};
export default React.memo(SortMenu);
...@@ -11,6 +11,7 @@ import useFetch from 'lib/hooks/useFetch'; ...@@ -11,6 +11,7 @@ import useFetch from 'lib/hooks/useFetch';
import { MARKETPLACE_APP } from 'stubs/marketplace'; import { MARKETPLACE_APP } from 'stubs/marketplace';
import useSecurityReports from './useSecurityReports'; import useSecurityReports from './useSecurityReports';
import type { SortValue } from './utils';
const feature = config.features.marketplace; const feature = config.features.marketplace;
...@@ -88,7 +89,7 @@ export default function useMarketplaceApps( ...@@ -88,7 +89,7 @@ export default function useMarketplaceApps(
enabled: feature.isEnabled && Boolean(snapshotFavoriteApps), enabled: feature.isEnabled && Boolean(snapshotFavoriteApps),
}); });
const [ sorting, setSorting ] = React.useState<'default' | 'security_score'>('default'); const [ sorting, setSorting ] = React.useState<SortValue>();
const appsWithSecurityReports = React.useMemo(() => const appsWithSecurityReports = React.useMemo(() =>
data?.map((app) => ({ ...app, securityReport: securityReports?.[app.id] })), data?.map((app) => ({ ...app, securityReport: securityReports?.[app.id] })),
......
...@@ -2,6 +2,14 @@ import type { NextRouter } from 'next/router'; ...@@ -2,6 +2,14 @@ import type { NextRouter } from 'next/router';
import getQueryParamString from 'lib/router/getQueryParamString'; import getQueryParamString from 'lib/router/getQueryParamString';
import removeQueryParam from 'lib/router/removeQueryParam'; import removeQueryParam from 'lib/router/removeQueryParam';
import type { TOption } from 'ui/shared/sort/Option';
export type SortValue = 'security_score';
export const SORT_OPTIONS: Array<TOption<SortValue>> = [
{ title: 'Default', id: undefined },
{ title: 'Security score', id: 'security_score' },
];
export function getAppUrl(url: string | undefined, router: NextRouter) { export function getAppUrl(url: string | undefined, router: NextRouter) {
if (!url) { if (!url) {
......
...@@ -135,9 +135,10 @@ const NameDomainsActionBar = ({ ...@@ -135,9 +135,10 @@ const NameDomainsActionBar = ({
const sortButton = ( const sortButton = (
<Sort <Sort
name="name_domains_sorting"
defaultValue={ sort }
options={ SORT_OPTIONS } options={ SORT_OPTIONS }
sort={ sort } onChange={ onSortChange }
setSort={ onSortChange }
isLoading={ isInitialLoading } isLoading={ isInitialLoading }
/> />
); );
......
import type { EnsLookupSorting } from 'types/api/ens'; import type { EnsLookupSorting } from 'types/api/ens';
import getNextSortValueShared from 'ui/shared/sort/getNextSortValue'; import getNextSortValueShared from 'ui/shared/sort/getNextSortValue';
import type { Option } from 'ui/shared/sort/Sort'; import type { TOption } from 'ui/shared/sort/Option';
export type SortField = EnsLookupSorting['sort']; export type SortField = EnsLookupSorting['sort'];
export type Sort = `${ EnsLookupSorting['sort'] }-${ EnsLookupSorting['order'] }`; export type Sort = `${ EnsLookupSorting['sort'] }-${ EnsLookupSorting['order'] }`;
export const SORT_OPTIONS: Array<Option<Sort>> = [ export const SORT_OPTIONS: Array<TOption<Sort>> = [
{ title: 'Default', id: undefined }, { title: 'Default', id: undefined },
{ title: 'Registered on descending', id: 'registration_date-DESC' }, { title: 'Registered on descending', id: 'registration_date-DESC' },
{ title: 'Registered on ascending', id: 'registration_date-ASC' }, { title: 'Registered on ascending', id: 'registration_date-ASC' },
......
...@@ -13,12 +13,13 @@ import ContractListModal from 'ui/marketplace/ContractListModal'; ...@@ -13,12 +13,13 @@ import ContractListModal from 'ui/marketplace/ContractListModal';
import MarketplaceAppModal from 'ui/marketplace/MarketplaceAppModal'; import MarketplaceAppModal from 'ui/marketplace/MarketplaceAppModal';
import MarketplaceDisclaimerModal from 'ui/marketplace/MarketplaceDisclaimerModal'; import MarketplaceDisclaimerModal from 'ui/marketplace/MarketplaceDisclaimerModal';
import MarketplaceList from 'ui/marketplace/MarketplaceList'; import MarketplaceList from 'ui/marketplace/MarketplaceList';
import SortMenu from 'ui/marketplace/SortMenu/Menu'; import { SORT_OPTIONS } from 'ui/marketplace/utils';
import FilterInput from 'ui/shared/filters/FilterInput'; import FilterInput from 'ui/shared/filters/FilterInput';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import type { IconName } from 'ui/shared/IconSvg'; import type { IconName } from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/links/LinkExternal'; import LinkExternal from 'ui/shared/links/LinkExternal';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Sort from 'ui/shared/sort/Sort';
import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll'; import TabsWithScroll from 'ui/shared/Tabs/TabsWithScroll';
import useMarketplace from '../marketplace/useMarketplace'; import useMarketplace from '../marketplace/useMarketplace';
...@@ -186,13 +187,9 @@ const Marketplace = () => { ...@@ -186,13 +187,9 @@ const Marketplace = () => {
<Flex mb={{ base: 4, lg: 6 }} gap={{ base: 2, lg: 3 }}> <Flex mb={{ base: 4, lg: 6 }} gap={{ base: 2, lg: 3 }}>
{ feature.securityReportsUrl && ( { feature.securityReportsUrl && (
<SortMenu <Sort
name="dapps_sorting" name="dapps_sorting"
defaultValue="default" options={ SORT_OPTIONS }
options={ [
{ value: 'default', label: 'Default' },
{ value: 'security_score', label: 'Security score' },
] }
onChange={ setSorting } onChange={ setSorting }
isLoading={ isPlaceholderData } isLoading={ isPlaceholderData }
/> />
......
...@@ -97,9 +97,10 @@ const Validators = () => { ...@@ -97,9 +97,10 @@ const Validators = () => {
const sortButton = ( const sortButton = (
<Sort <Sort
name="validators_sorting"
defaultValue={ sort }
options={ SORT_OPTIONS } options={ SORT_OPTIONS }
sort={ sort } onChange={ handleSortChange }
setSort={ handleSortChange }
/> />
); );
......
...@@ -97,9 +97,11 @@ const VerifiedContracts = () => { ...@@ -97,9 +97,11 @@ const VerifiedContracts = () => {
const sortButton = ( const sortButton = (
<Sort <Sort
name="verified_contracts_sorting"
defaultValue={ sort }
options={ SORT_OPTIONS } options={ SORT_OPTIONS }
sort={ sort } onChange={ handleSortChange }
setSort={ handleSortChange } isLoading={ isPlaceholderData }
/> />
); );
......
import {
Box,
useColorModeValue,
Button,
Skeleton,
chakra,
} from '@chakra-ui/react';
import React from 'react';
import IconSvg from 'ui/shared/IconSvg';
type ButtonProps = {
isMenuOpen: boolean;
onClick: () => void;
isLoading?: boolean;
children: React.ReactNode;
className?: string;
};
const ButtonDesktop = ({ children, isMenuOpen, onClick, isLoading, className }: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
const primaryColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
const secondaryColor = useColorModeValue('blackAlpha.600', 'whiteAlpha.600');
return (
<Skeleton isLoaded={ !isLoading }>
<Button
className={ className }
ref={ ref }
size="sm"
variant="outline"
onClick={ onClick }
color={ primaryColor }
fontWeight="600"
borderColor="transparent"
px={ 2 }
data-selected={ isMenuOpen }
>
<Box
as={ isMenuOpen ? 'div' : 'span' }
color={ isMenuOpen ? 'inherit' : secondaryColor }
fontWeight="400"
mr={ 1 }
transition={ isMenuOpen ? 'none' : 'inherit' }
>Sort by</Box>
{ children }
<IconSvg
name="arrows/east-mini"
boxSize={ 5 }
ml={ 1 }
transform={ isMenuOpen ? 'rotate(90deg)' : 'rotate(-90deg)' }
/>
</Button>
</Skeleton>
);
};
export default chakra(React.forwardRef(ButtonDesktop));
...@@ -10,7 +10,7 @@ type Props = { ...@@ -10,7 +10,7 @@ type Props = {
isLoading?: boolean; isLoading?: boolean;
} }
const SortButton = ({ onClick, isActive, className, isLoading }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => { const ButtonMobile = ({ onClick, isActive, className, isLoading }: Props, ref: React.ForwardedRef<HTMLButtonElement>) => {
if (isLoading) { if (isLoading) {
return <Skeleton className={ className } w="36px" h="32px" borderRadius="base"/>; return <Skeleton className={ className } w="36px" h="32px" borderRadius="base"/>;
} }
...@@ -32,4 +32,4 @@ const SortButton = ({ onClick, isActive, className, isLoading }: Props, ref: Rea ...@@ -32,4 +32,4 @@ const SortButton = ({ onClick, isActive, className, isLoading }: Props, ref: Rea
); );
}; };
export default chakra(React.forwardRef(SortButton)); export default chakra(React.forwardRef(ButtonMobile));
...@@ -8,9 +8,9 @@ import React from 'react'; ...@@ -8,9 +8,9 @@ import React from 'react';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
export interface TOption { export interface TOption<Sort extends string> {
value: string; id: Sort | undefined;
label: string; title: string;
} }
type OptionProps = ReturnType<ReturnType<typeof useRadioGroup>['getRadioProps']>; type OptionProps = ReturnType<ReturnType<typeof useRadioGroup>['getRadioProps']>;
......
import { import {
chakra, Popover,
Menu, PopoverTrigger,
MenuButton, PopoverContent,
MenuList, PopoverBody,
MenuOptionGroup,
MenuItemOption,
useDisclosure, useDisclosure,
useRadioGroup,
chakra,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import SortButton from './SortButton'; import useIsMobile from 'lib/hooks/useIsMobile';
export interface Option<Sort extends string> { import SortButtonDesktop from './ButtonDesktop';
title: string; import SortButtonMobile from './ButtonMobile';
id: Sort | undefined; import Option from './Option';
} import type { TOption } from './Option';
interface Props<Sort extends string> { interface Props<Sort extends string> {
options: Array<Option<Sort>>; name: string;
sort: Sort | undefined; options: Array<TOption<Sort>>;
setSort: (value: Sort | undefined) => void; defaultValue?: Sort;
isLoading?: boolean; isLoading?: boolean;
onChange: (value: Sort | undefined) => void;
} }
const Sort = <Sort extends string>({ sort, setSort, options, isLoading }: Props<Sort>) => { const Sort = <Sort extends string>({ name, options, isLoading, onChange, defaultValue }: Props<Sort>) => {
const { isOpen, onToggle } = useDisclosure(); const isMobile = useIsMobile(false);
const { isOpen, onToggle, onClose } = useDisclosure();
const handleChange = (value: Sort) => {
onChange(value);
onClose();
};
const { value, getRootProps, getRadioProps } = useRadioGroup({
name,
defaultValue,
onChange: handleChange,
});
const setSortingFromMenu = React.useCallback((val: string | Array<string>) => { const root = getRootProps();
const value = val as Sort | Array<Sort>;
setSort(Array.isArray(value) ? value[0] : value);
}, [ setSort ]);
return ( return (
<Menu> <Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<MenuButton as="div"> <PopoverTrigger>
<SortButton { isMobile ? (
isActive={ isOpen || Boolean(sort) } <SortButtonMobile isActive={ isOpen || Boolean(value) } onClick={ onToggle } isLoading={ isLoading }/>
onClick={ onToggle } ) : (
isLoading={ isLoading } <SortButtonDesktop isMenuOpen={ isOpen } isLoading={ isLoading } onClick={ onToggle }>
/> { options.find((option: TOption<Sort>) => option.id === value || (!option.id && !value))?.title }
</MenuButton> </SortButtonDesktop>
<MenuList minWidth="240px" zIndex="popover"> ) }
<MenuOptionGroup value={ sort } title="Sort by" type="radio" onChange={ setSortingFromMenu }> </PopoverTrigger>
{ options.map((option) => ( <PopoverContent w="fit-content" minW="165px">
<MenuItemOption <PopoverBody { ...root } py={ 2 } px={ 0 } display="flex" flexDir="column">
key={ option.id || 'default' } { options.map((option, index) => {
value={ option.id } const radio = getRadioProps({ value: option.id });
> return (
{ option.title } <Option key={ index } { ...radio } isChecked={ radio.isChecked || (!option.id && !value) }>
</MenuItemOption> { option.title }
)) } </Option>
</MenuOptionGroup> );
</MenuList> }) }
</Menu> </PopoverBody>
</PopoverContent>
</Popover>
); );
}; };
......
import type { Query } from 'nextjs-routes'; import type { Query } from 'nextjs-routes';
import type { Option } from 'ui/shared/sort/Sort'; import type { TOption } from 'ui/shared/sort/Option';
export default function getSortValueFromQuery<SortValue extends string>(query: Query, sortOptions: Array<Option<SortValue>>) { export default function getSortValueFromQuery<SortValue extends string>(query: Query, sortOptions: Array<TOption<SortValue>>) {
if (!query.sort || !query.order) { if (!query.sort || !query.order) {
return undefined; return undefined;
} }
......
...@@ -45,9 +45,10 @@ const TokensActionBar = ({ ...@@ -45,9 +45,10 @@ const TokensActionBar = ({
<HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}> <HStack spacing={ 3 } mb={ 6 } display={{ base: 'flex', lg: 'none' }}>
{ filter } { filter }
<Sort <Sort
name="tokens_sorting"
defaultValue={ sort }
options={ SORT_OPTIONS } options={ SORT_OPTIONS }
setSort={ onSortChange } onChange={ onSortChange }
sort={ sort }
/> />
{ searchInput } { searchInput }
</HStack> </HStack>
......
...@@ -4,9 +4,9 @@ import type { TokensSortingValue } from 'types/api/tokens'; ...@@ -4,9 +4,9 @@ import type { TokensSortingValue } from 'types/api/tokens';
import config from 'configs/app'; import config from 'configs/app';
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery'; import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes'; import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
import type { Option } from 'ui/shared/sort/Sort'; import type { TOption } from 'ui/shared/sort/Option';
export const SORT_OPTIONS: Array<Option<TokensSortingValue>> = [ export const SORT_OPTIONS: Array<TOption<TokensSortingValue>> = [
{ title: 'Default', id: undefined }, { title: 'Default', id: undefined },
{ title: 'Price ascending', id: 'fiat_value-asc' }, { title: 'Price ascending', id: 'fiat_value-asc' },
{ title: 'Price descending', id: 'fiat_value-desc' }, { title: 'Price descending', id: 'fiat_value-desc' },
......
...@@ -30,9 +30,10 @@ const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps ...@@ -30,9 +30,10 @@ const TxsHeaderMobile = ({ filterComponent, sorting, setSorting, paginationProps
<HStack> <HStack>
{ filterComponent } { filterComponent }
<Sort <Sort
name="transactions_sorting"
defaultValue={ sorting }
options={ SORT_OPTIONS } options={ SORT_OPTIONS }
setSort={ setSorting } onChange={ setSorting }
sort={ sorting }
isLoading={ paginationProps.isLoading } isLoading={ paginationProps.isLoading }
/> />
{ /* api is not implemented */ } { /* api is not implemented */ }
......
...@@ -5,11 +5,11 @@ import type { TransactionsSortingValue, TxsResponse } from 'types/api/transactio ...@@ -5,11 +5,11 @@ import type { TransactionsSortingValue, TxsResponse } from 'types/api/transactio
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
import * as cookies from 'lib/cookies'; import * as cookies from 'lib/cookies';
import type { Option } from 'ui/shared/sort/Sort'; import type { TOption } from 'ui/shared/sort/Option';
import sortTxs from './sortTxs'; import sortTxs from './sortTxs';
export const SORT_OPTIONS: Array<Option<TransactionsSortingValue>> = [ export const SORT_OPTIONS: Array<TOption<TransactionsSortingValue>> = [
{ title: 'Default', id: undefined }, { title: 'Default', id: undefined },
{ title: 'Value ascending', id: 'value-asc' }, { title: 'Value ascending', id: 'value-asc' },
{ title: 'Value descending', id: 'value-desc' }, { title: 'Value descending', id: 'value-desc' },
......
import type { ValidatorsSortingValue, ValidatorsSortingField } from 'types/api/validators'; import type { ValidatorsSortingValue, ValidatorsSortingField } from 'types/api/validators';
import type { Option } from 'ui/shared/sort/Sort'; import type { TOption } from 'ui/shared/sort/Option';
export const SORT_OPTIONS: Array<Option<ValidatorsSortingValue>> = [ export const SORT_OPTIONS: Array<TOption<ValidatorsSortingValue>> = [
{ title: 'Default', id: undefined }, { title: 'Default', id: undefined },
{ title: 'Status descending', id: 'state-desc' }, { title: 'Status descending', id: 'state-desc' },
{ title: 'Status ascending', id: 'state-asc' }, { title: 'Status ascending', id: 'state-asc' },
......
import type { VerifiedContractsSortingValue, VerifiedContractsSortingField } from 'types/api/verifiedContracts'; import type { VerifiedContractsSortingValue, VerifiedContractsSortingField } from 'types/api/verifiedContracts';
import type { Option } from 'ui/shared/sort/Sort'; import type { TOption } from 'ui/shared/sort/Option';
export const SORT_OPTIONS: Array<Option<VerifiedContractsSortingValue>> = [ export const SORT_OPTIONS: Array<TOption<VerifiedContractsSortingValue>> = [
{ title: 'Default', id: undefined }, { title: 'Default', id: undefined },
{ title: 'Balance descending', id: 'balance-desc' }, { title: 'Balance descending', id: 'balance-desc' },
{ title: 'Balance ascending', id: 'balance-asc' }, { title: 'Balance ascending', id: 'balance-asc' },
......
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