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 { 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 ButtonsOptions from "../ButtonsOptions";
import SwitchRating from "./switchRating"; import SwitchRating from "./switchRating";
import { questionStore, updateQuestionsList } from "@root/questions";
import CustomTextField from "@ui_kit/CustomTextField";
import TropfyIcon from "../../../assets/icons/questionsPage/tropfyIcon"; import TropfyIcon from "../../../assets/icons/questionsPage/tropfyIcon";
import FlagIcon from "../../../assets/icons/questionsPage/FlagIcon"; import FlagIcon from "../../../assets/icons/questionsPage/FlagIcon";
@ -25,10 +31,49 @@ export type ButtonRatingFrom = {
export default function RatingOptions({ totalIndex }: Props) { export default function RatingOptions({ totalIndex }: Props) {
const [switchState, setSwitchState] = useState("setting"); 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 quizId = Number(useParams().quizId);
const { listQuestions } = questionStore(); const { listQuestions } = questionStore();
const theme = useTheme(); const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(790)); 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[] = [ const buttonRatingForm: ButtonRatingFrom[] = [
{ name: "star", icon: <StarIconMini color={theme.palette.grey2.main} /> }, { name: "star", icon: <StarIconMini color={theme.palette.grey2.main} /> },
@ -54,7 +99,7 @@ export default function RatingOptions({ totalIndex }: Props) {
width: isMobile width: isMobile
? "auto" ? "auto"
: `${listQuestions[quizId][totalIndex].content.steps * 44}px`, : `${listQuestions[quizId][totalIndex].content.steps * 44}px`,
minWidth: "200px", minWidth: "300px",
maxWidth: "440px", maxWidth: "440px",
display: "flex", display: "flex",
padding: "20px", padding: "20px",
@ -62,35 +107,19 @@ export default function RatingOptions({ totalIndex }: Props) {
gap: "20px", 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( {Array.from(
{ length: listQuestions[quizId][totalIndex].content.steps }, { length: listQuestions[quizId][totalIndex].content.steps },
(_, index) => index (_, index) => index
).map((itemNumber) => ( ).map((itemNumber) => (
<Box <Box key={itemNumber} sx={{ transform: "scale(1.5)" }}>
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)" } })}
>
{ {
buttonRatingForm.find( buttonRatingForm.find(
({ name }) => ({ name }) =>
@ -100,65 +129,124 @@ export default function RatingOptions({ totalIndex }: Props) {
</Box> </Box>
))} ))}
</Box> </Box>
{!listQuestions[quizId][totalIndex].content.ratingDescription && ( <Box
<Box sx={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Typography
ref={negativeRef}
sx={{ sx={{
display: "flex", position: "absolute",
alignItems: "center", opacity: 0,
justifyContent: "space-between", zIndex: "-100",
whiteSpace: "nowrap",
fontSize: "16px",
}} }}
> >
<Typography {negativeText}
sx={{ </Typography>
color: theme.palette.grey2.main, <TextField
fontSize: "16px", defaultValue={
fontWeight: 400, listQuestions[quizId][totalIndex].content
}} .ratingNegativeDescription
> }
Негативно value={negativeText}
</Typography> placeholder="Негативно"
<Typography onChange={({ target }) => {
sx={{ if (positiveText) {
color: theme.palette.grey2.main, setPositiveText("");
fontSize: "16px", debouncePositiveDescription("");
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;
updateQuestionsList(quizId, totalIndex, { if (target.value.length <= 15) {
content: { setNegativeText(target.value);
...listQuestions[quizId][totalIndex].content, debounceNegativeDescription(target.value);
ratingDescription: currentTarget.value.substring(0, 20), }
}, }}
}); onBlur={({ target }) => debounceNegativeDescription(target.value)}
} sx={{
}} width: negativeTextWidth + 10 + "px",
onBlur={({ target }) => { background: "transparent",
updateQuestionsList(quizId, totalIndex, { fontSize: "18px",
content: { minWidth: "95px",
...listQuestions[quizId][totalIndex].content, maxWidth: "230px",
ratingDescription: target.value.substring(0, 20), 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> </Box>
<ButtonsOptions <ButtonsOptions
switchState={switchState} switchState={switchState}

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