diff --git a/src/App.tsx b/src/App.tsx index 5e2e7ad..c186a89 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,21 +1,37 @@ import { Box } from "@mui/material"; +import { useEffect, useState } from "react"; import { useParams } from "react-router-dom"; import QuizAnswerer from "./QuizAnswerer"; import { QuizIdContext } from "./contexts/QuizIdContext"; +import { RootContainerWidthContext } from "./contexts/RootContainerWidthContext"; const defaultQuizId = "ef836ff8-35b1-4031-9acf-af5766bac2b2"; export default function App() { const quizId = useParams().quizId ?? defaultQuizId; + const [rootContainerSize, setRootContainerSize] = useState(Infinity); + + useEffect(() => { + const handleWindowResize = () => { + setRootContainerSize(window.innerWidth); + }; + window.addEventListener("resize", handleWindowResize); + + return () => { + window.removeEventListener("resize", handleWindowResize); + }; + }, []); return ( - - - - - + + + + + + + ); } diff --git a/src/WidgetApp.tsx b/src/WidgetApp.tsx index a6c7215..142b00b 100644 --- a/src/WidgetApp.tsx +++ b/src/WidgetApp.tsx @@ -1,6 +1,8 @@ import { Box } from "@mui/material"; +import { useEffect, useRef, useState } from "react"; import QuizAnswerer from "./QuizAnswerer"; import { QuizIdContext } from "./contexts/QuizIdContext"; +import { RootContainerWidthContext } from "./contexts/RootContainerWidthContext"; interface Props { @@ -8,15 +10,35 @@ interface Props { } export default function WidgetApp({ quizId }: Props) { + const [rootContainerSize, setRootContainerSize] = useState(Infinity); + const rootContainerRef = useRef(null); + + useEffect(() => { + const handleWindowResize = () => { + if (!rootContainerRef.current) return; + + setRootContainerSize(rootContainerRef.current.clientWidth); + }; + window.addEventListener("resize", handleWindowResize); + + return () => { + window.removeEventListener("resize", handleWindowResize); + }; + }, []); return ( - - - - - + + + + + + + ); } diff --git a/src/contexts/RootContainerWidthContext.ts b/src/contexts/RootContainerWidthContext.ts new file mode 100644 index 0000000..e1987a7 --- /dev/null +++ b/src/contexts/RootContainerWidthContext.ts @@ -0,0 +1,11 @@ +import { createContext, useContext } from "react"; + + +export const RootContainerWidthContext = createContext(null); + +export const useRootContainerSize = () => { + const rootContainerSize = useContext(RootContainerWidthContext); + if (rootContainerSize === null) throw new Error("rootContainerSize context is null"); + + return rootContainerSize; +}; diff --git a/src/pages/ViewPublicationPage/ContactForm.tsx b/src/pages/ViewPublicationPage/ContactForm.tsx index 321423a..600d248 100644 --- a/src/pages/ViewPublicationPage/ContactForm.tsx +++ b/src/pages/ViewPublicationPage/ContactForm.tsx @@ -3,7 +3,7 @@ import EmailIcon from "@icons/ContactFormIcon/EmailIcon"; import NameIcon from "@icons/ContactFormIcon/NameIcon"; import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon"; import TextIcon from "@icons/ContactFormIcon/TextIcon"; -import { Box, Button, InputAdornment, Link, TextField as MuiTextField, TextFieldProps, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { Box, Button, InputAdornment, Link, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material"; import CustomCheckbox from "@ui_kit/CustomCheckbox"; import { FC, useRef, useState } from "react"; @@ -12,10 +12,11 @@ import { sendFC } from "@api/quizRelase"; import { NameplateLogo } from "@icons/NameplateLogo"; import { QuizQuestionResult } from "@model/questionTypes/result"; import { useQuizData } from "@utils/hooks/useQuizData"; +import { quizThemes } from "@utils/themes/Publication/themePublication"; import { enqueueSnackbar } from "notistack"; +import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; import { ApologyPage } from "./ApologyPage"; import { checkEmptyData } from "./tools/checkEmptyData"; -import { quizThemes } from "@utils/themes/Publication/themePublication"; const TextField = MuiTextField as unknown as FC; // temporary fix ts(2590) @@ -84,7 +85,7 @@ export const ContactForm = ({ const fireOnce = useRef(true); const [fire, setFire] = useState(false); - const isMobile = useMediaQuery(theme.breakpoints.down(850)); + const isMobile = useRootContainerSize() < 850; const followNextForm = () => { setShowContactForm(false); @@ -475,7 +476,7 @@ const Inputs = ({ const CustomInput = ({ title, desc, Icon, onChange }: any) => { const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(600)); + const isMobile = useRootContainerSize() < 600; //@ts-ignore return ( diff --git a/src/pages/ViewPublicationPage/Footer.tsx b/src/pages/ViewPublicationPage/Footer.tsx index 34866ca..5b89510 100644 --- a/src/pages/ViewPublicationPage/Footer.tsx +++ b/src/pages/ViewPublicationPage/Footer.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { Box, Button, Typography, useTheme } from "@mui/material"; import { useCallback, useMemo, useState } from "react"; import { enqueueSnackbar } from "notistack"; @@ -9,6 +9,7 @@ import { checkEmptyData } from "./tools/checkEmptyData"; import type { QuizQuestionResult } from "@model/questionTypes/result"; import { useQuizViewStore } from "@stores/quizView/store"; import { useQuizData } from "@utils/hooks/useQuizData"; +import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; type FooterProps = { setCurrentQuestion: (step: AnyTypedQuizQuestion) => void; @@ -25,7 +26,7 @@ export const Footer = ({ setCurrentQuestion, question, setShowContactForm, setSh const [stepNumber, setStepNumber] = useState(1); - const isMobileMini = useMediaQuery(theme.breakpoints.down(382)); + const isMobileMini = useRootContainerSize() < 382; const isLinear = !questions.some(({ content }) => content.rule.parentId === "root"); const getNextQuestionId = useCallback(() => { diff --git a/src/pages/ViewPublicationPage/Question.tsx b/src/pages/ViewPublicationPage/Question.tsx index d3fe759..2d42501 100644 --- a/src/pages/ViewPublicationPage/Question.tsx +++ b/src/pages/ViewPublicationPage/Question.tsx @@ -1,4 +1,4 @@ -import { Box, useMediaQuery, useTheme } from "@mui/material"; +import { Box, useTheme } from "@mui/material"; import { useEffect, useState } from "react"; import { ContactForm } from "./ContactForm"; @@ -21,9 +21,10 @@ import type { AnyTypedQuizQuestion } from "../../model/questionTypes/shared"; import { NameplateLogoFQ } from "@icons/NameplateLogoFQ"; import { NameplateLogoFQDark } from "@icons/NameplateLogoFQDark"; import { QuizQuestionResult } from "@model/questionTypes/result"; +import { useQuizData } from "@utils/hooks/useQuizData"; import { notReachable } from "@utils/notReachable"; import { quizThemes } from "@utils/themes/Publication/themePublication"; -import { useQuizData } from "@utils/hooks/useQuizData"; +import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; export const Question = () => { const theme = useTheme(); @@ -31,7 +32,7 @@ export const Question = () => { const [currentQuestion, setCurrentQuestion] = useState(); const [showContactForm, setShowContactForm] = useState(false); const [showResultForm, setShowResultForm] = useState(false); - const isMobile = useMediaQuery(theme.breakpoints.down(650)); + const isMobile = useRootContainerSize() < 650; useEffect(() => { if (settings?.cfg.haveRoot) {//ветвимся diff --git a/src/pages/ViewPublicationPage/ResultForm.tsx b/src/pages/ViewPublicationPage/ResultForm.tsx index 335d9f3..b574ae1 100644 --- a/src/pages/ViewPublicationPage/ResultForm.tsx +++ b/src/pages/ViewPublicationPage/ResultForm.tsx @@ -2,17 +2,17 @@ import { Box, Button, Typography, - useMediaQuery, - useTheme, + useTheme } from "@mui/material"; import { NameplateLogo } from "@icons/NameplateLogo"; import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe"; +import { useQuizData } from "@utils/hooks/useQuizData"; import { quizThemes } from "@utils/themes/Publication/themePublication"; import { useCallback, useEffect, useMemo } from "react"; +import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; import type { QuizQuestionResult } from "../../model/questionTypes/result"; -import { useQuizData } from "@utils/hooks/useQuizData"; type ResultFormProps = { @@ -28,7 +28,7 @@ export const ResultForm = ({ setShowResultForm, }: ResultFormProps) => { const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(650)); + const isMobile = useRootContainerSize() < 650; const { settings, questions } = useQuizData(); const resultQuestion = useMemo(() => { diff --git a/src/pages/ViewPublicationPage/StartPageViewPublication.tsx b/src/pages/ViewPublicationPage/StartPageViewPublication.tsx index b80dba8..f8f540b 100644 --- a/src/pages/ViewPublicationPage/StartPageViewPublication.tsx +++ b/src/pages/ViewPublicationPage/StartPageViewPublication.tsx @@ -1,4 +1,4 @@ -import { Box, Button, ButtonBase, Link, Paper, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { Box, Button, ButtonBase, Link, Paper, Typography, useTheme } from "@mui/material"; import { useUADevice } from "../../utils/hooks/useUADevice"; import { notReachable } from "../../utils/notReachable"; import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe"; @@ -7,6 +7,7 @@ import { NameplateLogo } from "@icons/NameplateLogo"; import { QuizStartpageAlignType, QuizStartpageType } from "@model/settingsData"; import { useQuizData } from "@utils/hooks/useQuizData"; import { quizThemes } from "@utils/themes/Publication/themePublication"; +import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; interface Props { @@ -17,7 +18,7 @@ export const StartPageViewPublication = ({ setVisualStartPage }: Props) => { const theme = useTheme(); const { settings } = useQuizData(); const { isMobileDevice } = useUADevice(); - const isMobile = useMediaQuery(theme.breakpoints.down(650)); + const isMobile = useRootContainerSize() < 650; const handleCopyNumber = () => { navigator.clipboard.writeText(settings.cfg.info.phonenumber); @@ -266,8 +267,8 @@ function QuizPreviewLayoutByType({ startpageType: QuizStartpageType; alignType: QuizStartpageAlignType; }) { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(650)); + const isMobile = useRootContainerSize() < 650; + function StartPageMobile() { return ( { const answer = answers.find( ({ questionId }) => questionId === currentQuestion.id )?.answer as string; - const isMobile = useMediaQuery(theme.breakpoints.down(500)); + const isMobile = useRootContainerSize() < 500; const uploadFile = async ({ target }: ChangeEvent) => { const file = target.files?.[0]; if (file) { diff --git a/src/pages/ViewPublicationPage/questions/Images.tsx b/src/pages/ViewPublicationPage/questions/Images.tsx index 6d0483c..4c65c49 100644 --- a/src/pages/ViewPublicationPage/questions/Images.tsx +++ b/src/pages/ViewPublicationPage/questions/Images.tsx @@ -1,21 +1,21 @@ import { Box, - Typography, - RadioGroup, FormControlLabel, Radio, - useTheme, - useMediaQuery, + RadioGroup, + Typography, + useTheme } from "@mui/material"; -import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store"; +import { deleteAnswer, updateAnswer, useQuizViewStore } from "@stores/quizView/store"; import RadioCheck from "@ui_kit/RadioCheck"; import RadioIcon from "@ui_kit/RadioIcon"; -import type { QuizQuestionImages } from "../../../model/questionTypes/images"; -import { enqueueSnackbar } from "notistack"; import { sendAnswer } from "@api/quizRelase"; import { useQuizData } from "@utils/hooks/useQuizData"; +import { enqueueSnackbar } from "notistack"; +import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext"; +import type { QuizQuestionImages } from "../../../model/questionTypes/images"; type ImagesProps = { currentQuestion: QuizQuestionImages; @@ -25,12 +25,9 @@ export const Images = ({ currentQuestion }: ImagesProps) => { const { settings } = useQuizData(); const { answers } = useQuizViewStore(); const theme = useTheme(); - const { answer } = - answers.find( - ({ questionId }) => questionId === currentQuestion.id - ) ?? {}; - const isTablet = useMediaQuery(theme.breakpoints.down(1000)); - const isMobile = useMediaQuery(theme.breakpoints.down(500)); + const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer; + const isTablet = useRootContainerSize() < 1000; + const isMobile = useRootContainerSize() < 500; return ( diff --git a/src/pages/ViewPublicationPage/questions/Number.tsx b/src/pages/ViewPublicationPage/questions/Number.tsx index 1c1c920..9ff810e 100644 --- a/src/pages/ViewPublicationPage/questions/Number.tsx +++ b/src/pages/ViewPublicationPage/questions/Number.tsx @@ -1,18 +1,19 @@ -import { useState, useEffect } from "react"; -import { Box, Typography, useMediaQuery, useTheme } from "@mui/material"; +import { Box, Typography, useTheme } from "@mui/material"; +import { useEffect, useState } from "react"; import { useDebouncedCallback } from "use-debounce"; -import CustomTextField from "@ui_kit/CustomTextField"; import { CustomSlider } from "@ui_kit/CustomSlider"; +import CustomTextField from "@ui_kit/CustomTextField"; -import { useQuizViewStore, updateAnswer } from "@stores/quizView/store"; +import { updateAnswer, useQuizViewStore } from "@stores/quizView/store"; -import type { QuizQuestionNumber } from "../../../model/questionTypes/number"; -import { enqueueSnackbar } from "notistack"; import { sendAnswer } from "@api/quizRelase"; +import { enqueueSnackbar } from "notistack"; +import type { QuizQuestionNumber } from "../../../model/questionTypes/number"; -import { quizThemes } from "@utils/themes/Publication/themePublication"; import { useQuizData } from "@utils/hooks/useQuizData"; +import { quizThemes } from "@utils/themes/Publication/themePublication"; +import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext"; type NumberProps = { currentQuestion: QuizQuestionNumber; @@ -26,7 +27,7 @@ export const Number = ({ currentQuestion }: NumberProps) => { const theme = useTheme(); const { answers } = useQuizViewStore(); - const isMobile = useMediaQuery(theme.breakpoints.down(650)); + const isMobile = useRootContainerSize() < 650; const min = window.Number(currentQuestion.content.range.split("—")[0]); const max = window.Number(currentQuestion.content.range.split("—")[1]); diff --git a/src/pages/ViewPublicationPage/questions/Rating.tsx b/src/pages/ViewPublicationPage/questions/Rating.tsx index bf89ab6..1d6f5f7 100644 --- a/src/pages/ViewPublicationPage/questions/Rating.tsx +++ b/src/pages/ViewPublicationPage/questions/Rating.tsx @@ -1,25 +1,25 @@ import { Box, - Typography, Rating as RatingComponent, - useTheme, - useMediaQuery + Typography, + useTheme } from "@mui/material"; -import { useQuizViewStore, updateAnswer } from "@stores/quizView/store"; +import { updateAnswer, useQuizViewStore } from "@stores/quizView/store"; -import TropfyIcon from "@icons/questionsPage/tropfyIcon"; import FlagIcon from "@icons/questionsPage/FlagIcon"; -import HeartIcon from "@icons/questionsPage/heartIcon"; -import LikeIcon from "@icons/questionsPage/likeIcon"; -import LightbulbIcon from "@icons/questionsPage/lightbulbIcon"; -import HashtagIcon from "@icons/questionsPage/hashtagIcon"; import StarIconMini from "@icons/questionsPage/StarIconMini"; +import HashtagIcon from "@icons/questionsPage/hashtagIcon"; +import HeartIcon from "@icons/questionsPage/heartIcon"; +import LightbulbIcon from "@icons/questionsPage/lightbulbIcon"; +import LikeIcon from "@icons/questionsPage/likeIcon"; +import TropfyIcon from "@icons/questionsPage/tropfyIcon"; -import type { QuizQuestionRating } from "../../../model/questionTypes/rating"; -import { enqueueSnackbar } from "notistack"; import { sendAnswer } from "@api/quizRelase"; import { useQuizData } from "@utils/hooks/useQuizData"; +import { enqueueSnackbar } from "notistack"; +import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext"; +import type { QuizQuestionRating } from "../../../model/questionTypes/rating"; type RatingProps = { currentQuestion: QuizQuestionRating; @@ -60,7 +60,7 @@ export const Rating = ({ currentQuestion }: RatingProps) => { const { settings } = useQuizData(); const { answers } = useQuizViewStore(); const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(650)); + const isMobile = useRootContainerSize() < 650; const { answer } = answers.find( ({ questionId }) => questionId === currentQuestion.id diff --git a/src/pages/ViewPublicationPage/questions/Varimg.tsx b/src/pages/ViewPublicationPage/questions/Varimg.tsx index ec8c79b..bee631c 100644 --- a/src/pages/ViewPublicationPage/questions/Varimg.tsx +++ b/src/pages/ViewPublicationPage/questions/Varimg.tsx @@ -1,23 +1,23 @@ import { Box, - Typography, - RadioGroup, FormControlLabel, Radio, - useTheme, - useMediaQuery + RadioGroup, + Typography, + useTheme } from "@mui/material"; -import { useQuizViewStore, updateAnswer, deleteAnswer } from "@stores/quizView/store"; +import { deleteAnswer, updateAnswer, useQuizViewStore } from "@stores/quizView/store"; import RadioCheck from "@ui_kit/RadioCheck"; import RadioIcon from "@ui_kit/RadioIcon"; -import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg"; -import { enqueueSnackbar } from "notistack"; import { sendAnswer } from "@api/quizRelase"; -import { quizThemes } from "@utils/themes/Publication/themePublication"; import BlankImage from "@icons/BlankImage"; import { useQuizData } from "@utils/hooks/useQuizData"; +import { quizThemes } from "@utils/themes/Publication/themePublication"; +import { enqueueSnackbar } from "notistack"; +import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext"; +import type { QuizQuestionVarImg } from "../../../model/questionTypes/varimg"; type VarimgProps = { currentQuestion: QuizQuestionVarImg; @@ -27,7 +27,7 @@ export const Varimg = ({ currentQuestion }: VarimgProps) => { const { settings } = useQuizData(); const { answers } = useQuizViewStore(); const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down(650)); + const isMobile = useRootContainerSize() < 650; const { answer } = answers.find( diff --git a/src/ui_kit/LabeledDatePicker.tsx b/src/ui_kit/LabeledDatePicker.tsx index ee8218c..3a68702 100644 --- a/src/ui_kit/LabeledDatePicker.tsx +++ b/src/ui_kit/LabeledDatePicker.tsx @@ -1,69 +1,70 @@ import CalendarIcon from "@icons/CalendarIcon"; -import { Typography, Box, SxProps, Theme, useMediaQuery, useTheme } from "@mui/material"; +import { Box, SxProps, Theme, Typography, useTheme } from "@mui/material"; import { DatePicker } from "@mui/x-date-pickers"; import moment from "moment"; +import { useRootContainerSize } from "../contexts/RootContainerWidthContext"; interface Props { - label?: string; - sx?: SxProps; - value?: moment.Moment; - onChange?: (value: string | null) => void; + label?: string; + sx?: SxProps; + value?: moment.Moment; + onChange?: (value: string | null) => void; } export default function LabeledDatePicker({ label, value = moment(), onChange, sx }: Props) { - const theme = useTheme(); - const upLg = useMediaQuery(theme.breakpoints.up("md")); + const theme = useTheme(); + const upLg = useRootContainerSize() > theme.breakpoints.values.md; - return ( - - {label && ( - - {label} - - )} - , - }} - slotProps={{ - openPickerButton: { - sx: { - p: 0, - }, - "data-cy": "open-datepicker", - }, - }} - sx={{ - "& .MuiInputBase-root": { - backgroundColor: "#F2F3F7", - borderRadius: "10px", - pr: "22px", - "& input": { - py: "11px", - pl: upLg ? "20px" : "13px", - lineHeight: "19px", - }, - "& fieldset": { - borderColor: "#9A9AAF", - }, - }, - }} - /> - - ); + {label && ( + + {label} + + )} + , + }} + slotProps={{ + openPickerButton: { + sx: { + p: 0, + }, + "data-cy": "open-datepicker", + }, + }} + sx={{ + "& .MuiInputBase-root": { + backgroundColor: "#F2F3F7", + borderRadius: "10px", + pr: "22px", + "& input": { + py: "11px", + pl: upLg ? "20px" : "13px", + lineHeight: "19px", + }, + "& fieldset": { + borderColor: "#9A9AAF", + }, + }, + }} + /> + + ); }