feat: rating autosize inputs

This commit is contained in:
IlyaDoronin 2023-09-27 10:55:11 +03:00
parent 123a26a436
commit 0c1279b37d
2 changed files with 176 additions and 88 deletions

@ -1,10 +1,16 @@
import { useState } from "react";
import { useState, useEffect, useRef } from "react";
import { useParams } from "react-router-dom";
import { Box, Typography, useMediaQuery, useTheme } from "@mui/material";
import {
Box,
Typography,
TextField,
useMediaQuery,
useTheme,
} from "@mui/material";
import { useDebouncedCallback } from "use-debounce";
import { questionStore, updateQuestionsList } from "@root/questions";
import ButtonsOptions from "../ButtonsOptions";
import SwitchRating from "./switchRating";
import { questionStore, updateQuestionsList } from "@root/questions";
import CustomTextField from "@ui_kit/CustomTextField";
import TropfyIcon from "../../../assets/icons/questionsPage/tropfyIcon";
import FlagIcon from "../../../assets/icons/questionsPage/FlagIcon";
@ -25,10 +31,49 @@ export type ButtonRatingFrom = {
export default function RatingOptions({ totalIndex }: Props) {
const [switchState, setSwitchState] = useState("setting");
const [negativeText, setNegativeText] = useState<string>("");
const [positiveText, setPositiveText] = useState<string>("");
const [negativeTextWidth, setNegativeTextWidth] = useState<number>(0);
const [positiveTextWidth, setPositiveTextWidth] = useState<number>(0);
const quizId = Number(useParams().quizId);
const { listQuestions } = questionStore();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790));
const negativeRef = useRef<HTMLDivElement>(null);
const positiveRef = useRef<HTMLDivElement>(null);
const debounceNegativeDescription = useDebouncedCallback((value) => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingNegativeDescription: value.substring(0, 15),
},
});
}, 500);
const debouncePositiveDescription = useDebouncedCallback((value) => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingPositiveDescription: value.substring(0, 15),
},
});
}, 500);
useEffect(() => {
setNegativeText(
listQuestions[quizId][totalIndex].content.ratingNegativeDescription
);
setPositiveText(
listQuestions[quizId][totalIndex].content.ratingPositiveDescription
);
}, []);
useEffect(() => {
setNegativeTextWidth(negativeRef.current?.offsetWidth || 0);
}, [negativeText]);
useEffect(() => {
setPositiveTextWidth(positiveRef.current?.offsetWidth || 0);
}, [positiveText]);
const buttonRatingForm: ButtonRatingFrom[] = [
{ name: "star", icon: <StarIconMini color={theme.palette.grey2.main} /> },
@ -54,7 +99,7 @@ export default function RatingOptions({ totalIndex }: Props) {
width: isMobile
? "auto"
: `${listQuestions[quizId][totalIndex].content.steps * 44}px`,
minWidth: "200px",
minWidth: "300px",
maxWidth: "440px",
display: "flex",
padding: "20px",
@ -62,35 +107,19 @@ export default function RatingOptions({ totalIndex }: Props) {
gap: "20px",
}}
>
<Box sx={{ display: "flex", gap: isMobile ? "10px" : "15px" }}>
<Box
sx={{
display: "flex",
justifyContent: "space-between",
padding: "0 10px",
gap: isMobile ? "10px" : "15px",
}}
>
{Array.from(
{ length: listQuestions[quizId][totalIndex].content.steps },
(_, index) => index
).map((itemNumber) => (
<Box
key={itemNumber}
{...(itemNumber === 0 ||
itemNumber === listQuestions[quizId][totalIndex].content.steps - 1
? {
onClick: () => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingExpanded: true,
},
});
},
sx: {
cursor: "pointer",
transform: "scale(1.5)",
":hover": {
transform: "scale(1.7)",
transition: "0.2s",
},
},
}
: { sx: { transform: "scale(1.5)" } })}
>
<Box key={itemNumber} sx={{ transform: "scale(1.5)" }}>
{
buttonRatingForm.find(
({ name }) =>
@ -100,65 +129,124 @@ export default function RatingOptions({ totalIndex }: Props) {
</Box>
))}
</Box>
{!listQuestions[quizId][totalIndex].content.ratingDescription && (
<Box
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Typography
ref={negativeRef}
sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
position: "absolute",
opacity: 0,
zIndex: "-100",
whiteSpace: "nowrap",
fontSize: "16px",
}}
>
<Typography
sx={{
color: theme.palette.grey2.main,
fontSize: "16px",
fontWeight: 400,
}}
>
Негативно
</Typography>
<Typography
sx={{
color: theme.palette.grey2.main,
fontSize: "16px",
fontWeight: 400,
}}
>
Позитивно
</Typography>
</Box>
)}
{listQuestions[quizId][totalIndex].content.ratingExpanded &&
(listQuestions[quizId][totalIndex].content.ratingDescription ? (
<Typography>
{listQuestions[quizId][totalIndex].content.ratingDescription}
</Typography>
) : (
<CustomTextField
placeholder={"Описание"}
text={listQuestions[quizId][totalIndex].content.ratingDescription}
onKeyDown={({ target, key }) => {
if (key === "Enter") {
const currentTarget = target as HTMLInputElement;
{negativeText}
</Typography>
<TextField
defaultValue={
listQuestions[quizId][totalIndex].content
.ratingNegativeDescription
}
value={negativeText}
placeholder="Негативно"
onChange={({ target }) => {
if (positiveText) {
setPositiveText("");
debouncePositiveDescription("");
}
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingDescription: currentTarget.value.substring(0, 20),
},
});
}
}}
onBlur={({ target }) => {
updateQuestionsList(quizId, totalIndex, {
content: {
...listQuestions[quizId][totalIndex].content,
ratingDescription: target.value.substring(0, 20),
if (target.value.length <= 15) {
setNegativeText(target.value);
debounceNegativeDescription(target.value);
}
}}
onBlur={({ target }) => debounceNegativeDescription(target.value)}
sx={{
width: negativeTextWidth + 10 + "px",
background: "transparent",
fontSize: "18px",
minWidth: "95px",
maxWidth: "230px",
transition: "0.2s",
"& .MuiInputBase-root": {
"& .MuiInputBase-input": {
color: theme.palette.grey2.main,
fontSize: "16px",
padding: "0 3px",
borderRadius: "3px",
border: "1px solid",
borderColor: "transparent",
"&:hover, &:focus": {
borderColor: theme.palette.grey2.main,
},
});
}}
/>
))}
},
"& .MuiOutlinedInput-notchedOutline": {
outline: "none",
border: "none",
},
},
}}
/>
<Typography
ref={positiveRef}
sx={{
position: "absolute",
opacity: 0,
zIndex: "-100",
whiteSpace: "nowrap",
fontSize: "16px",
}}
>
{positiveText}
</Typography>
<TextField
value={positiveText}
placeholder="Позитивно"
onChange={({ target }) => {
if (negativeText) {
setNegativeText("");
debounceNegativeDescription("");
}
if (target.value.length <= 15) {
setPositiveText(target.value);
debouncePositiveDescription(target.value);
}
}}
onBlur={({ target }) => debouncePositiveDescription(target.value)}
sx={{
width: positiveTextWidth + 10 + "px",
background: "transparent",
fontSize: "18px",
minWidth: "95px",
maxWidth: "230px",
transition: "0.2s",
"& .MuiInputBase-root": {
"& .MuiInputBase-input": {
color: theme.palette.grey2.main,
fontSize: "16px",
padding: "0 3px",
borderRadius: "3px",
border: "1px solid",
borderColor: "transparent",
"&:hover, &:focus": {
borderColor: theme.palette.grey2.main,
},
},
"& .MuiOutlinedInput-notchedOutline": {
outline: "none",
border: "none",
},
},
}}
/>
</Box>
</Box>
<ButtonsOptions
switchState={switchState}

@ -64,8 +64,8 @@ export interface Question {
chooseRange: boolean;
required: boolean;
replText: string;
ratingExpanded: boolean;
ratingDescription: string;
ratingNegativeDescription: string;
ratingPositiveDescription: string;
};
version: number;
parent_ids: number[];
@ -116,8 +116,8 @@ export const DEFAULT_QUESTION: Omit<Question, "id"> = {
chooseRange: false,
required: false,
replText: "",
ratingExpanded: false,
ratingDescription: "",
ratingNegativeDescription: "",
ratingPositiveDescription: "",
variants: [
{
answer: "",