замена инпут на текстареа

This commit is contained in:
Nastya 2025-10-05 15:41:26 +03:00
parent 426449d9bf
commit 70cc2e4e5b
8 changed files with 299 additions and 219 deletions

@ -1,3 +1,4 @@
1.0.7 _ 2025-10-05 _ замена инпут на текстареа
1.0.6 _ 2025-09-19 _ логика включения таймера
1.0.5 _ 2025-09-18 _ особые условия для вывода картинок
1.0.4 _ 2025-09-14 _ особые условия для вывода картинок

@ -39,22 +39,31 @@ const AnswerItem = memo<AnswerItemProps>(
const isTablet = useMediaQuery(theme.breakpoints.down(790));
const setOwnPlaceholder = (replText: string) => {
updateQuestion(questionId, (question) => {
updateQuestion<QuizQuestionVariant>(questionId, (question) => {
question.content.ownPlaceholder = replText;
});
};
const inputRefCallback = useCallback((element: HTMLInputElement | null) => {
const inputRefCallback = useCallback((element: HTMLInputElement | HTMLTextAreaElement | null) => {
if (element && shouldAutoFocus) {
element.focus();
onFocusHandled?.();
}
}, [shouldAutoFocus, onFocusHandled]);
const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
const handleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
// Shift+Enter — новая строка, ничего не делаем (даём браузеру вставить перенос)
if (event.key === "Enter" && event.shiftKey) {
return;
}
// Enter — добавить новый вариант
if (event.key === "Enter") {
event.preventDefault();
if (disableKeyDown) {
enqueueSnackbar("100 максимальное количество");
} else if (event.code === "Enter" && !largeCheck) {
return;
}
if (onEnterKeyPress) {
onEnterKeyPress();
} else {
@ -92,8 +101,9 @@ const AnswerItem = memo<AnswerItemProps>(
fullWidth
focused={false}
placeholder={isOwn ? "Добавьте текст-подсказку для ввода \"своего ответа\"" : "Добавьте ответ"}
multiline={largeCheck}
onChange={({ target }: ChangeEvent<HTMLInputElement>) => {
multiline
rows={1}
onChange={({ target }: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
if (target.value.length <= 1000) {
isOwn ?
setOwnPlaceholder(target.value || " ")
@ -148,6 +158,9 @@ const AnswerItem = memo<AnswerItemProps>(
},
"& textarea.MuiInputBase-input": {
marginTop: "1px",
resize: "none",
// удерживаем стартовую высоту визуально как у однострочного
lineHeight: "21px",
},
"& .MuiOutlinedInput-notchedOutline": {
border: "none",

@ -51,7 +51,7 @@ export default function DropDown({ question, openBranchingPage, setOpenBranching
questionId={question.id}
variant={variant}
isOwn={Boolean(variant?.isOwn)}
ownPlaceholder={question.content.ownPlaceholder || ""}
ownPlaceholder={""}
shouldAutoFocus={focusedVariantId === variant.id}
onFocusHandled={clearFocusedVariant}
onEnterKeyPress={() => addVariantOnEnter(question.id)}
@ -59,13 +59,8 @@ export default function DropDown({ question, openBranchingPage, setOpenBranching
))}
/>
)}
<Box
sx={{
display: "flex",
alignItems: "center",
marginBottom: "20px",
}}
>
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "flex-start", marginBottom: "20px" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Link
component="button"
variant="body2"
@ -102,6 +97,20 @@ export default function DropDown({ question, openBranchingPage, setOpenBranching
</>
)}
</Box>
{isMobile ? null : (
<Typography
sx={{
fontWeight: 400,
lineHeight: "21.33px",
color: theme.palette.grey2.main,
fontSize: "16px",
mt: "4px",
}}
>
нажмите shift + enter для переноса строки
</Typography>
)}
</Box>
</Box>
<ButtonsOptions
switchState={switchState}
@ -110,6 +119,7 @@ export default function DropDown({ question, openBranchingPage, setOpenBranching
questionContentId={question.content.id}
questionType={question.type}
questionHasParent={question.content.rule.parentId?.length !== 0}
openBranchingPage={openBranchingPage}
setOpenBranchingPage={setOpenBranchingPage}
/>
<SwitchDropDown

@ -47,7 +47,7 @@ export default function Emoji({ question, openBranchingPage, setOpenBranchingPag
setOpen={setOpen}
setSelectedVariant={setSelectedVariant}
isOwn={Boolean(variant?.isOwn)}
ownPlaceholder={question.content.ownPlaceholder}
ownPlaceholder={question.content.ownPlaceholder || ""}
shouldAutoFocus={focusedVariantId === variant.id}
onFocusHandled={clearFocusedVariant}
onEnterKeyPress={() => addVariantOnEnter(question.id)}
@ -84,14 +84,8 @@ export default function Emoji({ question, openBranchingPage, setOpenBranchingPag
}}
/>
</Popover>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "10px",
marginBottom: isMobile ? "17px" : "20px",
}}
>
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "flex-start", marginBottom: isMobile ? "17px" : "20px" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Link
component="button"
variant="body2"
@ -122,6 +116,20 @@ export default function Emoji({ question, openBranchingPage, setOpenBranchingPag
</>
)}
</Box>
{!isTablet && (
<Typography
sx={{
fontWeight: 400,
lineHeight: "21.33px",
color: theme.palette.grey2.main,
fontSize: "16px",
mt: "4px",
}}
>
для переноса строки нажмите shift + enter
</Typography>
)}
</Box>
</Box>
<ButtonsOptions
switchState={switchState}
@ -130,6 +138,7 @@ export default function Emoji({ question, openBranchingPage, setOpenBranchingPag
questionContentId={question.content.id}
questionType={question.type}
questionHasParent={question.content.rule.parentId?.length !== 0}
openBranchingPage={openBranchingPage}
setOpenBranchingPage={setOpenBranchingPage}
/>
<SwitchEmoji

@ -134,13 +134,8 @@ export default function OptionsAndPicture({
selfClose={() => setOpenCropModal(false)}
setPictureUploading={setPictureUploading}
/>
<Box
sx={{
display: "flex",
alignItems: "center",
marginBottom: "17px",
}}
>
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "flex-start", marginBottom: "17px" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Link
component="button"
variant="body2"
@ -179,6 +174,20 @@ export default function OptionsAndPicture({
</>
)}
</Box>
{isMobile ? null : (
<Typography
sx={{
fontWeight: 400,
lineHeight: "21.33px",
color: theme.palette.grey2.main,
fontSize: "16px",
mt: "4px",
}}
>
для переноса строки нажмите shift + enter
</Typography>
)}
</Box>
</Box>
<ButtonsOptions
switchState={switchState}

@ -87,12 +87,12 @@ export default function OptionsPicture({
largeCheck={question.content.largeCheck}
variant={variant}
isMobile={isMobile}
openCropModal={() => {setOpenCropModal(true)}}
openCropModal={() => { setOpenCropModal(true); return Promise.resolve(); }}
openImageUploadModal={openImageUploadModal}
pictureUploding={pictureUploding}
setSelectedVariantId={setSelectedVariantId}
isOwn={Boolean(variant?.isOwn)}
ownPlaceholder={question.content.ownPlaceholder}
ownPlaceholder={question.content.ownPlaceholder || ""}
shouldAutoFocus={focusedVariantId === variant.id}
onFocusHandled={clearFocusedVariant}
onEnterKeyPress={() => addVariantOnEnter(question.id)}
@ -105,17 +105,18 @@ export default function OptionsPicture({
handleImageChange={handleImageUpload}
/>
<CropModalInit
originalImageUrl={variant?.originalImageUrl}
editedUrlImagesList={variant?.editedUrlImagesList}
originalImageUrl={variant?.originalImageUrl ?? ""}
editedUrlImagesList={(variant?.editedUrlImagesList as any) ?? undefined}
questionId={question.id.toString()}
questionType={question.type}
quizId={quizQid}
variantId={variant?.id}
questionType={question.type as any}
quizId={quizQid ?? ""}
variantId={variant?.id ?? ""}
open={openCropModal}
selfClose={() => setOpenCropModal(false)}
setPictureUploading={setPictureUploading}
setPictureUploading={setPictureUploading as any}
/>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "flex-start" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Link
component="button"
variant="body2"
@ -146,6 +147,20 @@ export default function OptionsPicture({
</>
)}
</Box>
{isMobile ? null : (
<Typography
sx={{
fontWeight: 400,
lineHeight: "21.33px",
color: theme.palette.grey2.main,
fontSize: "16px",
mt: "4px",
}}
>
для переноса строки нажмите shift + enter
</Typography>
)}
</Box>
</Box>
<ButtonsOptions
switchState={switchState}
@ -154,11 +169,12 @@ export default function OptionsPicture({
questionContentId={question.content.id}
questionType={question.type}
questionHasParent={question.content.rule.parentId?.length !== 0}
openBranchingPage={openBranchingPage}
setOpenBranchingPage={setOpenBranchingPage}
/>
<SwitchAnswerOptionsPict
switchState={switchState}
question={question}
question={question as any}
/>
</>
);

@ -54,7 +54,7 @@ export default function AnswerOptions({ question, openBranchingPage, setOpenBran
questionId={question.id}
variant={variant}
isOwn={Boolean(variant.isOwn)}
ownPlaceholder={question.content.ownPlaceholder}
ownPlaceholder={question.content.ownPlaceholder || ""}
shouldAutoFocus={focusedVariantId === variant.id}
onFocusHandled={clearFocusedVariant}
onEnterKeyPress={() => addVariantOnEnter(question.id)}
@ -63,13 +63,8 @@ export default function AnswerOptions({ question, openBranchingPage, setOpenBran
/>
)}
<Box
sx={{
display: "flex",
alignItems: "center",
marginBottom: "17px",
}}
>
<Box sx={{ display: "flex", flexDirection: "column", alignItems: "flex-start", marginBottom: "17px" }}>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Link
component="button"
variant="body2"
@ -106,6 +101,20 @@ export default function AnswerOptions({ question, openBranchingPage, setOpenBran
</>
)}
</Box>
{isMobile ? null : (
<Typography
sx={{
fontWeight: 400,
lineHeight: "21.33px",
color: theme.palette.grey2.main,
fontSize: "16px",
mt: "4px",
}}
>
для переноса строки нажмите shift + enter
</Typography>
)}
</Box>
</Box>
<ButtonsOptions
switchState={switchState}

@ -17,12 +17,12 @@ interface CustomTextFieldProps {
value?: string;
error?: string;
emptyError?: boolean;
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
onFocus?: (event: FocusEvent<HTMLInputElement>) => void;
onClick?: (event: MouseEvent<HTMLInputElement>) => void;
onPaste?: (event: ClipboardEvent<HTMLInputElement>) => void;
onChange?: (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onKeyDown?: (event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onBlur?: (event: FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onFocus?: (event: FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onClick?: (event: MouseEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onPaste?: (event: ClipboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
text?: string;
maxLength?: number;
sx?: SxProps<Theme>;
@ -32,7 +32,7 @@ interface CustomTextFieldProps {
rows?: number;
className?: string;
disabled?: boolean;
inputRef?: Ref<HTMLInputElement>;
inputRef?: Ref<HTMLInputElement | HTMLTextAreaElement>;
}
export default function CustomTextField({
@ -67,7 +67,9 @@ export default function CustomTextField({
setInputValue(value);
}, [value]);
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const handleInputChange = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
if (event.target.value.length <= maxLength) {
const inputValue = event.target.value;
@ -85,12 +87,16 @@ export default function CustomTextField({
}
};
const handleInputFocus = (event: React.FocusEvent<HTMLInputElement>) => {
const handleInputFocus = (
event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setIsInputActive(true);
if (onFocus) onFocus(event);
};
const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>) => {
const handleInputBlur = (
event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
setIsInputActive(false);
if (onBlur) {
@ -98,6 +104,12 @@ export default function CustomTextField({
}
};
const mergedInputElementProps = {
...(InputProps?.inputProps as any),
onClick,
onPaste,
};
return (
<FormControl
fullWidth
@ -126,14 +138,13 @@ export default function CustomTextField({
onFocus={handleInputFocus}
onBlur={handleInputBlur}
onKeyDown={onKeyDown}
onClick={onClick}
onPaste={onPaste}
multiline={rows > 0}
rows={rows}
multiline
rows={rows > 0 ? rows : 1}
disabled={disabled}
disableUnderline
inputRef={inputRef}
{...InputProps}
inputProps={mergedInputElementProps}
sx={{
maxLength: maxLength,
borderRadius: "10px",
@ -143,6 +154,8 @@ export default function CustomTextField({
border: `${isInputActive ? "black 2px" : "#9A9AAF 1px"} solid`,
backgroundColor: theme.palette.background.default,
height: "48px",
// Prevent resize handle to keep visuals unchanged
'& textarea': { resize: 'none' },
...sx,
}}
data-cy="textfield"