Makes filtering by multiple payment methods in desktop possible (#1172)

* Make the payments autocomplete header text independant on isFilter prop

* makes filtering by MULTIPLE PAYMENT METHODS possible without great impact in the current experience on DESKTOP only

---------

Co-authored-by: Reckless_Satoshi <90936742+Reckless-Satoshi@users.noreply.github.com>
This commit is contained in:
Renato Peres 2024-03-14 09:07:07 -03:00 committed by GitHub
parent 2b5bc9062e
commit 09f70c493b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 124 additions and 24 deletions

View File

@ -229,9 +229,10 @@ const BookControl = ({
helperText={''} helperText={''}
label={fav.currency === 1000 ? t('DESTINATION') : t('METHOD')} label={fav.currency === 1000 ? t('DESTINATION') : t('METHOD')}
tooltipTitle='' tooltipTitle=''
listHeaderText=''
addNewButtonText='' addNewButtonText=''
isFilter={true} isFilter={true}
multiple={true}
optionsDisplayLimit={1}
/> />
</Grid> </Grid>
) : null} ) : null}

View File

@ -11,6 +11,7 @@ import {
useTheme, useTheme,
type SxProps, type SxProps,
type Theme, type Theme,
Chip,
} from '@mui/material'; } from '@mui/material';
import { fiatMethods, swapMethods, PaymentIcon } from '../PaymentMethods'; import { fiatMethods, swapMethods, PaymentIcon } from '../PaymentMethods';
@ -114,8 +115,8 @@ const InputWrapper = styled('div')(
interface TagProps { interface TagProps {
label: string; label: string;
icon: string; icon?: string;
onDelete: () => void; onDelete?: () => void;
onClick: () => void; onClick: () => void;
} }
@ -124,22 +125,53 @@ const Tag: React.FC<TagProps> = ({ label, icon, onDelete, onClick, ...other }) =
const iconSize = 1.5 * theme.typography.fontSize; const iconSize = 1.5 * theme.typography.fontSize;
return ( return (
<div {...other}> <div {...other}>
<div style={{ position: 'relative', left: '-5px', top: '0.28em' }} onClick={onClick}> {icon ? (
<PaymentIcon width={iconSize} height={iconSize} icon={icon} /> <div style={{ position: 'relative', left: '-5px', top: '0.28em' }} onClick={onClick}>
</div> <PaymentIcon width={iconSize} height={iconSize} icon={icon} />
</div>
) : null}
<span style={{ position: 'relative', left: '2px' }} onClick={onClick}> <span style={{ position: 'relative', left: '2px' }} onClick={onClick}>
{label} {label}
</span> </span>
<CloseIcon onClick={onDelete} /> <CloseIcon className='delete-icon' onClick={onDelete} />
</div> </div>
); );
}; };
const StyledChip = styled(Chip)(
({ theme, sx }) => `
display: flex;
align-items: center;
height: ${String(sx?.height ?? '1.6rem')};
margin: 2px;
line-height: 1.5em;
background-color: ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : '#fafafa'};
border: 1px solid ${theme.palette.mode === 'dark' ? '#303030' : '#e8e8e8'};
border-radius: 2px;
box-sizing: content-box;
padding: 0;
outline: 0;
overflow: hidden;
&:focus {
border-color: ${theme.palette.mode === 'dark' ? '#177ddc' : '#40a9ff'};
background-color: ${theme.palette.mode === 'dark' ? '#003b57' : '#e6f7ff'};
}
& span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 0.928em;
}
`,
);
const StyledTag = styled(Tag)( const StyledTag = styled(Tag)(
({ theme, sx }) => ` ({ theme, sx }) => `
display: flex; display: flex;
align-items: center; align-items: center;
height: ${String(sx?.height ?? '2.1em')}; height: ${String(sx?.height ?? '1.6rem')};
margin: 2px; margin: 2px;
line-height: 1.5em; line-height: 1.5em;
background-color: ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : '#fafafa'}; background-color: ${theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.08)' : '#fafafa'};
@ -244,11 +276,19 @@ interface AutocompletePaymentsProps {
sx: SxProps<Theme>; sx: SxProps<Theme>;
addNewButtonText: string; addNewButtonText: string;
isFilter: boolean; isFilter: boolean;
multiple: number;
optionsDisplayLimit?: number;
listHeaderText: string; listHeaderText: string;
} }
const AutocompletePayments: React.FC<AutocompletePaymentsProps> = (props) => { const AutocompletePayments: React.FC<AutocompletePaymentsProps> = (props) => {
// ** State
const [val, setVal] = useState('');
const [showFilterInput, setShowFilterInput] = useState(false);
// ** Hooks
const { t } = useTranslation(); const { t } = useTranslation();
const filterInputRef = React.useRef<HTMLInputElement>(null);
const { const {
getRootProps, getRootProps,
getInputLabelProps, getInputLabelProps,
@ -258,7 +298,7 @@ const AutocompletePayments: React.FC<AutocompletePaymentsProps> = (props) => {
getOptionProps, getOptionProps,
groupedOptions, groupedOptions,
value, value,
focused = true, popupOpen,
setAnchorEl, setAnchorEl,
} = useAutocomplete({ } = useAutocomplete({
fullWidth: true, fullWidth: true,
@ -266,19 +306,23 @@ const AutocompletePayments: React.FC<AutocompletePaymentsProps> = (props) => {
multiple: true, multiple: true,
value: props.value, value: props.value,
options: props.optionsType === 'fiat' ? fiatMethods : swapMethods, options: props.optionsType === 'fiat' ? fiatMethods : swapMethods,
open: props.isFilter ? showFilterInput : undefined,
getOptionLabel: (option) => option.name, getOptionLabel: (option) => option.name,
onInputChange: (e) => { onInputChange: (e) => {
setVal(e?.target?.value ?? ''); if (e?.target) setVal(e?.target?.value ?? '');
}, },
onChange: (event, value) => { onChange: (event, value) => {
if (props.isFilter) setShowFilterInput(false);
props.onAutocompleteChange(value); props.onAutocompleteChange(value);
}, },
onOpen: () => {
if (props.isFilter) setShowFilterInput(true);
},
onClose: () => { onClose: () => {
setVal(() => ''); setVal(() => '');
}, },
}); });
const [val, setVal] = useState('');
const fewerOptions = groupedOptions.length > 8 ? groupedOptions.slice(0, 8) : groupedOptions; const fewerOptions = groupedOptions.length > 8 ? groupedOptions.slice(0, 8) : groupedOptions;
const theme = useTheme(); const theme = useTheme();
const iconSize = 1.5 * theme.typography.fontSize; const iconSize = 1.5 * theme.typography.fontSize;
@ -293,6 +337,21 @@ const AutocompletePayments: React.FC<AutocompletePaymentsProps> = (props) => {
} }
} }
const tagsToDisplay = value.length
? props.optionsDisplayLimit
? value.slice(0, props.optionsDisplayLimit)
: value
: [];
const qttHiddenTags = props.optionsDisplayLimit ? value.length - props.optionsDisplayLimit : 0;
React.useEffect(() => {
if (showFilterInput && props.isFilter) {
filterInputRef.current?.focus();
document.getElementById('payment-methods')?.focus();
}
}, [showFilterInput]);
return ( return (
<Root> <Root>
<Tooltip <Tooltip
@ -320,31 +379,70 @@ const AutocompletePayments: React.FC<AutocompletePaymentsProps> = (props) => {
<InputWrapper <InputWrapper
ref={setAnchorEl} ref={setAnchorEl}
error={Boolean(props.error)} error={Boolean(props.error)}
className={focused ? 'focused' : ''} className={popupOpen ? 'focused' : ''}
sx={{ sx={{
minHeight: '2.9em', minHeight: '2.9em',
maxHeight: '8.6em', maxHeight: '8.6em',
hoverBorderColor: '#ffffff', hoverBorderColor: '#ffffff',
...props.sx, ...props.sx,
}} }}
onClick={(evt) => {
if (props.isFilter) {
if (evt.target instanceof HTMLElement && !evt.target.matches('.delete-icon')) {
// Check if click is not on delete
setShowFilterInput(!showFilterInput);
}
}
}}
> >
{value.map((option, index) => ( {!showFilterInput || !props.isFilter ? (
<StyledTag <>
key={index} {tagsToDisplay.map((option, index) => (
label={t(option.name)} <StyledTag
icon={option.icon} key={index}
onClick={props.onClick} label={t(option.name)}
sx={{ height: '2.1em', ...(props.tagProps ?? {}) }} icon={option.icon}
{...getTagProps({ index })} onClick={props.onClick}
sx={{ height: '1.6rem', ...(props.tagProps ?? {}) }}
{...getTagProps({ index })}
/>
))}
{qttHiddenTags > 0 ? (
<StyledChip
sx={{ borderRadius: 1 }}
label={`+${qttHiddenTags}`}
sx={{ height: '1.6rem' }}
/>
) : null}
</>
) : null}
{value.length > 0 && !props.multiple ? null : (
<input
ref={filterInputRef}
autoFocus={true}
style={
props.isFilter
? {
position: 'absolute',
backgroundColor: 'transparent',
display: showFilterInput ? 'block' : 'none',
width: '166px',
}
: {}
}
{...getInputProps()}
value={val}
onBlur={() => {
if (props.isFilter) setShowFilterInput(false);
}}
/> />
))} )}
{value.length > 0 && props.isFilter ? null : <input {...getInputProps()} value={val} />}
</InputWrapper> </InputWrapper>
</div> </div>
</Tooltip> </Tooltip>
<Grow in={fewerOptions.length > 0}> <Grow in={fewerOptions.length > 0}>
<Listbox sx={props.listBoxProps?.sx ?? undefined} {...getListboxProps()}> <Listbox sx={props.listBoxProps?.sx ?? undefined} {...getListboxProps()}>
{!props.isFilter ? ( {props.listHeaderText ? (
<div <div
style={{ style={{
position: 'fixed', position: 'fixed',

View File

@ -880,7 +880,8 @@ const MakerForm = ({
)} )}
listHeaderText={t('You can add new methods')} listHeaderText={t('You can add new methods')}
addNewButtonText={t('Add New')} addNewButtonText={t('Add New')}
asFilter={false} isFilter={false}
multiple={true}
value={maker.paymentMethods} value={maker.paymentMethods}
/> />
{maker.badPaymentMethod && ( {maker.badPaymentMethod && (