frontPanel/src/pages/PersonalizationAI/AgeInputWithSelect.tsx
Nastya f0a977031d
All checks were successful
Deploy / CreateImage (push) Successful in 5m0s
Deploy / DeployService (push) Successful in 27s
дизабл кнопки при некорректных условиях
2025-06-03 21:50:07 +03:00

181 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useRef } from 'react';
import {
Box,
InputBase,
IconButton,
Menu,
MenuItem,
Paper,
Popper,
Grow,
ClickAwayListener,
MenuList,
useTheme,
FormHelperText
} from '@mui/material';
import ArrowDownIcon from "@/assets/icons/ArrowDownIcon";
interface AgeInputWithSelectProps {
value: string;
onChange: (value: string) => void;
onErrorChange?: (isError: boolean) => void;
}
const AgeInputWithSelect = ({ value, onChange, onErrorChange }: AgeInputWithSelectProps) => {
const theme = useTheme();
const [open, setOpen] = useState(false);
const anchorRef = useRef<HTMLDivElement>(null);
const [errorType, setErrorType] = useState<'format' | 'range' | false>(false);
// Валидация: только число или диапазон число-число, и диапазон 0-150
const validate = (val: string) => {
if (!val) return false;
// Число (только положительное)
if (/^-?\d+$/.test(val)) {
const num = Number(val);
if (num < 0 || num > 150) return 'range';
return false;
}
// Диапазон (только положительные числа)
const rangeMatch = val.match(/^(-?\d+)-(-?\d+)$/);
if (rangeMatch) {
const left = Number(rangeMatch[1]);
const right = Number(rangeMatch[2]);
if (left < 0 || left > 150 || right < 0 || right > 150 || left > right) return 'range';
return false;
}
return 'format';
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const filtered = e.target.value.replace(/[^\d-]/g, '');
onChange(filtered);
const err = validate(filtered);
setErrorType(err);
if (onErrorChange) onErrorChange(!!err);
};
const handleInputBlur = (e: React.FocusEvent<HTMLInputElement>) => {
const trimmed = e.target.value.replace(/\s+/g, '');
onChange(trimmed);
const err = validate(trimmed);
setErrorType(err);
if (onErrorChange) onErrorChange(!!err);
};
const handleSelectItemClick = (selectedValue: string) => {
onChange(selectedValue);
setErrorType(false);
setOpen(false);
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event: Event) => {
if (anchorRef.current && anchorRef.current.contains(event.target as HTMLElement)) {
return;
}
setOpen(false);
};
return (
<Box
ref={anchorRef}
sx={{
position: 'relative',
mt: "17px",
height: "48px",
maxWidth: "420px",
width: "100%",
borderRadius: "8px",
border: "1px solid #9A9AAF",
'&:hover': {
borderColor: '#B0B0B0',
},
'&:focus-within': {
borderColor: '#7E2AEA',
}
}}
>
<InputBase
value={value}
onChange={handleInputChange}
onBlur={handleInputBlur}
fullWidth
placeholder="Введите возраст"
inputProps={{ inputMode: 'numeric', pattern: '[0-9-]*' }}
sx={{
height: "100%",
padding: "10px 20px",
'& input': {
height: "100%",
width: "100%",
}
}}
/>
{errorType === 'format' && (
<FormHelperText error sx={{ position: 'absolute', left: 0, top: '100%', mt: '2px', ml: '10px' }}>
можно только число или диапазон
</FormHelperText>
)}
{errorType === 'range' && (
<FormHelperText error sx={{ position: 'absolute', left: 0, top: '100%', mt: '2px', ml: '10px' }}>
таких возрастов нет
</FormHelperText>
)}
<IconButton
onClick={handleToggle}
sx={{
position: 'absolute',
right: 0,
top: '50%',
transform: `translateY(-50%) rotate(${open ? 180 : 0}deg)`,
cursor: 'pointer',
color: theme.palette.brightPurple.main,
display: 'flex',
alignItems: 'center',
transition: 'transform 0.2s',
padding: '8px'
}}
>
<ArrowDownIcon style={{ width: "18px", height: "18px" }} />
</IconButton>
<Popper
open={open}
anchorEl={anchorRef.current}
role={undefined}
placement="bottom-end"
transition
disablePortal
sx={{ zIndex: 1300 }}
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper elevation={8}>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem={open} id="menu-list-grow">
<MenuItem onClick={() => handleSelectItemClick('')}>Выберите возраст</MenuItem>
<MenuItem onClick={() => handleSelectItemClick('18-24')}>18-24</MenuItem>
<MenuItem onClick={() => handleSelectItemClick('25-34')}>25-34</MenuItem>
<MenuItem onClick={() => handleSelectItemClick('35-44')}>35-44</MenuItem>
<MenuItem onClick={() => handleSelectItemClick('45-54')}>45-54</MenuItem>
<MenuItem onClick={() => handleSelectItemClick('55+')}>55+</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</Box>
);
};
export default AgeInputWithSelect;