frontPanel/src/pages/PersonalizationAI/AgeInputWithSelect.tsx

181 lines
5.4 KiB
TypeScript
Raw Normal View History

2025-06-03 17:27:14 +00:00
import React, { useState, useRef } from 'react';
import {
Box,
InputBase,
IconButton,
Menu,
MenuItem,
Paper,
Popper,
Grow,
ClickAwayListener,
MenuList,
useTheme,
FormHelperText
2025-06-03 17:27:14 +00:00
} from '@mui/material';
import ArrowDownIcon from "@/assets/icons/ArrowDownIcon";
interface AgeInputWithSelectProps {
value: string;
onChange: (value: string) => void;
onErrorChange?: (isError: boolean) => void;
2025-06-03 17:27:14 +00:00
}
const AgeInputWithSelect = ({ value, onChange, onErrorChange }: AgeInputWithSelectProps) => {
2025-06-03 17:27:14 +00:00
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';
};
2025-06-03 17:27:14 +00:00
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);
2025-06-03 17:27:14 +00:00
};
const handleSelectItemClick = (selectedValue: string) => {
onChange(selectedValue);
setErrorType(false);
2025-06-03 17:27:14 +00:00
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}
2025-06-03 17:27:14 +00:00
fullWidth
placeholder="Введите возраст"
inputProps={{ inputMode: 'numeric', pattern: '[0-9-]*' }}
2025-06-03 17:27:14 +00:00
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>
)}
2025-06-03 17:27:14 +00:00
<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;