feat: rating autosize inputs
This commit is contained in:
parent
123a26a436
commit
0c1279b37d
@ -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: "",
|
||||||
|
Loading…
Reference in New Issue
Block a user