add hook for retreiving root container width

This commit is contained in:
nflnkr 2024-02-05 13:10:02 +03:00
parent f6d4dfe826
commit ba547dcb55
14 changed files with 197 additions and 143 deletions

@ -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<number>(Infinity);
useEffect(() => {
const handleWindowResize = () => {
setRootContainerSize(window.innerWidth);
};
window.addEventListener("resize", handleWindowResize);
return () => {
window.removeEventListener("resize", handleWindowResize);
};
}, []);
return (
<QuizIdContext.Provider value={quizId}>
<Box sx={{
height: "100dvh",
}}>
<QuizAnswerer />
</Box>
</QuizIdContext.Provider>
<RootContainerWidthContext.Provider value={rootContainerSize}>
<QuizIdContext.Provider value={quizId}>
<Box sx={{
height: "100dvh",
}}>
<QuizAnswerer />
</Box>
</QuizIdContext.Provider>
</RootContainerWidthContext.Provider>
);
}

@ -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<number>(Infinity);
const rootContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleWindowResize = () => {
if (!rootContainerRef.current) return;
setRootContainerSize(rootContainerRef.current.clientWidth);
};
window.addEventListener("resize", handleWindowResize);
return () => {
window.removeEventListener("resize", handleWindowResize);
};
}, []);
return (
<QuizIdContext.Provider value={quizId}>
<Box sx={{
width: "100%",
height: "100%",
}}>
<QuizAnswerer />
</Box>
</QuizIdContext.Provider>
<RootContainerWidthContext.Provider value={rootContainerSize}>
<QuizIdContext.Provider value={quizId}>
<Box
ref={rootContainerRef}
sx={{
width: "100%",
height: "100%",
}}
>
<QuizAnswerer />
</Box>
</QuizIdContext.Provider>
</RootContainerWidthContext.Provider>
);
}

@ -0,0 +1,11 @@
import { createContext, useContext } from "react";
export const RootContainerWidthContext = createContext<number | null>(null);
export const useRootContainerSize = () => {
const rootContainerSize = useContext(RootContainerWidthContext);
if (rootContainerSize === null) throw new Error("rootContainerSize context is null");
return rootContainerSize;
};

@ -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<TextFieldProps>; // 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 (
<Box m="15px 0">

@ -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(() => {

@ -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<AnyTypedQuizQuestion>();
const [showContactForm, setShowContactForm] = useState<boolean>(false);
const [showResultForm, setShowResultForm] = useState<boolean>(false);
const isMobile = useMediaQuery(theme.breakpoints.down(650));
const isMobile = useRootContainerSize() < 650;
useEffect(() => {
if (settings?.cfg.haveRoot) {//ветвимся

@ -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(() => {

@ -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 (
<Box

@ -1,23 +1,25 @@
import {
Box,
Typography,
ButtonBase,
useTheme,
IconButton, useMediaQuery, Modal,
IconButton,
Modal,
Typography,
useTheme
} from "@mui/material";
import { useQuizViewStore, updateAnswer } from "@stores/quizView/store";
import { updateAnswer, useQuizViewStore } from "@stores/quizView/store";
import UploadIcon from "@icons/UploadIcon";
import CloseBold from "@icons/CloseBold";
import UploadIcon from "@icons/UploadIcon";
import { useState, type ChangeEvent } from "react";
import type { QuizQuestionFile } from "../../../model/questionTypes/file";
import type { DragEvent } from "react";
import type { UploadFileType } from "@model/questionTypes/file";
import { enqueueSnackbar } from "notistack";
import { sendAnswer, sendFile } from "@api/quizRelase";
import Info from "@icons/Info";
import type { UploadFileType } from "@model/questionTypes/file";
import { useQuizData } from "@utils/hooks/useQuizData";
import { enqueueSnackbar } from "notistack";
import type { DragEvent } from "react";
import { useState, type ChangeEvent } from "react";
import { useRootContainerSize } from "../../../contexts/RootContainerWidthContext";
import type { QuizQuestionFile } from "../../../model/questionTypes/file";
type FileProps = {
currentQuestion: QuizQuestionFile;
@ -123,7 +125,7 @@ export const File = ({ currentQuestion }: FileProps) => {
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<HTMLInputElement>) => {
const file = target.files?.[0];
if (file) {

@ -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 (
<Box>

@ -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]);

@ -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

@ -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(

@ -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<Theme>;
value?: moment.Moment;
onChange?: (value: string | null) => void;
label?: string;
sx?: SxProps<Theme>;
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 (
<Box
sx={{
...sx,
}}
>
{label && (
<Typography
sx={{
fontWeight: 500,
fontSize: "16px",
lineHeight: "20px",
color: "#4D4D4D",
mb: "10px",
}}
return (
<Box
sx={{
...sx,
}}
>
{label}
</Typography>
)}
<DatePicker
//@ts-ignore
value={value._d}
onChange={onChange}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
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",
},
},
}}
/>
</Box>
);
{label && (
<Typography
sx={{
fontWeight: 500,
fontSize: "16px",
lineHeight: "20px",
color: "#4D4D4D",
mb: "10px",
}}
>
{label}
</Typography>
)}
<DatePicker
//@ts-ignore
value={value._d}
onChange={onChange}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
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",
},
},
}}
/>
</Box>
);
}