mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
AddressTxsFilter refactoring
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import type { CollectionItem } from '@chakra-ui/react';
|
||||
import { Select as ChakraSelect, Portal } from '@chakra-ui/react';
|
||||
import { Select as ChakraSelect, Portal, useSelect, useSelectContext } from '@chakra-ui/react';
|
||||
import * as React from 'react';
|
||||
|
||||
import IconSvg from 'ui/shared/IconSvg';
|
||||
@@ -9,6 +9,11 @@ import IconSvg from 'ui/shared/IconSvg';
|
||||
import { CloseButton } from './close-button';
|
||||
import { Skeleton } from './skeleton';
|
||||
|
||||
export interface SelectOption<Value extends string = string> {
|
||||
value: Value;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface SelectControlProps extends ChakraSelect.ControlProps {
|
||||
clearable?: boolean;
|
||||
noIndicator?: boolean;
|
||||
@@ -20,11 +25,16 @@ export const SelectControl = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
SelectControlProps
|
||||
>(function SelectControl(props, ref) {
|
||||
const { children, clearable, noIndicator, triggerProps, loading, ...rest } = props;
|
||||
// NOTE: here defaultValue means the "default" option of the select, not its initial value
|
||||
const { children, clearable, noIndicator, triggerProps, loading, defaultValue, ...rest } = props;
|
||||
|
||||
const context = useSelectContext();
|
||||
const isDefaultValue = Array.isArray(defaultValue) ? context.value.every((item) => defaultValue.includes(item)) : context.value === defaultValue;
|
||||
|
||||
return (
|
||||
<Skeleton loading={ loading } asChild>
|
||||
<ChakraSelect.Control { ...rest } className="group">
|
||||
<ChakraSelect.Trigger ref={ ref } { ...triggerProps }>{ children }</ChakraSelect.Trigger>
|
||||
<ChakraSelect.Trigger ref={ ref } { ...triggerProps } data-default-value={ isDefaultValue }>{ children }</ChakraSelect.Trigger>
|
||||
{ (!noIndicator || clearable) && (
|
||||
<ChakraSelect.IndicatorGroup>
|
||||
{ clearable && <SelectClearTrigger/> }
|
||||
|
||||
@@ -243,12 +243,12 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
|
||||
},
|
||||
filter: {
|
||||
fg: {
|
||||
DEFAULT: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
|
||||
selected: { value: { _light: '{colors.blue.600}', _dark: '{colors.gray.50}' } },
|
||||
DEFAULT: { value: '{colors.button.dropdown.fg}' },
|
||||
selected: { value: '{colors.button.dropdown.fg.selected}' },
|
||||
},
|
||||
border: {
|
||||
DEFAULT: { value: { _light: '{colors.gray.200}', _dark: '{colors.gray.600}' } },
|
||||
selected: { value: { _light: '{colors.blue.50}', _dark: '{colors.gray.600}' } },
|
||||
DEFAULT: { value: '{colors.button.dropdown.border}' },
|
||||
selected: { value: '{colors.button.dropdown.border.selected}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@ export const recipe = defineSlotRecipe({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1.5',
|
||||
width: 'full',
|
||||
width: 'fit-content',
|
||||
},
|
||||
trigger: {
|
||||
display: 'flex',
|
||||
@@ -156,12 +156,14 @@ export const recipe = defineSlotRecipe({
|
||||
bgColor: 'select.trigger.filter.border.selected',
|
||||
borderColor: 'select.trigger.filter.border.selected',
|
||||
_expanded: {
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'link.primary.hover',
|
||||
color: 'select.trigger.filter.fg.selected',
|
||||
bgColor: 'select.trigger.filter.border.selected',
|
||||
borderColor: 'select.trigger.filter.border.selected',
|
||||
},
|
||||
_hover: {
|
||||
color: 'select.trigger.filter.fg.selected',
|
||||
borderColor: 'select.trigger.filter.border.selected',
|
||||
bgColor: 'select.trigger.filter.border.selected',
|
||||
},
|
||||
_focusVisible: {
|
||||
borderColor: 'link.primary.hover',
|
||||
@@ -174,6 +176,23 @@ export const recipe = defineSlotRecipe({
|
||||
_hover: {
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'link.primary.hover',
|
||||
bgColor: 'transparent',
|
||||
},
|
||||
},
|
||||
// If the default value is selected, the styles should be the same as when the placeholder is shown
|
||||
'&[data-default-value="true"]': {
|
||||
color: 'select.trigger.filter.fg',
|
||||
borderColor: 'select.trigger.filter.border',
|
||||
bgColor: 'transparent',
|
||||
_hover: {
|
||||
color: 'link.primary.hover',
|
||||
borderColor: 'link.primary.hover',
|
||||
bgColor: 'transparent',
|
||||
},
|
||||
_expanded: {
|
||||
color: 'inherit',
|
||||
borderColor: 'inherit',
|
||||
bgColor: 'inherit',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+10
-10
@@ -66,7 +66,8 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender =
|
||||
const isMobile = useIsMobile();
|
||||
const currentAddress = getQueryParamString(router.query.hash);
|
||||
|
||||
const [ filterValue, setFilterValue ] = React.useState<AddressFromToFilter>(getFilterValue(router.query.filter));
|
||||
const initialFilterValue = getFilterValue(router.query.filter);
|
||||
const [ filterValue, setFilterValue ] = React.useState<AddressFromToFilter>(initialFilterValue);
|
||||
|
||||
const addressTxsQuery = useQueryWithPages({
|
||||
resourceName: 'address_txs',
|
||||
@@ -165,15 +166,14 @@ const AddressTxs = ({ scrollRef, overloadCount = OVERLOAD_COUNT, shouldRender =
|
||||
return null;
|
||||
}
|
||||
|
||||
// const filter = (
|
||||
// <AddressTxsFilter
|
||||
// defaultFilter={ filterValue }
|
||||
// onFilterChange={ handleFilterChange }
|
||||
// hasActiveFilter={ Boolean(filterValue) }
|
||||
// isLoading={ addressTxsQuery.pagination.isLoading }
|
||||
// />
|
||||
// );
|
||||
const filter = null;
|
||||
const filter = (
|
||||
<AddressTxsFilter
|
||||
initialValue={ initialFilterValue }
|
||||
onFilterChange={ handleFilterChange }
|
||||
hasActiveFilter={ Boolean(filterValue) }
|
||||
isLoading={ addressTxsQuery.pagination.isLoading }
|
||||
/>
|
||||
);
|
||||
|
||||
const csvExportLink = (
|
||||
<AddressCsvExportLink
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createListCollection } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { AddressFromToFilter } from 'types/api/address';
|
||||
@@ -10,25 +11,26 @@ const OPTIONS = [
|
||||
{ value: 'from', label: 'Outgoing transactions' },
|
||||
{ value: 'to', label: 'Incoming transactions' },
|
||||
];
|
||||
const collection = createListCollection({ items: OPTIONS });
|
||||
|
||||
interface Props {
|
||||
hasActiveFilter: boolean;
|
||||
defaultFilter: AddressFromToFilter;
|
||||
initialValue: AddressFromToFilter;
|
||||
onFilterChange: (nextValue: string | Array<string>) => void;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const AddressTxsFilter = ({ onFilterChange, defaultFilter, hasActiveFilter, isLoading }: Props) => {
|
||||
const AddressTxsFilter = ({ onFilterChange, initialValue, hasActiveFilter, isLoading }: Props) => {
|
||||
const isInitialLoading = useIsInitialLoading(isLoading);
|
||||
|
||||
return (
|
||||
<PopoverFilterRadio
|
||||
name="txs_filter"
|
||||
options={ OPTIONS }
|
||||
collection={ collection }
|
||||
onChange={ onFilterChange }
|
||||
hasActiveFilter={ hasActiveFilter }
|
||||
isLoading={ isInitialLoading }
|
||||
defaultValue={ defaultFilter || OPTIONS[0].value }
|
||||
initialValue={ initialValue }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { NextRouter } from 'next/router';
|
||||
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
|
||||
import config from 'configs/app';
|
||||
import getQueryParamString from 'lib/router/getQueryParamString';
|
||||
import removeQueryParam from 'lib/router/removeQueryParam';
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
const feature = config.features.marketplace;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { EnsLookupSorting } from 'types/api/ens';
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
import getNextSortValueShared from 'ui/shared/sort/getNextSortValue';
|
||||
|
||||
|
||||
@@ -39,8 +39,10 @@ const RawInputData = ({ hex, rightSlot: rightSlotProp, defaultDataType = 'Hex',
|
||||
variant="outline"
|
||||
defaultValue={ [ defaultDataType ] }
|
||||
onValueChange={ handleValueChange }
|
||||
w="100px"
|
||||
mr="auto"
|
||||
>
|
||||
<SelectControl w="100px" mr="auto" loading={ isLoading }>
|
||||
<SelectControl loading={ isLoading }>
|
||||
<SelectValueText placeholder="Select framework"/>
|
||||
</SelectControl>
|
||||
<SelectContent>
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { TagProps } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { StatsInterval, StatsIntervalIds } from 'types/client/stats';
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
import Skeleton from 'ui/shared/chakra/Skeleton';
|
||||
import Select from 'ui/shared/select/Select';
|
||||
import TagGroupSelect from 'ui/shared/tagGroupSelect/TagGroupSelect';
|
||||
|
||||
@@ -1,35 +1,51 @@
|
||||
import type { ListCollection } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
import { SelectContent, SelectItem, SelectRoot, SelectControl } from 'toolkit/chakra/select';
|
||||
import FilterButton from 'ui/shared/filters/FilterButton';
|
||||
import Select from 'ui/shared/select/Select';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
options: Array<SelectOption>;
|
||||
collection: ListCollection<SelectOption>;
|
||||
hasActiveFilter: boolean;
|
||||
defaultValue?: string;
|
||||
initialValue?: string;
|
||||
isLoading?: boolean;
|
||||
onChange: (nextValue: string) => void;
|
||||
}
|
||||
|
||||
const PopoverFilterRadio = ({ name, hasActiveFilter, options, isLoading, onChange, defaultValue }: Props) => {
|
||||
const PopoverFilterRadio = ({ name, hasActiveFilter, collection, isLoading, onChange, initialValue }: Props) => {
|
||||
|
||||
const handleValueChange = React.useCallback(({ value }: { value: Array<string> }) => {
|
||||
onChange(value[0]);
|
||||
}, [ onChange ]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
options={ options }
|
||||
<SelectRoot
|
||||
name={ name }
|
||||
defaultValue={ defaultValue }
|
||||
onChange={ onChange }
|
||||
collection={ collection }
|
||||
defaultValue={ initialValue ? [ initialValue ] : [ collection.items[0].value ] }
|
||||
onValueChange={ handleValueChange }
|
||||
variant="filter"
|
||||
>
|
||||
{ ({ isOpen, onToggle }) => (
|
||||
<SelectControl
|
||||
triggerProps={{ asChild: true, pr: 2 }}
|
||||
noIndicator
|
||||
defaultValue={ [ collection.items[0].value ] }
|
||||
>
|
||||
<FilterButton
|
||||
isActive={ isOpen }
|
||||
onClick={ onToggle }
|
||||
appliedFiltersNum={ hasActiveFilter ? 1 : 0 }
|
||||
isLoading={ isLoading }
|
||||
/>
|
||||
) }
|
||||
</Select>
|
||||
</SelectControl>
|
||||
<SelectContent>
|
||||
{ collection.items.map((item) => (
|
||||
<SelectItem item={ item } key={ item.value }>
|
||||
{ item.label }
|
||||
</SelectItem>
|
||||
)) }
|
||||
</SelectContent>
|
||||
</SelectRoot>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface SelectOption<Value extends string = string> {
|
||||
value: Value;
|
||||
label: string;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
|
||||
import type { Query } from 'nextjs-routes';
|
||||
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
export default function getSortValueFromQuery<SortValue extends string>(query: Query, sortOptions: Array<SelectOption<SortValue>>) {
|
||||
if (!query.sort || !query.order) {
|
||||
return undefined;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { noop } from 'es-toolkit';
|
||||
import React from 'react';
|
||||
|
||||
import { SelectContent, SelectItem, SelectRoot, SelectControl, SelectValueText } from 'toolkit/chakra/select';
|
||||
import PopoverFilterRadio from 'ui/shared/filters/PopoverFilterRadio';
|
||||
import Sort from 'ui/shared/sort/Sort';
|
||||
import TokenTransferFilter from 'ui/shared/TokenTransfer/TokenTransferFilter';
|
||||
import { SORT_OPTIONS } from 'ui/txs/useTxsSort';
|
||||
@@ -22,6 +23,11 @@ const txSortingOptions = createListCollection({
|
||||
});
|
||||
|
||||
const SelectShowcase = () => {
|
||||
const [ hasActiveFilter, setHasActiveFilter ] = React.useState(false);
|
||||
|
||||
const handleFilterChange = React.useCallback((nextValue: string) => {
|
||||
setHasActiveFilter(nextValue !== txSortingOptions.items[0].value);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Container value="select">
|
||||
@@ -68,12 +74,14 @@ const SelectShowcase = () => {
|
||||
name="transactions_sorting"
|
||||
defaultValue={ [ txSortingOptions.items[0].value ] }
|
||||
collection={ txSortingOptions }
|
||||
w="fit-content"
|
||||
/>
|
||||
<Sort
|
||||
name="transactions_sorting"
|
||||
defaultValue={ [ txSortingOptions.items[0].value ] }
|
||||
collection={ txSortingOptions }
|
||||
isLoading
|
||||
w="fit-content"
|
||||
/>
|
||||
</Sample>
|
||||
</SamplesStack>
|
||||
@@ -86,6 +94,25 @@ const SelectShowcase = () => {
|
||||
<TokenTransferFilter defaultTypeFilters={ [ ] } onTypeFilterChange={ noop } appliedFiltersNum={ 2 } isLoading/>
|
||||
</Sample>
|
||||
</SamplesStack>
|
||||
|
||||
<SectionSubHeader>Radio filter</SectionSubHeader>
|
||||
<SamplesStack>
|
||||
<Sample flexWrap="nowrap">
|
||||
<PopoverFilterRadio
|
||||
name="transactions_sorting"
|
||||
collection={ txSortingOptions }
|
||||
hasActiveFilter={ hasActiveFilter }
|
||||
onChange={ handleFilterChange }
|
||||
/>
|
||||
<PopoverFilterRadio
|
||||
name="transactions_sorting"
|
||||
collection={ txSortingOptions }
|
||||
hasActiveFilter
|
||||
onChange={ noop }
|
||||
isLoading
|
||||
/>
|
||||
</Sample>
|
||||
</SamplesStack>
|
||||
</Section>
|
||||
</Container>
|
||||
);
|
||||
|
||||
+1
-1
@@ -1,10 +1,10 @@
|
||||
import type { TokenType } from 'types/api/token';
|
||||
import type { TokensSortingValue } from 'types/api/tokens';
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
|
||||
import config from 'configs/app';
|
||||
import getFilterValuesFromQuery from 'lib/getFilterValuesFromQuery';
|
||||
import { TOKEN_TYPE_IDS } from 'lib/token/tokenTypes';
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
export const SORT_OPTIONS: Array<SelectOption<TokensSortingValue>> = [
|
||||
{ label: 'Default', value: undefined },
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { UseQueryResult } from '@tanstack/react-query';
|
||||
import React from 'react';
|
||||
|
||||
import type { TransactionsSortingValue, TxsResponse } from 'types/api/transaction';
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
|
||||
import type { ResourceError } from 'lib/api/resources';
|
||||
import * as cookies from 'lib/cookies';
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
import sortTxs from './sortTxs';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {
|
||||
ValidatorsBlackfortSortingValue,
|
||||
ValidatorsBlackfortSortingField,
|
||||
} from 'types/api/validators';
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
export const VALIDATORS_BLACKFORT_SORT_OPTIONS: Array<SelectOption<ValidatorsBlackfortSortingValue>> = [
|
||||
{ label: 'Default', value: undefined },
|
||||
|
||||
@@ -2,7 +2,7 @@ import type {
|
||||
ValidatorsStabilitySortingValue,
|
||||
ValidatorsStabilitySortingField,
|
||||
} from 'types/api/validators';
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
export const VALIDATORS_STABILITY_SORT_OPTIONS: Array<SelectOption<ValidatorsStabilitySortingValue>> = [
|
||||
{ label: 'Default', value: undefined },
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { VerifiedContractsSortingValue, VerifiedContractsSortingField } from 'types/api/verifiedContracts';
|
||||
import type { SelectOption } from 'ui/shared/select/types';
|
||||
import type { SelectOption } from 'toolkit/chakra/select';
|
||||
|
||||
export const SORT_OPTIONS: Array<SelectOption<VerifiedContractsSortingValue>> = [
|
||||
{ label: 'Default', value: undefined },
|
||||
|
||||
Reference in New Issue
Block a user