From 037adc12a344a28bcfcf027d028a027edda81841 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Thu, 2 May 2024 21:51:05 +0300 Subject: [PATCH 01/76] add side widget button color param --- src/widgets/side/QuizSideButton.tsx | 4 +++- src/widgets/side/SideWidget.tsx | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index ad3ea43..f08bf92 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -10,9 +10,10 @@ const PADDING = 10; interface Props { quizId: string; position: "left" | "right"; + buttonColor?: string; } -export default function QuizSideButton({ quizId, position }: Props) { +export default function QuizSideButton({ quizId, position, buttonColor }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); return createPortal( @@ -53,6 +54,7 @@ export default function QuizSideButton({ quizId, position }: Props) { position: "fixed", height: "70px", width: `calc(min(calc(100% - ${PADDING * 2}px), 600px))`, + color: buttonColor, }, position === "left" && { bottom: PADDING, diff --git a/src/widgets/side/SideWidget.tsx b/src/widgets/side/SideWidget.tsx index f42f47b..1d8192e 100644 --- a/src/widgets/side/SideWidget.tsx +++ b/src/widgets/side/SideWidget.tsx @@ -7,7 +7,9 @@ export class SideWidget { root: Root | undefined; element = document.createElement("div"); - constructor({ quizId, position }: ComponentPropsWithoutRef) { + constructor({ quizId, position, buttonColor }: ComponentPropsWithoutRef & { + buttonColor?: string; + }) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); @@ -17,6 +19,7 @@ export class SideWidget { ); } From d46163a4ce8824d6f5bf081bae9b7fe4ee167326 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 3 May 2024 21:42:28 +0300 Subject: [PATCH 02/76] add component and path for widget development --- src/WidgetDev.tsx | 64 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.tsx | 27 +++++++++++++++++--- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 src/WidgetDev.tsx diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx new file mode 100644 index 0000000..9159934 --- /dev/null +++ b/src/WidgetDev.tsx @@ -0,0 +1,64 @@ +import lightTheme from "@/utils/themes/light"; +import { Box, ThemeProvider, Typography } from "@mui/material"; +import { useEffect, useRef } from "react"; +import { SideWidget } from "./widgets"; + + +const widgetProps: ConstructorParameters[0] = { + quizId: "3c49550d-8c77-4788-bc2d-42586a261514", + position: "right", +}; + +export default function WidgetDev() { + const widgetRef = useRef(null); + + useEffect(() => { + if (!widgetRef.current) { + widgetRef.current = new SideWidget(widgetProps); + } else { + widgetRef.current.render(widgetProps); + } + }); + + return ( + + + + + + + + + + + + + + + + + + + + ); +} + +const lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qu"; + +function Lorem() { + + return ( + + {lorem} + + ); +} diff --git a/src/main.tsx b/src/main.tsx index f018768..81eb653 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,10 @@ -// import "https://markknol.github.io/console-log-viewer/console-log-viewer.js"; import { createRoot } from "react-dom/client"; import { RouterProvider, createBrowserRouter } from "react-router-dom"; import App from "./App"; +import { StrictMode, lazy } from "react"; -const router = createBrowserRouter([ +const routes = [ { path: "/", children: [ @@ -18,8 +18,27 @@ const router = createBrowserRouter([ }, ] } -]); +]; + +if (import.meta.env.DEV) { + const WidgetDev = lazy(() => import("./WidgetDev")); + + routes[0].children.push({ + path: "widgetdev", + element: ( + + + + ) + }); +} + +const router = createBrowserRouter(routes); const root = createRoot(document.getElementById("root")!); -root.render(); +root.render( + + + +); From 82b6d3708021b81a95ccd7ea5ce639ecd12bb469 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 3 May 2024 21:46:14 +0300 Subject: [PATCH 03/76] add side widget class render method --- src/widgets/side/SideWidget.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/widgets/side/SideWidget.tsx b/src/widgets/side/SideWidget.tsx index 1d8192e..2c7c4d3 100644 --- a/src/widgets/side/SideWidget.tsx +++ b/src/widgets/side/SideWidget.tsx @@ -3,25 +3,23 @@ import QuizSideButton from "./QuizSideButton"; import { ComponentPropsWithoutRef } from "react"; +type Props = ComponentPropsWithoutRef; + export class SideWidget { root: Root | undefined; element = document.createElement("div"); - constructor({ quizId, position, buttonColor }: ComponentPropsWithoutRef & { - buttonColor?: string; - }) { + constructor(props: Props) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); this.root = createRoot(this.element); - this.root.render( - - ); + this.render(props); + } + + render(props: Props) { + this.root?.render(); } destroy() { From 79674fedbb6745b1e9a306d0cabfc0411585e0a3 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 3 May 2024 22:30:38 +0300 Subject: [PATCH 04/76] use QuizDialog in side widget add QuizDialog close button --- src/widgets/QuizDialog.tsx | 61 +++++++++++++++--- src/widgets/side/QuizSideButton.tsx | 99 +++++++++++++---------------- 2 files changed, 96 insertions(+), 64 deletions(-) diff --git a/src/widgets/QuizDialog.tsx b/src/widgets/QuizDialog.tsx index 44cf2eb..e2c5517 100644 --- a/src/widgets/QuizDialog.tsx +++ b/src/widgets/QuizDialog.tsx @@ -1,28 +1,52 @@ import QuizAnswerer from "@/components/QuizAnswerer"; -import { Dialog } from "@mui/material"; +import CloseIcon from '@mui/icons-material/Close'; +import { Dialog, IconButton, Slide, SlideProps, SxProps, Theme } from "@mui/material"; +import { forwardRef } from "react"; +const SlideTransition = forwardRef((props, ref) => { + return ( + + ); +}); + interface Props { open?: boolean; quizId: string; + paperSx?: SxProps; + hideBackdrop?: boolean; + disableScrollLock?: boolean; onClose?: () => void; } -export default function QuizDialog({ open = true, quizId, onClose }: Props) { +export default function QuizDialog({ + open = true, + quizId, + paperSx = [], + hideBackdrop, + disableScrollLock, + onClose +}: Props) { return ( + + + ); } diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index f08bf92..c8d7d4c 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -1,8 +1,8 @@ -import { QuizAnswerer } from "@/index"; import lightTheme from "@/utils/themes/light"; -import { Box, Button, Grow, ThemeProvider } from "@mui/material"; +import { Button, ThemeProvider } from "@mui/material"; import { useState } from "react"; import { createPortal } from "react-dom"; +import QuizDialog from "../QuizDialog"; const PADDING = 10; @@ -10,65 +10,54 @@ const PADDING = 10; interface Props { quizId: string; position: "left" | "right"; - buttonColor?: string; + buttonBackgroundColor?: string; } -export default function QuizSideButton({ quizId, position, buttonColor }: Props) { +export default function QuizSideButton({ quizId, position, buttonBackgroundColor }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); return createPortal( - {isQuizShown ? ( - - - - - - ) : ( - - )} + setIsQuizShown(false)} + hideBackdrop + disableScrollLock + paperSx={{ + position: "absolute", + bottom: PADDING, + right: position === "right" ? PADDING : undefined, + left: position === "left" ? PADDING : undefined, + height: `calc(min(calc(100% - ${PADDING * 2}px), 800px))`, + width: `calc(min(calc(100% - ${PADDING * 2}px), 600px))`, + m: 0, + }} + /> + , document.body ); From ffc8b5e9cb2c4afa6f05cad3203a1997f6c76d16 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 4 May 2024 15:24:59 +0300 Subject: [PATCH 05/76] minor fix --- src/main.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index 81eb653..376161d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,10 @@ import { createRoot } from "react-dom/client"; -import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { RouteObject, RouterProvider, createBrowserRouter } from "react-router-dom"; import App from "./App"; import { StrictMode, lazy } from "react"; -const routes = [ +const routes: RouteObject[] = [ { path: "/", children: [ @@ -23,13 +23,9 @@ const routes = [ if (import.meta.env.DEV) { const WidgetDev = lazy(() => import("./WidgetDev")); - routes[0].children.push({ + routes[0].children?.push({ path: "widgetdev", - element: ( - - - - ) + element: , }); } From b421e6eed327ed212292a8b1146abfa9f05868f3 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 4 May 2024 15:53:56 +0300 Subject: [PATCH 06/76] quiz is fullscreen when viewport width is small --- src/widgets/side/QuizSideButton.tsx | 32 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index c8d7d4c..bb39a17 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -1,5 +1,5 @@ import lightTheme from "@/utils/themes/light"; -import { Button, ThemeProvider } from "@mui/material"; +import { Button, ThemeProvider, useMediaQuery } from "@mui/material"; import { useState } from "react"; import { createPortal } from "react-dom"; import QuizDialog from "../QuizDialog"; @@ -15,6 +15,7 @@ interface Props { export default function QuizSideButton({ quizId, position, buttonBackgroundColor }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); + const isMobile = useMediaQuery("(max-width: 600px)"); return createPortal( @@ -24,15 +25,26 @@ export default function QuizSideButton({ quizId, position, buttonBackgroundColor onClose={() => setIsQuizShown(false)} hideBackdrop disableScrollLock - paperSx={{ - position: "absolute", - bottom: PADDING, - right: position === "right" ? PADDING : undefined, - left: position === "left" ? PADDING : undefined, - height: `calc(min(calc(100% - ${PADDING * 2}px), 800px))`, - width: `calc(min(calc(100% - ${PADDING * 2}px), 600px))`, - m: 0, - }} + paperSx={[ + { + m: 0, + }, + !isMobile && { + position: "absolute", + bottom: PADDING, + right: position === "right" ? PADDING : undefined, + left: position === "left" ? PADDING : undefined, + height: `calc(min(calc(100% - ${PADDING * 2}px), 800px))`, + width: `calc(min(calc(100% - ${PADDING * 2}px), 600px))`, + }, + isMobile && { + position: "relative", + height: "100%", + maxHeight: "100%", + width: "100%", + borderRadius: 0, + }, + ]} /> , From a18dbf00222037e82dff501925cba06a487460f5 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 4 May 2024 19:47:17 +0300 Subject: [PATCH 11/76] add apology page background color --- lib/components/ViewPublicationPage/ApologyPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/components/ViewPublicationPage/ApologyPage.tsx b/lib/components/ViewPublicationPage/ApologyPage.tsx index 1a983f2..f869827 100644 --- a/lib/components/ViewPublicationPage/ApologyPage.tsx +++ b/lib/components/ViewPublicationPage/ApologyPage.tsx @@ -19,6 +19,7 @@ export const ApologyPage = ({ error }: Props) => { alignItems: "center", justifyContent: "center", height: "100%", + backgroundColor: "#F2F3F7", }} > Date: Sat, 4 May 2024 19:47:47 +0300 Subject: [PATCH 12/76] hide side widget button flash animation if quiz is completed --- src/widgets/shared/RunningStripe.tsx | 1 + src/widgets/shared/useQuizCompletionStatus.ts | 17 +++++++++++++++++ src/widgets/side/QuizSideButton.tsx | 4 +++- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/widgets/shared/useQuizCompletionStatus.ts diff --git a/src/widgets/shared/RunningStripe.tsx b/src/widgets/shared/RunningStripe.tsx index b1b31df..7b526c4 100644 --- a/src/widgets/shared/RunningStripe.tsx +++ b/src/widgets/shared/RunningStripe.tsx @@ -9,6 +9,7 @@ export default function RunningStripe({ sx = [] }: Props) { return ( { + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + + if ( + typeof sessions[quizId] === "number" + && Date.now() - sessions[quizId] < 86400000 + ) { + return true; + } + + return false; + }, [quizId]); +} diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index e110c9e..7f563ec 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -4,6 +4,7 @@ import { useState } from "react"; import { createPortal } from "react-dom"; import QuizDialog from "../shared/QuizDialog"; import RunningStripe from "../shared/RunningStripe"; +import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; const PADDING = 10; @@ -21,6 +22,7 @@ interface Props { export default function QuizSideButton({ quizId, position, buttonBackgroundColor, dimensions, fullScreen = false }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); const isMobile = useMediaQuery("(max-width: 600px)"); + const isQuizCompleted = useQuizCompletionStatus(quizId); return createPortal( @@ -78,7 +80,7 @@ export default function QuizSideButton({ quizId, position, buttonBackgroundColor }, ]} > - + {!isQuizCompleted && } Пройти квиз , From 8c1b6d97efe02bd0d2b90c96fdbdb02561d46159 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 4 May 2024 19:58:01 +0300 Subject: [PATCH 13/76] add side widget button text color param --- src/widgets/side/QuizSideButton.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index 7f563ec..e911c20 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -15,11 +15,12 @@ interface Props { quizId: string; position: "left" | "right"; buttonBackgroundColor?: string; + buttonTextColor?: string; dimensions?: { width: string; height: string; }; fullScreen?: boolean; } -export default function QuizSideButton({ quizId, position, buttonBackgroundColor, dimensions, fullScreen = false }: Props) { +export default function QuizSideButton({ quizId, position, buttonBackgroundColor, buttonTextColor, dimensions, fullScreen = false }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); const isMobile = useMediaQuery("(max-width: 600px)"); const isQuizCompleted = useQuizCompletionStatus(quizId); @@ -68,6 +69,7 @@ export default function QuizSideButton({ quizId, position, buttonBackgroundColor width: "600px", maxWidth: `calc(100% - ${PADDING * 2}px)`, backgroundColor: buttonBackgroundColor, + color: buttonTextColor, overflow: "hidden", }, position === "left" && { From 9f5ec6653374f83ab0a487c2d3798c6102ab0433 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 7 May 2024 13:09:43 +0300 Subject: [PATCH 14/76] side widget: disable flash on quiz opening --- src/widgets/side/QuizSideButton.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index e911c20..b825c66 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -24,6 +24,12 @@ export default function QuizSideButton({ quizId, position, buttonBackgroundColor const [isQuizShown, setIsQuizShown] = useState(false); const isMobile = useMediaQuery("(max-width: 600px)"); const isQuizCompleted = useQuizCompletionStatus(quizId); + const [isFlashEnabled, setIsFlashEnabled] = useState(true); + + function openQuiz() { + setIsQuizShown(true); + setIsFlashEnabled(false); + } return createPortal( @@ -37,7 +43,7 @@ export default function QuizSideButton({ quizId, position, buttonBackgroundColor { m: 0, }, - (!isMobile && !fullScreen) && { + !(isMobile || fullScreen) && { position: "absolute", bottom: PADDING, right: position === "right" ? PADDING : undefined, @@ -59,7 +65,7 @@ export default function QuizSideButton({ quizId, position, buttonBackgroundColor , From 0ffa3585857090751a4995b0918ee2566281864e Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 7 May 2024 13:12:42 +0300 Subject: [PATCH 15/76] side widget: add enable button flash param --- src/widgets/side/QuizSideButton.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index b825c66..6ea447c 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -18,13 +18,22 @@ interface Props { buttonTextColor?: string; dimensions?: { width: string; height: string; }; fullScreen?: boolean; + buttonFlash?: boolean; } -export default function QuizSideButton({ quizId, position, buttonBackgroundColor, buttonTextColor, dimensions, fullScreen = false }: Props) { +export default function QuizSideButton({ + quizId, + position, + buttonBackgroundColor, + buttonTextColor, + dimensions, + fullScreen = false, + buttonFlash = false, +}: Props) { const [isQuizShown, setIsQuizShown] = useState(false); const isMobile = useMediaQuery("(max-width: 600px)"); const isQuizCompleted = useQuizCompletionStatus(quizId); - const [isFlashEnabled, setIsFlashEnabled] = useState(true); + const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); function openQuiz() { setIsQuizShown(true); From 41e02c82ef598999fac52f929f71bc4c262b2e60 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Wed, 8 May 2024 14:23:15 +0300 Subject: [PATCH 16/76] side widget: add auto open time and hide on mobile params --- src/widgets/shared/useAutoOpenTimer.ts | 18 +++++++ src/widgets/side/QuizSideButton.tsx | 70 +++++++++++++++----------- 2 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 src/widgets/shared/useAutoOpenTimer.ts diff --git a/src/widgets/shared/useAutoOpenTimer.ts b/src/widgets/shared/useAutoOpenTimer.ts new file mode 100644 index 0000000..1eb839e --- /dev/null +++ b/src/widgets/shared/useAutoOpenTimer.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from "react"; + + +export function useAutoOpenTimer(autoOpenTime: number) { + const [isWidgetHidden, setIsWidgetHidden] = useState(autoOpenTime ? true : false); + + useEffect(function setAutoOpenTimer() { + if (!autoOpenTime) return; + + const timeout = setTimeout(() => setIsWidgetHidden(false), autoOpenTime * 1000); + + return () => { + clearTimeout(timeout); + }; + }, [autoOpenTime]); + + return isWidgetHidden; +} diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index 6ea447c..8aa1aea 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -1,9 +1,10 @@ import lightTheme from "@/utils/themes/light"; -import { Button, ThemeProvider, useMediaQuery } from "@mui/material"; +import { Button, Fade, ThemeProvider, useMediaQuery } from "@mui/material"; import { useState } from "react"; import { createPortal } from "react-dom"; import QuizDialog from "../shared/QuizDialog"; import RunningStripe from "../shared/RunningStripe"; +import { useAutoOpenTimer } from "../shared/useAutoOpenTimer"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; @@ -19,6 +20,8 @@ interface Props { dimensions?: { width: string; height: string; }; fullScreen?: boolean; buttonFlash?: boolean; + autoOpenTime?: number; + hideOnMobile?: boolean; } export default function QuizSideButton({ @@ -29,17 +32,22 @@ export default function QuizSideButton({ dimensions, fullScreen = false, buttonFlash = false, + autoOpenTime = 0, + hideOnMobile = false, }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); const isMobile = useMediaQuery("(max-width: 600px)"); const isQuizCompleted = useQuizCompletionStatus(quizId); const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); + const isWidgetHidden = useAutoOpenTimer(autoOpenTime); function openQuiz() { setIsQuizShown(true); setIsFlashEnabled(false); } + if (hideOnMobile && isMobile) return null; + return createPortal( - + + + , document.body ); From 7749b00e56026930925f3cf44b6026606892cfa9 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Wed, 8 May 2024 15:12:40 +0300 Subject: [PATCH 17/76] side widget: add quiz auto open timer --- src/widgets/side/QuizSideButton.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index 8aa1aea..3e14cee 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -1,6 +1,6 @@ import lightTheme from "@/utils/themes/light"; import { Button, Fade, ThemeProvider, useMediaQuery } from "@mui/material"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import QuizDialog from "../shared/QuizDialog"; import RunningStripe from "../shared/RunningStripe"; @@ -20,7 +20,14 @@ interface Props { dimensions?: { width: string; height: string; }; fullScreen?: boolean; buttonFlash?: boolean; + /** + * Скрывать виджет первые X секунд + */ autoOpenTime?: number; + /** + * Открыть квиз через X секунд + */ + autoShowQuizTime?: number; hideOnMobile?: boolean; } @@ -33,6 +40,7 @@ export default function QuizSideButton({ fullScreen = false, buttonFlash = false, autoOpenTime = 0, + autoShowQuizTime = 0, hideOnMobile = false, }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); @@ -40,8 +48,22 @@ export default function QuizSideButton({ const isQuizCompleted = useQuizCompletionStatus(quizId); const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); const isWidgetHidden = useAutoOpenTimer(autoOpenTime); + const preventQuizAutoShowRef = useRef(false); + + useEffect(function setAutoShowQuizTimer() { + if (!autoShowQuizTime) return; + + const timeout = setTimeout(() => { + if (!preventQuizAutoShowRef.current) setIsQuizShown(true); + }, autoShowQuizTime * 1000); + + return () => { + clearTimeout(timeout); + }; + }, [autoShowQuizTime]); function openQuiz() { + preventQuizAutoShowRef.current = true; setIsQuizShown(true); setIsFlashEnabled(false); } From afb641c382f10bb5bcd3b2a2c6fbb46c0a1d38a5 Mon Sep 17 00:00:00 2001 From: aleksandr-raw <104529174+aleksandr-raw@users.noreply.github.com> Date: Wed, 8 May 2024 17:53:57 +0400 Subject: [PATCH 18/76] result page refactoring completed --- .../ViewPublicationPage/ResultForm.tsx | 525 +++++++++--------- 1 file changed, 268 insertions(+), 257 deletions(-) diff --git a/lib/components/ViewPublicationPage/ResultForm.tsx b/lib/components/ViewPublicationPage/ResultForm.tsx index 61b672d..f4c2224 100644 --- a/lib/components/ViewPublicationPage/ResultForm.tsx +++ b/lib/components/ViewPublicationPage/ResultForm.tsx @@ -1,275 +1,286 @@ -import { Box, Button, Link, Typography, useTheme } from "@mui/material"; +import {Box, Button, Link, Typography, useTheme} from "@mui/material"; -import { NameplateLogo } from "@icons/NameplateLogo"; +import {NameplateLogo} from "@icons/NameplateLogo"; import YoutubeEmbedIframe from "./tools/YoutubeEmbedIframe"; -import { useQuizData } from "@contexts/QuizDataContext"; -import { quizThemes } from "@utils/themes/Publication/themePublication"; -import { useRootContainerSize } from "../../contexts/RootContainerWidthContext"; -import type { QuizQuestionResult } from "../../model/questionTypes/result"; -import { useQuizViewStore } from "@/stores/quizView"; -import { DESIGN_LIST } from "@/utils/designList"; -import { useEffect } from "react"; +import {useQuizData} from "@contexts/QuizDataContext"; +import {quizThemes} from "@utils/themes/Publication/themePublication"; +import {useRootContainerSize} from "../../contexts/RootContainerWidthContext"; +import type {QuizQuestionResult} from "../../model/questionTypes/result"; +import {useQuizViewStore} from "@/stores/quizView"; +import {DESIGN_LIST} from "@/utils/designList"; +import {useEffect} from "react"; type ResultFormProps = { - resultQuestion: QuizQuestionResult; + resultQuestion: QuizQuestionResult; }; -export const ResultForm = ({ resultQuestion }: ResultFormProps) => { - const theme = useTheme(); - const isMobile = useRootContainerSize() < 650; - const isTablet = useRootContainerSize() < 1000; - const { settings, show_badge, quizId } = useQuizData(); - const setCurrentQuizStep = useQuizViewStore( - (state) => state.setCurrentQuizStep - ); - const spec = settings.cfg.spec; +export const ResultForm = ({resultQuestion}: ResultFormProps) => { + const theme = useTheme(); + const isMobile = useRootContainerSize() < 650; + const isTablet = useRootContainerSize() < 1100; + const {settings, show_badge, quizId} = useQuizData(); + const setCurrentQuizStep = useQuizViewStore( + (state) => state.setCurrentQuizStep, + ); + const spec = settings.cfg.spec; - useEffect(() => { - //@ts-ignore - let YM = window?.ym; - //@ts-ignore - let VP = window?._tmr; - if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) { - YM( - settings.cfg.yandexMetricNumber, - "reachGoal", - `penaquiz-result-{${resultQuestion.id}}` - ); - }; - if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) { - VP.push({ - type: "reachGoal", - id: settings.cfg.vkMetricNumber, - goal: `penaquiz-result-{${resultQuestion.id}}` - }); - }; - }, []) + useEffect(() => { + //@ts-ignore + const YM = window?.ym; + //@ts-ignore + const VP = window?._tmr; + if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) { + YM( + settings.cfg.yandexMetricNumber, + "reachGoal", + `penaquiz-result-{${resultQuestion.id}}`, + ); + } + if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) { + VP.push({ + type: "reachGoal", + id: settings.cfg.vkMetricNumber, + goal: `penaquiz-result-{${resultQuestion.id}}`, + }); + } + }, []); - return ( - - + return ( - - Ваш результат: - - - - {!resultQuestion?.content.useImage && - resultQuestion.content.video && ( - - )} - {resultQuestion?.content.useImage && resultQuestion.content.back && ( - + + + + Ваш результат: + + + + {!resultQuestion?.content.useImage && + resultQuestion.content.video && ( + + )} + {resultQuestion?.content.useImage && + resultQuestion.content.back && ( + + resultImage + + )} + {resultQuestion.description !== "" && + resultQuestion.description !== " " && ( + + {resultQuestion.description} + + )} + + + {resultQuestion.title} + + + {resultQuestion.content.text !== "" && + resultQuestion.content.text !== " " && ( + + {resultQuestion.content.text} + + )} + + + {show_badge && ( + + + + )} + + {settings.cfg.resultInfo.showResultForm === "before" && + !settings.cfg.score && ( + + )} + {settings.cfg.resultInfo.showResultForm === "after" && + resultQuestion.content.redirect && ( + + )} + - )} - {resultQuestion.description !== "" && - resultQuestion.description !== " " && ( - - {resultQuestion.description} - - )} - - - {resultQuestion.title} - - - {resultQuestion.content.text !== "" && - resultQuestion.content.text !== " " && ( - - {resultQuestion.content.text} - - )} - - - - {show_badge && ( - - - - - )} - - - - {settings.cfg.resultInfo.showResultForm === "before" && - !settings.cfg.score && ( - - )} - {settings.cfg.resultInfo.showResultForm === "after" && - resultQuestion.content.redirect && ( - - )} - - - - - ); + ); }; From 8436c4668eab01106a3df6dbddc844560989ce2e Mon Sep 17 00:00:00 2001 From: aleksandr-raw <104529174+aleksandr-raw@users.noreply.github.com> Date: Wed, 8 May 2024 19:00:12 +0400 Subject: [PATCH 19/76] completed with refactoring contact form --- .../ContactForm/ContactForm.tsx | 612 +++++++++--------- .../ContactTextBlock/ContactTextBlock.tsx | 4 +- .../CountrySelector/CountrySelector.tsx | 7 +- 3 files changed, 319 insertions(+), 304 deletions(-) diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx index 2e0c454..15a0926 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx @@ -13,346 +13,358 @@ import {quizThemes} from "@utils/themes/Publication/themePublication.ts"; import {enqueueSnackbar} from "notistack"; import {useRootContainerSize} from "@contexts/RootContainerWidthContext.ts"; import { - FormContactFieldData, - FormContactFieldName, + FormContactFieldData, + FormContactFieldName, } from "@model/settingsData.ts"; import { - Inputs + Inputs } from "@/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx"; import {EMAIL_REGEXP} from "@utils/emailRegexp.tsx"; import { - ContactTextBlock + ContactTextBlock } from "@/components/ViewPublicationPage/ContactForm/ContactTextBlock/ContactTextBlock.tsx"; type Props = { - currentQuestion: AnyTypedQuizQuestion; - onShowResult: () => void; + currentQuestion: AnyTypedQuizQuestion; + onShowResult: () => void; }; -export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { - const theme = useTheme(); - const { settings, questions, quizId, show_badge, preview } = useQuizData(); +export const ContactForm = ({currentQuestion, onShowResult}: Props) => { + const theme = useTheme(); + const {settings, questions, quizId, show_badge, preview} = useQuizData(); - const [ready, setReady] = useState(false); - const [name, setName] = useState(""); - const [email, setEmail] = useState(""); - const [phone, setPhone] = useState(""); - const [text, setText] = useState(""); - const [adress, setAdress] = useState(""); - const [screenHeight, setScreenHeight] = useState(window.innerHeight); + const [ready, setReady] = useState(false); + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [phone, setPhone] = useState(""); + const [text, setText] = useState(""); + const [adress, setAdress] = useState(""); + const [screenHeight, setScreenHeight] = useState(window.innerHeight); - const fireOnce = useRef(true); - const [fire, setFire] = useState(false); - const isMobile = useRootContainerSize() < 850; - const isTablet = useRootContainerSize() < 1000; + const fireOnce = useRef(true); + const [fire, setFire] = useState(false); + const isMobile = useRootContainerSize() < 850; + const isTablet = useRootContainerSize() < 1000; - useEffect(() => { - function handleResize() { - setScreenHeight(window.innerHeight); - } - - window.addEventListener("resize", handleResize); - - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); - - const resultQuestion = - currentQuestion.type === "result" - ? currentQuestion - : questions.find((question): question is QuizQuestionResult => { - if (settings?.cfg.haveRoot) { - return ( - question.type === "result" && - question.content.rule.parentId === currentQuestion.content.id - ); - } else { - return ( - question.type === "result" && - question.content.rule.parentId === "line" - ); + useEffect(() => { + function handleResize() { + setScreenHeight(window.innerHeight); } - }); - if (!resultQuestion) throw new Error("Result question not found"); + window.addEventListener("resize", handleResize); - const inputHC = async () => { - const FC = settings.cfg.formContact.fields || settings.cfg.formContact; - const body: SendFCParams["body"] = {}; - if (name.length > 0) body.name = name; - if (email.length > 0) body.email = email; - if (phone.length > 0) body.phone = phone; - if (adress.length > 0) body.address = adress; - if (text.length > 0) body.customs = { [FC.text.text || "Фамилия"]: text }; + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); - if (Object.keys(body).length > 0) { - try { - await sendFC({ - questionId: currentQuestion.id, - body: body, - qid: quizId, - preview, - }); + const resultQuestion = + currentQuestion.type === "result" + ? currentQuestion + : questions.find((question): question is QuizQuestionResult => { + if (settings?.cfg.haveRoot) { + return ( + question.type === "result" && + question.content.rule.parentId === currentQuestion.content.id + ); + } else { + return ( + question.type === "result" && + question.content.rule.parentId === "line" + ); + } + }); - const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - localStorage.setItem( - "sessions", - JSON.stringify({ ...sessions, [quizId]: new Date().getTime() }) - ); - } catch (e) { - enqueueSnackbar("ответ не был засчитан"); - } - } - }; + if (!resultQuestion) throw new Error("Result question not found"); - const FCcopy: Record = - settings.cfg.formContact.fields || settings.cfg.formContact; + const inputHC = async () => { + const FC = settings.cfg.formContact.fields || settings.cfg.formContact; + const body: SendFCParams["body"] = {}; + if (name.length > 0) body.name = name; + if (email.length > 0) body.email = email; + if (phone.length > 0) body.phone = phone; + if (adress.length > 0) body.address = adress; + if (text.length > 0) body.customs = {[FC.text.text || "Фамилия"]: text}; - const filteredFC: Partial< - Record - > = {}; - for (const i in FCcopy) { - const field = FCcopy[i as keyof typeof FCcopy]; - if (field.used) { - filteredFC[i as FormContactFieldName] = field; - } - } + if (Object.keys(body).length > 0) { + try { + await sendFC({ + questionId: currentQuestion.id, + body: body, + qid: quizId, + preview, + }); - async function handleShowResultsClick() { - const FC = settings.cfg.formContact.fields; - if (FC["email"].used !== EMAIL_REGEXP.test(email)) { - return enqueueSnackbar("введена некорректная почта"); + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + localStorage.setItem( + "sessions", + JSON.stringify({ + ...sessions, + [quizId]: new Date().getTime() + }) + ); + } catch (e) { + enqueueSnackbar("ответ не был засчитан"); + } + } + }; + + const FCcopy: Record = + settings.cfg.formContact.fields || settings.cfg.formContact; + + const filteredFC: Partial< + Record + > = {}; + for (const i in FCcopy) { + const field = FCcopy[i as keyof typeof FCcopy]; + if (field.used) { + filteredFC[i as FormContactFieldName] = field; + } } - if (fireOnce.current) { - if ( - name.length === 0 && - email.length === 0 && - phone.length === 0 && - text.length === 0 && - adress.length === 0 - ) - return enqueueSnackbar("Пожалуйста, заполните поля"); + async function handleShowResultsClick() { + const FC = settings.cfg.formContact.fields; + if (FC["email"].used !== EMAIL_REGEXP.test(email)) { + return enqueueSnackbar("введена некорректная почта"); + } - //почта валидна, хоть одно поле заполнено - setFire(true); - try { - await inputHC(); - fireOnce.current = false; - const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - sessions[quizId] = Date.now(); - localStorage.setItem("sessions", JSON.stringify(sessions)); + if (fireOnce.current) { + if ( + name.length === 0 && + email.length === 0 && + phone.length === 0 && + text.length === 0 && + adress.length === 0 + ) + return enqueueSnackbar("Пожалуйста, заполните поля"); + //почта валидна, хоть одно поле заполнено + setFire(true); + try { + await inputHC(); + fireOnce.current = false; + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + sessions[quizId] = Date.now(); + localStorage.setItem("sessions", JSON.stringify(sessions)); + + //@ts-ignore + const YM = window?.ym; + //@ts-ignore + const VP = window?._tmr; + if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) { + YM( + settings.cfg.yandexMetricNumber, + "reachGoal", + "penaquiz-contacts" + ); + } + if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) { + VP.push({ + type: "reachGoal", + id: settings.cfg.vkMetricNumber, + goal: "penaquiz-contacts" + }); + } + } catch (e) { + enqueueSnackbar("повторите попытку позже"); + } + if (settings.cfg.resultInfo.showResultForm === "after") { + onShowResult(); + } + } + + setFire(false); + } + + useEffect(() => { //@ts-ignore const YM = window?.ym; //@ts-ignore const VP = window?._tmr; if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) { - YM( - settings.cfg.yandexMetricNumber, - "reachGoal", - "penaquiz-contacts" - ); + YM( + settings.cfg.yandexMetricNumber, + "reachGoal", + "penaquiz-form" + ); } if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) { - VP.push({ - type: "reachGoal", - id: settings.cfg.vkMetricNumber, - goal: "penaquiz-contacts" - }); + VP.push({ + type: "reachGoal", + id: settings.cfg.vkMetricNumber, + goal: "penaquiz-form" + }); } - } catch (e) { - enqueueSnackbar("повторите попытку позже"); - } - if (settings.cfg.resultInfo.showResultForm === "after") { - onShowResult(); - } - } + }, []) - setFire(false); - } - useEffect(() => { - //@ts-ignore - const YM = window?.ym; - //@ts-ignore - const VP = window?._tmr; - if (YM !== undefined && settings.cfg.yandexMetricNumber !== undefined) { - YM( - settings.cfg.yandexMetricNumber, - "reachGoal", - "penaquiz-form" - ); - } - if (VP !== undefined && settings.cfg.vkMetricNumber !== undefined) { - VP.push({ - type: "reachGoal", - id: settings.cfg.vkMetricNumber, - goal: "penaquiz-form" - }); - } - }, []) - - return ( - 500 ? "100%" : "auto", - overflow: "auto", - "&::-webkit-scrollbar": { - width: "0", - display: "none", - msOverflowStyle: "none", - }, - scrollbarWidth: "none", - msOverflowStyle: "none", - backgroundPosition: "center", - backgroundSize: "cover", - backgroundImage: - settings.cfg.design && !isMobile - ? quizThemes[settings.cfg.theme].isLight - ? `url(${DESIGN_LIST[settings.cfg.theme]})` - : `linear-gradient(90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%), url(${DESIGN_LIST[settings.cfg.theme] - })` - : null, - }} - > - - - - - - - - - { - setReady(target.checked); - }} - checked={ready} - colorIcon={theme.palette.primary.main} - sx={{ marginRight: "0" }} - /> - - С  - - Положением об обработке персональных данных{" "} - -  и  - - {" "} - Политикой конфиденциальности{" "} - -  ознакомлен - - - - - - {show_badge && ( - + - - - )} + justifyContent: "space-between", + flexDirection: "column", + backgroundColor: theme.palette.background.default, + height: "auto", + }}> + + + + + + { + setReady(target.checked); + }} + checked={ready} + colorIcon={theme.palette.primary.main} + sx={{marginRight: "0"}} + /> + + С  + + Положением об обработке персональных + данных{" "} + +  и  + + {" "} + Политикой конфиденциальности{" "} + +  ознакомлен + + + + + + {show_badge && ( + + + + )} + + - - - ); + ); }; diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/ContactTextBlock.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/ContactTextBlock.tsx index b8263e6..e76fda9 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/ContactTextBlock.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/ContactTextBlock.tsx @@ -14,7 +14,7 @@ export const ContactTextBlock: FC = ({settings}) => { return ( = ({settings}) => { > >; } -export const CountrySelector:FC = ({setMask}) => { +export const CountrySelector: FC = ({setMask}) => { const theme = useTheme(); const [country, setCountry] = useState('RU'); @@ -24,6 +24,8 @@ export const CountrySelector:FC = ({setMask}) => { PaperProps: { style: { backgroundColor: theme.palette.background.default, + borderRadius: '12px', + scrollbarWidth: "none", }, }, }} @@ -55,7 +57,8 @@ export const CountrySelector:FC = ({setMask}) => { }} > {Object.keys(phoneMasksByCountry).map((countryCode) => { - return {phoneMasksByCountry[countryCode][0]} + return {phoneMasksByCountry[countryCode][0]} })} ); From cd972e493b060f58ac73a1ac353e38f25f624423 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Wed, 8 May 2024 20:50:18 +0300 Subject: [PATCH 20/76] add popup quiz features --- src/WidgetDev.tsx | 9 ++-- src/widgets/popup/PopupWidget.tsx | 23 +++++---- src/widgets/popup/QuizPopup.tsx | 78 +++++++++++++++++++++++++++++++ src/widgets/shared/QuizDialog.tsx | 3 +- 4 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 src/widgets/popup/QuizPopup.tsx diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx index 9159934..901713c 100644 --- a/src/WidgetDev.tsx +++ b/src/WidgetDev.tsx @@ -1,20 +1,19 @@ import lightTheme from "@/utils/themes/light"; import { Box, ThemeProvider, Typography } from "@mui/material"; import { useEffect, useRef } from "react"; -import { SideWidget } from "./widgets"; +import { PopupWidget as Widget } from "./widgets"; -const widgetProps: ConstructorParameters[0] = { +const widgetProps: ConstructorParameters[0] = { quizId: "3c49550d-8c77-4788-bc2d-42586a261514", - position: "right", }; export default function WidgetDev() { - const widgetRef = useRef(null); + const widgetRef = useRef(null); useEffect(() => { if (!widgetRef.current) { - widgetRef.current = new SideWidget(widgetProps); + widgetRef.current = new Widget(widgetProps); } else { widgetRef.current.render(widgetProps); } diff --git a/src/widgets/popup/PopupWidget.tsx b/src/widgets/popup/PopupWidget.tsx index eb490bd..d238eaa 100644 --- a/src/widgets/popup/PopupWidget.tsx +++ b/src/widgets/popup/PopupWidget.tsx @@ -1,26 +1,25 @@ import { Root, createRoot } from "react-dom/client"; -import QuizDialog from "../shared/QuizDialog"; +import { ComponentPropsWithoutRef } from "react"; +import QuizPopup from "./QuizPopup"; +type Props = ComponentPropsWithoutRef; + export class PopupWidget { root: Root | undefined; - element: HTMLDivElement; + element = document.createElement("div"); - constructor({ quizId }: { - quizId: string; - }) { - this.element = document.createElement("div"); + constructor(props: Props) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); this.root = createRoot(this.element); - this.root.render( - this.destroy()} - /> - ); + this.render(props); + } + + render(props: Props) { + this.root?.render(); } destroy() { diff --git a/src/widgets/popup/QuizPopup.tsx b/src/widgets/popup/QuizPopup.tsx new file mode 100644 index 0000000..f824328 --- /dev/null +++ b/src/widgets/popup/QuizPopup.tsx @@ -0,0 +1,78 @@ +import { useEffect, useRef, useState } from "react"; +import QuizDialog from "../shared/QuizDialog"; +import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; +import { useMediaQuery } from "@mui/material"; + + +const WIDGET_DEFAULT_WIDTH = "600px"; +const WIDGET_DEFAULT_HEIGHT = "80%"; + +interface Props { + quizId: string; + dimensions?: { width: string; height: string; }; + /** + * Открыть квиз через X секунд + */ + autoShowQuizTime?: number; + hideOnMobile?: boolean; + openOnLeaveAttempt?: boolean; +} + +export default function QuizPopup({ + quizId, + dimensions, + autoShowQuizTime = 0, + hideOnMobile = false, + openOnLeaveAttempt = false, +}: Props) { + const initialIsQuizShown = (autoShowQuizTime || openOnLeaveAttempt) ? false : true; + + const [isQuizShown, setIsQuizShown] = useState(initialIsQuizShown); + const isQuizCompleted = useQuizCompletionStatus(quizId); + const isMobile = useMediaQuery("(max-width: 600px)"); + const preventOpenOnLeaveAttemptRef = useRef(false); + + useEffect(function setAutoShowQuizTimer() { + if (!autoShowQuizTime || openOnLeaveAttempt) return; + + const timeout = setTimeout(() => { + setIsQuizShown(true); + }, autoShowQuizTime * 1000); + + return () => { + clearTimeout(timeout); + }; + }, [autoShowQuizTime, openOnLeaveAttempt]); + + useEffect(function attachLeaveListener() { + if (!openOnLeaveAttempt) return; + + const handleMouseLeave = () => { + if (!preventOpenOnLeaveAttemptRef.current) { + preventOpenOnLeaveAttemptRef.current = true; + setIsQuizShown(true); + } + }; + + document.addEventListener("mouseleave", handleMouseLeave); + + return () => { + document.removeEventListener("mouseleave", handleMouseLeave); + }; + }, [openOnLeaveAttempt]); + + if (isQuizCompleted) return null; + if (hideOnMobile && isMobile) return null; + + return ( + setIsQuizShown(false)} + paperSx={{ + width: dimensions?.width ?? WIDGET_DEFAULT_WIDTH, + height: dimensions?.height ?? WIDGET_DEFAULT_HEIGHT, + }} + /> + ); +} diff --git a/src/widgets/shared/QuizDialog.tsx b/src/widgets/shared/QuizDialog.tsx index 4429a3a..fa3895c 100644 --- a/src/widgets/shared/QuizDialog.tsx +++ b/src/widgets/shared/QuizDialog.tsx @@ -41,8 +41,9 @@ export default function QuizDialog({ { backgroundColor: "transparent", width: "calc(min(100%, max(70%, 700px)))", - height: "80%", maxWidth: "100%", + height: "80%", + maxHeight: "100%", m: "16px", }, ...(Array.isArray(paperSx) ? paperSx : [paperSx]) From 92d727b2e904f0cda7a4512f7c7a53815a8ed2a9 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Thu, 9 May 2024 14:27:54 +0300 Subject: [PATCH 21/76] add button widget features --- src/WidgetDev.tsx | 4 +- src/widgets/button/ButtonWidget.tsx | 37 ++++----- src/widgets/button/OpenQuizButton.tsx | 107 +++++++++++++++++++++++--- src/widgets/shared/RunningStripe.tsx | 2 +- 4 files changed, 120 insertions(+), 30 deletions(-) diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx index 901713c..98f50df 100644 --- a/src/WidgetDev.tsx +++ b/src/WidgetDev.tsx @@ -1,11 +1,12 @@ import lightTheme from "@/utils/themes/light"; import { Box, ThemeProvider, Typography } from "@mui/material"; import { useEffect, useRef } from "react"; -import { PopupWidget as Widget } from "./widgets"; +import { ButtonWidget as Widget } from "./widgets"; const widgetProps: ConstructorParameters[0] = { quizId: "3c49550d-8c77-4788-bc2d-42586a261514", + selector: "#widget-button", }; export default function WidgetDev() { @@ -28,6 +29,7 @@ export default function WidgetDev() { }} > + diff --git a/src/widgets/button/ButtonWidget.tsx b/src/widgets/button/ButtonWidget.tsx index 5482d18..a432749 100644 --- a/src/widgets/button/ButtonWidget.tsx +++ b/src/widgets/button/ButtonWidget.tsx @@ -1,25 +1,28 @@ +import { ComponentPropsWithoutRef } from "react"; import { Root, createRoot } from "react-dom/client"; +import { pollForSelector } from "../shared/pollForSelector"; import OpenQuizButton from "./OpenQuizButton"; import { createPortal } from "react-dom"; -import { pollForSelector } from "../shared/pollForSelector"; +type Props = ComponentPropsWithoutRef; + export class ButtonWidget { root: Root | undefined; - element = document.createElement("div"); - constructor({ quizId, selector, selectorPollingTimeLimit = 60 }: { - quizId: string; + constructor(props: Props & { selector: string; /** * In seconds, null - polling disabled */ selectorPollingTimeLimit?: number | null; }) { + const { selector, selectorPollingTimeLimit = 60 } = props; + const element = document.querySelector(selector); if (element) { this.root = createRoot(element); - this.root.render(); + this.render(props); return; } @@ -31,13 +34,16 @@ export class ButtonWidget { pollForSelector(selector, selectorPollingTimeLimit, (element) => { this.root = createRoot(element); - this.root.render(); + this.render(props); }); } + render(props: Props) { + this.root?.render(); + } + destroy() { if (this.root) this.root.unmount(); - this.element.remove(); } } @@ -45,22 +51,17 @@ export class ButtonWidgetFixed { root: Root | undefined; element = document.createElement("div"); - constructor({ quizId, side }: { - quizId: string; - side: "left" | "right"; - }) { + constructor(props: Props) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); this.root = createRoot(this.element); - this.root.render(createPortal( - , - document.body - )); + this.render(props); + } + + render(props: Props) { + this.root?.render(createPortal(, document.body)); } destroy() { diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index 08b35a8..72cc093 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -1,26 +1,108 @@ import lightTheme from "@/utils/themes/light"; -import { Button, ThemeProvider } from "@mui/material"; -import { useState } from "react"; +import { Button, ThemeProvider, useMediaQuery } from "@mui/material"; +import { useEffect, useRef, useState } from "react"; import QuizDialog from "../shared/QuizDialog"; +import RunningStripe from "../shared/RunningStripe"; +import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; +const WIDGET_DEFAULT_WIDTH = "600px"; +const WIDGET_DEFAULT_HEIGHT = "80%"; + interface Props { - fixedSide?: "left" | "right"; quizId: string; + fixedSide?: "left" | "right"; + dimensions?: { width: string; height: string; }; + /** + * Открыть квиз через X секунд + */ + autoShowQuizTime?: number; + hideOnMobile?: boolean; + openOnLeaveAttempt?: boolean; + buttonFlash?: boolean; + withShadow?: boolean; + rounded?: boolean; + buttonText?: string; + buttonTextColor?: string; + buttonBackgroundColor?: string; } -export default function OpenQuizButton({ quizId, fixedSide }: Props) { - const [isQuizDialogOpen, setIsQuizDialogOpen] = useState(false); +export default function OpenQuizButton({ + quizId, + fixedSide, + autoShowQuizTime = 0, + dimensions, + hideOnMobile, + openOnLeaveAttempt, + buttonFlash = false, + withShadow = false, + rounded = false, + buttonText = "Пройти квиз", + buttonTextColor, + buttonBackgroundColor, +}: Props) { + const isMobile = useMediaQuery("(max-width: 600px)"); + const [isQuizShown, setIsQuizShown] = useState(false); + const isQuizCompleted = useQuizCompletionStatus(quizId); + const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); + const preventQuizAutoShowRef = useRef(false); + const preventOpenOnLeaveAttemptRef = useRef(false); + + useEffect(function setAutoShowQuizTimer() { + if (!autoShowQuizTime || openOnLeaveAttempt) return; + + const timeout = setTimeout(() => { + setIsQuizShown(true); + }, autoShowQuizTime * 1000); + + return () => { + clearTimeout(timeout); + }; + }, [autoShowQuizTime, openOnLeaveAttempt]); + + useEffect(function attachLeaveListener() { + if (!openOnLeaveAttempt) return; + + const handleMouseLeave = () => { + if (!preventOpenOnLeaveAttemptRef.current) { + preventOpenOnLeaveAttemptRef.current = true; + setIsQuizShown(true); + } + }; + + document.addEventListener("mouseleave", handleMouseLeave); + + return () => { + document.removeEventListener("mouseleave", handleMouseLeave); + }; + }, [openOnLeaveAttempt]); + + function openQuiz() { + preventQuizAutoShowRef.current = true; + setIsQuizShown(true); + setIsFlashEnabled(false); + } + + if (hideOnMobile && isMobile) return null; return ( setIsQuizDialogOpen(false)} + onClose={() => setIsQuizShown(false)} + paperSx={{ + width: dimensions?.width ?? WIDGET_DEFAULT_WIDTH, + height: dimensions?.height ?? WIDGET_DEFAULT_HEIGHT, + }} /> ); diff --git a/src/widgets/shared/RunningStripe.tsx b/src/widgets/shared/RunningStripe.tsx index 7b526c4..0faf39a 100644 --- a/src/widgets/shared/RunningStripe.tsx +++ b/src/widgets/shared/RunningStripe.tsx @@ -20,7 +20,7 @@ export default function RunningStripe({ sx = [] }: Props) { transform: "rotate(-60deg)", "@keyframes runningStripe": { "0%": { - left: "-20%", + left: "-150px", opacity: 1, }, "25%, 100%": { From b8313765d37ba48d3403e50ddffd61823566af9d Mon Sep 17 00:00:00 2001 From: nflnkr Date: Thu, 9 May 2024 14:39:36 +0300 Subject: [PATCH 22/76] refactor autoShowQuizTime quiz widget param --- src/widgets/button/OpenQuizButton.tsx | 8 ++++---- src/widgets/popup/QuizPopup.tsx | 10 +++++----- src/widgets/side/QuizSideButton.tsx | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index 72cc093..aac0186 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -14,9 +14,9 @@ interface Props { fixedSide?: "left" | "right"; dimensions?: { width: string; height: string; }; /** - * Открыть квиз через X секунд + * Открыть квиз через X секунд, 0 - сразу */ - autoShowQuizTime?: number; + autoShowQuizTime?: number | null; hideOnMobile?: boolean; openOnLeaveAttempt?: boolean; buttonFlash?: boolean; @@ -30,7 +30,7 @@ interface Props { export default function OpenQuizButton({ quizId, fixedSide, - autoShowQuizTime = 0, + autoShowQuizTime = null, dimensions, hideOnMobile, openOnLeaveAttempt, @@ -49,7 +49,7 @@ export default function OpenQuizButton({ const preventOpenOnLeaveAttemptRef = useRef(false); useEffect(function setAutoShowQuizTimer() { - if (!autoShowQuizTime || openOnLeaveAttempt) return; + if (autoShowQuizTime === null || openOnLeaveAttempt) return; const timeout = setTimeout(() => { setIsQuizShown(true); diff --git a/src/widgets/popup/QuizPopup.tsx b/src/widgets/popup/QuizPopup.tsx index f824328..068068e 100644 --- a/src/widgets/popup/QuizPopup.tsx +++ b/src/widgets/popup/QuizPopup.tsx @@ -11,9 +11,9 @@ interface Props { quizId: string; dimensions?: { width: string; height: string; }; /** - * Открыть квиз через X секунд + * Открыть квиз через X секунд, 0 - сразу */ - autoShowQuizTime?: number; + autoShowQuizTime?: number | null; hideOnMobile?: boolean; openOnLeaveAttempt?: boolean; } @@ -21,11 +21,11 @@ interface Props { export default function QuizPopup({ quizId, dimensions, - autoShowQuizTime = 0, + autoShowQuizTime = null, hideOnMobile = false, openOnLeaveAttempt = false, }: Props) { - const initialIsQuizShown = (autoShowQuizTime || openOnLeaveAttempt) ? false : true; + const initialIsQuizShown = (autoShowQuizTime !== null || openOnLeaveAttempt) ? false : true; const [isQuizShown, setIsQuizShown] = useState(initialIsQuizShown); const isQuizCompleted = useQuizCompletionStatus(quizId); @@ -33,7 +33,7 @@ export default function QuizPopup({ const preventOpenOnLeaveAttemptRef = useRef(false); useEffect(function setAutoShowQuizTimer() { - if (!autoShowQuizTime || openOnLeaveAttempt) return; + if (autoShowQuizTime === null || openOnLeaveAttempt) return; const timeout = setTimeout(() => { setIsQuizShown(true); diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index 3e14cee..6ad6fe2 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -25,9 +25,9 @@ interface Props { */ autoOpenTime?: number; /** - * Открыть квиз через X секунд + * Открыть квиз через X секунд, 0 - сразу */ - autoShowQuizTime?: number; + autoShowQuizTime?: number | null; hideOnMobile?: boolean; } @@ -40,7 +40,7 @@ export default function QuizSideButton({ fullScreen = false, buttonFlash = false, autoOpenTime = 0, - autoShowQuizTime = 0, + autoShowQuizTime = null, hideOnMobile = false, }: Props) { const [isQuizShown, setIsQuizShown] = useState(false); @@ -51,7 +51,7 @@ export default function QuizSideButton({ const preventQuizAutoShowRef = useRef(false); useEffect(function setAutoShowQuizTimer() { - if (!autoShowQuizTime) return; + if (autoShowQuizTime === null) return; const timeout = setTimeout(() => { if (!preventQuizAutoShowRef.current) setIsQuizShown(true); From ceb1f1e7c29cd6e4f95981d0d2f7be917acbff40 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Thu, 9 May 2024 15:05:12 +0300 Subject: [PATCH 23/76] fix button widget params types --- src/widgets/button/ButtonWidget.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/widgets/button/ButtonWidget.tsx b/src/widgets/button/ButtonWidget.tsx index a432749..68965ea 100644 --- a/src/widgets/button/ButtonWidget.tsx +++ b/src/widgets/button/ButtonWidget.tsx @@ -5,12 +5,12 @@ import OpenQuizButton from "./OpenQuizButton"; import { createPortal } from "react-dom"; -type Props = ComponentPropsWithoutRef; +type ButtonWidgetProps = Omit, "fixedSide">; export class ButtonWidget { root: Root | undefined; - constructor(props: Props & { + constructor(props: ButtonWidgetProps & { selector: string; /** * In seconds, null - polling disabled @@ -38,7 +38,7 @@ export class ButtonWidget { }); } - render(props: Props) { + render(props: ButtonWidgetProps) { this.root?.render(); } @@ -47,11 +47,13 @@ export class ButtonWidget { } } +type ButtonWidgetFixedProps = Omit, "selector">; + export class ButtonWidgetFixed { root: Root | undefined; element = document.createElement("div"); - constructor(props: Props) { + constructor(props: ButtonWidgetFixedProps) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); @@ -60,7 +62,7 @@ export class ButtonWidgetFixed { this.render(props); } - render(props: Props) { + render(props: ButtonWidgetFixedProps) { this.root?.render(createPortal(, document.body)); } From ab6f58066e9530223baf393277632c3394fcedbf Mon Sep 17 00:00:00 2001 From: nflnkr Date: Thu, 9 May 2024 18:16:56 +0300 Subject: [PATCH 24/76] fix button widget types & minor --- src/widgets/button/ButtonWidget.tsx | 4 +++- src/widgets/button/OpenQuizButton.tsx | 2 +- src/widgets/side/QuizSideButton.tsx | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/widgets/button/ButtonWidget.tsx b/src/widgets/button/ButtonWidget.tsx index 68965ea..d322a3e 100644 --- a/src/widgets/button/ButtonWidget.tsx +++ b/src/widgets/button/ButtonWidget.tsx @@ -47,7 +47,9 @@ export class ButtonWidget { } } -type ButtonWidgetFixedProps = Omit, "selector">; +type ButtonWidgetFixedProps = Omit, "selector"> & { + fixedSide: "left" | "right"; +}; export class ButtonWidgetFixed { root: Root | undefined; diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index aac0186..fe28a2d 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -120,8 +120,8 @@ export default function OpenQuizButton({ }, ]} > + {buttonText} {!isQuizCompleted && isFlashEnabled && } - {buttonText} - {!isQuizCompleted && isFlashEnabled && } Пройти квиз + {!isQuizCompleted && isFlashEnabled && } , From 4e0bdb6f3f785ade5f8146c612a2977ca9300a12 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 10 May 2024 09:59:12 +0300 Subject: [PATCH 25/76] add banner widget features --- src/WidgetDev.tsx | 6 +- src/widgets/banner/BannerWidget.tsx | 15 +- src/widgets/banner/QuizBanner.tsx | 245 +++++++++++++++++++++------- src/widgets/shared/BannerIcon.tsx | 25 +++ 4 files changed, 225 insertions(+), 66 deletions(-) create mode 100644 src/widgets/shared/BannerIcon.tsx diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx index 98f50df..567736f 100644 --- a/src/WidgetDev.tsx +++ b/src/WidgetDev.tsx @@ -1,12 +1,14 @@ import lightTheme from "@/utils/themes/light"; import { Box, ThemeProvider, Typography } from "@mui/material"; import { useEffect, useRef } from "react"; -import { ButtonWidget as Widget } from "./widgets"; +import { BannerWidget as Widget } from "./widgets"; const widgetProps: ConstructorParameters[0] = { quizId: "3c49550d-8c77-4788-bc2d-42586a261514", - selector: "#widget-button", + position: "bottomright", + pulsation: true, + rounded: true, }; export default function WidgetDev() { diff --git a/src/widgets/banner/BannerWidget.tsx b/src/widgets/banner/BannerWidget.tsx index 3153d6f..f2e4ed1 100644 --- a/src/widgets/banner/BannerWidget.tsx +++ b/src/widgets/banner/BannerWidget.tsx @@ -3,21 +3,26 @@ import QuizBanner from "./QuizBanner"; import { ComponentPropsWithoutRef } from "react"; +type Props = Omit, "onClose">; + export class BannerWidget { root: Root | undefined; element = document.createElement("div"); - constructor({ quizId, position }: ComponentPropsWithoutRef) { + constructor(props: Props) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); this.root = createRoot(this.element); - this.root.render( + this.render(props); + } + + render(props: Props) { + this.root?.render( this.destroy()} + {...props} + onWidgetClose={() => this.destroy()} /> ); } diff --git a/src/widgets/banner/QuizBanner.tsx b/src/widgets/banner/QuizBanner.tsx index 7a81976..93a2a09 100644 --- a/src/widgets/banner/QuizBanner.tsx +++ b/src/widgets/banner/QuizBanner.tsx @@ -1,80 +1,207 @@ import lightTheme from "@/utils/themes/light"; import CloseIcon from '@mui/icons-material/Close'; -import { Box, Button, IconButton, ThemeProvider } from "@mui/material"; -import { useState } from "react"; +import { Box, Button, Fade, IconButton, ThemeProvider, Typography, useMediaQuery } from "@mui/material"; +import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import QuizDialog from "../shared/QuizDialog"; +import RunningStripe from "../shared/RunningStripe"; +import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; +import BannerIcon from "../shared/BannerIcon"; const PADDING = 10; interface Props { - position: "topleft" | "topright" | "bottomleft" | "bottomright"; quizId: string; - onClose: () => void; + position: "topleft" | "topright" | "bottomleft" | "bottomright"; + onWidgetClose?: () => void; + appealText?: string; + quizHeaderText?: string; + buttonTextColor?: string; + buttonBackgroundColor?: string; + /** + * Открыть квиз через X секунд, 0 - сразу + */ + autoShowQuizTime?: number | null; + openOnLeaveAttempt?: boolean; + buttonFlash?: boolean; + hideOnMobile?: boolean; + withShadow?: boolean; + rounded?: boolean; + bannerFullWidth?: boolean; + pulsation?: boolean; } -export default function QuizBanner({ quizId, position, onClose }: Props) { - const [isQuizDialogOpen, setIsQuizDialogOpen] = useState(false); +export default function QuizBanner({ + quizId, + position, + onWidgetClose, + appealText = "Пройти тест", + quizHeaderText = "Заголовок квиза", + buttonTextColor, + buttonBackgroundColor, + autoShowQuizTime = null, + openOnLeaveAttempt, + buttonFlash = false, + hideOnMobile, + withShadow = false, + rounded = false, + bannerFullWidth = false, + pulsation = false, +}: Props) { + const isMobile = useMediaQuery("(max-width: 600px)"); + const [isQuizShown, setIsQuizShown] = useState(false); + const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); + const isQuizCompleted = useQuizCompletionStatus(quizId); + const preventQuizAutoShowRef = useRef(false); + const preventOpenOnLeaveAttemptRef = useRef(false); + + useEffect(function setAutoShowQuizTimer() { + if (autoShowQuizTime === null || openOnLeaveAttempt) return; + + const timeout = setTimeout(() => { + setIsQuizShown(true); + }, autoShowQuizTime * 1000); + + return () => { + clearTimeout(timeout); + }; + }, [autoShowQuizTime, openOnLeaveAttempt]); + + useEffect(function attachLeaveListener() { + if (!openOnLeaveAttempt) return; + + const handleMouseLeave = () => { + if (!preventOpenOnLeaveAttemptRef.current) { + preventOpenOnLeaveAttemptRef.current = true; + setIsQuizShown(true); + } + }; + + document.addEventListener("mouseleave", handleMouseLeave); + + return () => { + document.removeEventListener("mouseleave", handleMouseLeave); + }; + }, [openOnLeaveAttempt]); + + function openQuiz() { + preventQuizAutoShowRef.current = true; + setIsQuizShown(true); + setIsFlashEnabled(false); + } + + if (hideOnMobile && isMobile) return null; return createPortal( - - - - - - + + + + + + setIsQuizDialogOpen(false)} + onClose={() => setIsQuizShown(false)} + disableScrollLock /> , document.body diff --git a/src/widgets/shared/BannerIcon.tsx b/src/widgets/shared/BannerIcon.tsx new file mode 100644 index 0000000..fd3b402 --- /dev/null +++ b/src/widgets/shared/BannerIcon.tsx @@ -0,0 +1,25 @@ +import { Box } from "@mui/material"; + + +export default function BannerIcon() { + + return ( + + + + + + + + + ); +} From aebf66eba4d86de9fd7bd1b1efb7ee607ff1d089 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 10 May 2024 14:51:29 +0300 Subject: [PATCH 26/76] fix banner widget close button --- src/WidgetDev.tsx | 2 +- src/widgets/banner/QuizBanner.tsx | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx index 567736f..7da8233 100644 --- a/src/WidgetDev.tsx +++ b/src/WidgetDev.tsx @@ -6,7 +6,7 @@ import { BannerWidget as Widget } from "./widgets"; const widgetProps: ConstructorParameters[0] = { quizId: "3c49550d-8c77-4788-bc2d-42586a261514", - position: "bottomright", + position: "bottomleft", pulsation: true, rounded: true, }; diff --git a/src/widgets/banner/QuizBanner.tsx b/src/widgets/banner/QuizBanner.tsx index 93a2a09..88427ba 100644 --- a/src/widgets/banner/QuizBanner.tsx +++ b/src/widgets/banner/QuizBanner.tsx @@ -1,12 +1,11 @@ import lightTheme from "@/utils/themes/light"; -import CloseIcon from '@mui/icons-material/Close'; import { Box, Button, Fade, IconButton, ThemeProvider, Typography, useMediaQuery } from "@mui/material"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; +import BannerIcon from "../shared/BannerIcon"; import QuizDialog from "../shared/QuizDialog"; import RunningStripe from "../shared/RunningStripe"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; -import BannerIcon from "../shared/BannerIcon"; const PADDING = 10; @@ -103,7 +102,7 @@ export default function QuizBanner({ position: "fixed", height: "120px", width: bannerFullWidth ? "100%" : "800px", - maxWidth: "100%", + maxWidth: `calc(100% - ${PADDING * 2}px)`, }, position === "topleft" && { top: bannerFullWidth ? 0 : PADDING, @@ -186,14 +185,18 @@ export default function QuizBanner({ position: "absolute", top: 0, right: 0, - p: 0, - width: "34px", - height: "34px", + p: "8px", + width: "44px", + height: "44px", borderRadius: "4px", - backgroundColor: "#333647", + ":hover": { + backgroundColor: "rgba(0, 0, 0, 0.3)", + }, }} > - + + + From 2375f81b48fec3d2a9c3dea0c610d75e3ed25e6b Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 13 May 2024 16:41:10 +0300 Subject: [PATCH 27/76] rename widget components dimensions prop to dialogDimensions --- src/WidgetDev.tsx | 12 ++++++++---- src/widgets/button/OpenQuizButton.tsx | 8 ++++---- src/widgets/popup/QuizPopup.tsx | 8 ++++---- src/widgets/side/QuizSideButton.tsx | 8 ++++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx index 7da8233..29dd6b3 100644 --- a/src/WidgetDev.tsx +++ b/src/WidgetDev.tsx @@ -1,14 +1,12 @@ import lightTheme from "@/utils/themes/light"; import { Box, ThemeProvider, Typography } from "@mui/material"; import { useEffect, useRef } from "react"; -import { BannerWidget as Widget } from "./widgets"; +import { ContainerWidget as Widget } from "./widgets"; const widgetProps: ConstructorParameters[0] = { quizId: "3c49550d-8c77-4788-bc2d-42586a261514", - position: "bottomleft", - pulsation: true, - rounded: true, + selector: "#widget-container", }; export default function WidgetDev() { @@ -32,6 +30,12 @@ export default function WidgetDev() { > + diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index fe28a2d..f60ba3a 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -12,7 +12,7 @@ const WIDGET_DEFAULT_HEIGHT = "80%"; interface Props { quizId: string; fixedSide?: "left" | "right"; - dimensions?: { width: string; height: string; }; + dialogDimensions?: { width: string; height: string; }; /** * Открыть квиз через X секунд, 0 - сразу */ @@ -31,7 +31,7 @@ export default function OpenQuizButton({ quizId, fixedSide, autoShowQuizTime = null, - dimensions, + dialogDimensions, hideOnMobile, openOnLeaveAttempt, buttonFlash = false, @@ -128,8 +128,8 @@ export default function OpenQuizButton({ quizId={quizId} onClose={() => setIsQuizShown(false)} paperSx={{ - width: dimensions?.width ?? WIDGET_DEFAULT_WIDTH, - height: dimensions?.height ?? WIDGET_DEFAULT_HEIGHT, + width: dialogDimensions?.width ?? WIDGET_DEFAULT_WIDTH, + height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, }} /> diff --git a/src/widgets/popup/QuizPopup.tsx b/src/widgets/popup/QuizPopup.tsx index 068068e..681c844 100644 --- a/src/widgets/popup/QuizPopup.tsx +++ b/src/widgets/popup/QuizPopup.tsx @@ -9,7 +9,7 @@ const WIDGET_DEFAULT_HEIGHT = "80%"; interface Props { quizId: string; - dimensions?: { width: string; height: string; }; + dialogDimensions?: { width: string; height: string; }; /** * Открыть квиз через X секунд, 0 - сразу */ @@ -20,7 +20,7 @@ interface Props { export default function QuizPopup({ quizId, - dimensions, + dialogDimensions, autoShowQuizTime = null, hideOnMobile = false, openOnLeaveAttempt = false, @@ -70,8 +70,8 @@ export default function QuizPopup({ quizId={quizId} onClose={() => setIsQuizShown(false)} paperSx={{ - width: dimensions?.width ?? WIDGET_DEFAULT_WIDTH, - height: dimensions?.height ?? WIDGET_DEFAULT_HEIGHT, + width: dialogDimensions?.width ?? WIDGET_DEFAULT_WIDTH, + height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, }} /> ); diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index 0d145f0..aaa78a4 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -17,7 +17,7 @@ interface Props { position: "left" | "right"; buttonBackgroundColor?: string; buttonTextColor?: string; - dimensions?: { width: string; height: string; }; + dialogDimensions?: { width: string; height: string; }; fullScreen?: boolean; buttonFlash?: boolean; /** @@ -36,7 +36,7 @@ export default function QuizSideButton({ position, buttonBackgroundColor, buttonTextColor, - dimensions, + dialogDimensions, fullScreen = false, buttonFlash = false, autoOpenTime = 0, @@ -87,9 +87,9 @@ export default function QuizSideButton({ bottom: PADDING, right: position === "right" ? PADDING : undefined, left: position === "left" ? PADDING : undefined, - width: dimensions?.width ?? WIDGET_DEFAULT_WIDTH, + width: dialogDimensions?.width ?? WIDGET_DEFAULT_WIDTH, maxWidth: `calc(100% - ${PADDING * 2}px)`, - height: dimensions?.height ?? WIDGET_DEFAULT_HEIGHT, + height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, maxHeight: `calc(100% - ${PADDING * 2}px)`, }, (isMobile || fullScreen) && { From 8242f9958016496b4968353d96ef442d7b725e8c Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 13 May 2024 16:41:41 +0300 Subject: [PATCH 28/76] add container widget param: show button on mobile instead of quiz --- src/widgets/container/ContainerWidget.tsx | 30 +++++++++---------- src/widgets/container/QuizContainer.tsx | 35 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 src/widgets/container/QuizContainer.tsx diff --git a/src/widgets/container/ContainerWidget.tsx b/src/widgets/container/ContainerWidget.tsx index ee056b6..8efc41d 100644 --- a/src/widgets/container/ContainerWidget.tsx +++ b/src/widgets/container/ContainerWidget.tsx @@ -1,29 +1,27 @@ -import QuizAnswerer from "@/components/QuizAnswerer"; +import { ComponentPropsWithoutRef } from "react"; import { Root, createRoot } from "react-dom/client"; import { pollForSelector } from "../shared/pollForSelector"; +import QuizContainer from "./QuizContainer"; +type Props = ComponentPropsWithoutRef; + export class ContainerWidget { root: Root | undefined; - constructor({ selector, quizId, selectorPollingTimeLimit = 60 }: { - quizId: string; + constructor(props: Props & { selector: string; /** * In seconds, null - polling disabled */ selectorPollingTimeLimit?: number | null; }) { + const { selector, selectorPollingTimeLimit = 60 } = props; + const element = document.querySelector(selector); if (element) { this.root = createRoot(element); - this.root.render( - - ); + this.render(props); return; } @@ -35,16 +33,14 @@ export class ContainerWidget { pollForSelector(selector, selectorPollingTimeLimit, (element) => { this.root = createRoot(element); - this.root.render( - - ); + this.render(props); }); } + render(props: Props) { + this.root?.render(); + } + destroy() { if (this.root) this.root.unmount(); } diff --git a/src/widgets/container/QuizContainer.tsx b/src/widgets/container/QuizContainer.tsx new file mode 100644 index 0000000..3d6f267 --- /dev/null +++ b/src/widgets/container/QuizContainer.tsx @@ -0,0 +1,35 @@ +import QuizAnswerer from "@/components/QuizAnswerer"; +import { Box, useMediaQuery } from "@mui/material"; +import { ComponentPropsWithoutRef } from "react"; +import OpenQuizButton from "../button/OpenQuizButton"; + + +type Props = ComponentPropsWithoutRef & { + quizId: string; + showButtonOnMobile?: boolean; + dimensions?: { width: string; height: string; }; +}; + +export default function QuizContainer(props: Props) { + const { quizId, dimensions, showButtonOnMobile = false } = props; + const isMobile = useMediaQuery("(max-width: 600px)"); + + return showButtonOnMobile && isMobile ? ( + + ) : ( + + + + ); +} From 81cbe420c5f797c22e8639d9b9f2bcdeb8fa99fd Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 13 May 2024 20:36:27 +0300 Subject: [PATCH 29/76] move widget types to package --- .gitignore | 2 +- lib/model/widget/banner.ts | 22 +++++++++++++++++ lib/model/widget/button.ts | 29 +++++++++++++++++++++++ lib/model/widget/container.ts | 15 ++++++++++++ lib/model/widget/popup.ts | 12 ++++++++++ lib/model/widget/side.ts | 20 ++++++++++++++++ src/widgets/banner/BannerWidget.tsx | 8 +++---- src/widgets/banner/QuizBanner.tsx | 24 ++----------------- src/widgets/button/ButtonWidget.tsx | 24 +++++-------------- src/widgets/button/OpenQuizButton.tsx | 21 ++-------------- src/widgets/container/ContainerWidget.tsx | 14 +++-------- src/widgets/container/QuizContainer.tsx | 10 ++------ src/widgets/popup/PopupWidget.tsx | 8 +++---- src/widgets/popup/QuizPopup.tsx | 14 ++--------- src/widgets/side/QuizSideButton.tsx | 22 ++--------------- src/widgets/side/SideWidget.tsx | 8 +++---- 16 files changed, 127 insertions(+), 126 deletions(-) create mode 100644 lib/model/widget/banner.ts create mode 100644 lib/model/widget/button.ts create mode 100644 lib/model/widget/container.ts create mode 100644 lib/model/widget/popup.ts create mode 100644 lib/model/widget/side.ts diff --git a/.gitignore b/.gitignore index 5d68f75..0a42158 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ node_modules dist dist-package dist-ssr -widget +/widget *.local # Editor directories and files diff --git a/lib/model/widget/banner.ts b/lib/model/widget/banner.ts new file mode 100644 index 0000000..7fc6732 --- /dev/null +++ b/lib/model/widget/banner.ts @@ -0,0 +1,22 @@ +export interface BannerWidgetComponentProps { + quizId: string; + position: "topleft" | "topright" | "bottomleft" | "bottomright"; + onWidgetClose?: () => void; + appealText?: string; + quizHeaderText?: string; + buttonTextColor?: string; + buttonBackgroundColor?: string; + /** + * Открыть квиз через X секунд, 0 - сразу + */ + autoShowQuizTime?: number | null; + openOnLeaveAttempt?: boolean; + buttonFlash?: boolean; + hideOnMobile?: boolean; + withShadow?: boolean; + rounded?: boolean; + bannerFullWidth?: boolean; + pulsation?: boolean; +} + +export type BannerWidgetParams = Omit; diff --git a/lib/model/widget/button.ts b/lib/model/widget/button.ts new file mode 100644 index 0000000..962002b --- /dev/null +++ b/lib/model/widget/button.ts @@ -0,0 +1,29 @@ +export interface ButtonWidgetComponentProps { + quizId: string; + fixedSide?: "left" | "right"; + dialogDimensions?: { width: string; height: string; }; + /** + * Открыть квиз через X секунд, 0 - сразу + */ + autoShowQuizTime?: number | null; + hideOnMobile?: boolean; + openOnLeaveAttempt?: boolean; + buttonFlash?: boolean; + withShadow?: boolean; + rounded?: boolean; + buttonText?: string; + buttonTextColor?: string; + buttonBackgroundColor?: string; +} + +export type ButtonWidgetParams = Omit & { + selector: string; + /** + * In seconds, null - polling disabled + */ + selectorPollingTimeLimit?: number | null; +}; + +export type ButtonWidgetFixedParams = Omit & { + fixedSide: "left" | "right"; +}; diff --git a/lib/model/widget/container.ts b/lib/model/widget/container.ts new file mode 100644 index 0000000..759e5fe --- /dev/null +++ b/lib/model/widget/container.ts @@ -0,0 +1,15 @@ +import { ButtonWidgetComponentProps } from "./button"; + +export type ContainerWidgetComponentProps = ButtonWidgetComponentProps & { + quizId: string; + showButtonOnMobile?: boolean; + dimensions?: { width: string; height: string; }; +}; + +export type ContainerWidgetParams = ContainerWidgetComponentProps & { + selector: string; + /** + * In seconds, null - polling disabled + */ + selectorPollingTimeLimit?: number | null; +}; diff --git a/lib/model/widget/popup.ts b/lib/model/widget/popup.ts new file mode 100644 index 0000000..1fa8feb --- /dev/null +++ b/lib/model/widget/popup.ts @@ -0,0 +1,12 @@ +export interface PopupWidgetComponentProps { + quizId: string; + dialogDimensions?: { width: string; height: string; }; + /** + * Открыть квиз через X секунд, 0 - сразу + */ + autoShowQuizTime?: number | null; + hideOnMobile?: boolean; + openOnLeaveAttempt?: boolean; +} + +export type PopupWidgetParams = PopupWidgetComponentProps; diff --git a/lib/model/widget/side.ts b/lib/model/widget/side.ts new file mode 100644 index 0000000..3e07f3a --- /dev/null +++ b/lib/model/widget/side.ts @@ -0,0 +1,20 @@ +export interface SideWidgetComponentProps { + quizId: string; + position: "left" | "right"; + buttonBackgroundColor?: string; + buttonTextColor?: string; + dialogDimensions?: { width: string; height: string; }; + fullScreen?: boolean; + buttonFlash?: boolean; + /** + * Скрывать виджет первые X секунд + */ + autoOpenTime?: number; + /** + * Открыть квиз через X секунд, 0 - сразу + */ + autoShowQuizTime?: number | null; + hideOnMobile?: boolean; +} + +export type SideWidgetParams = SideWidgetComponentProps; diff --git a/src/widgets/banner/BannerWidget.tsx b/src/widgets/banner/BannerWidget.tsx index f2e4ed1..5e67f28 100644 --- a/src/widgets/banner/BannerWidget.tsx +++ b/src/widgets/banner/BannerWidget.tsx @@ -1,15 +1,13 @@ +import { BannerWidgetParams } from "@/model/widget/banner"; import { Root, createRoot } from "react-dom/client"; import QuizBanner from "./QuizBanner"; -import { ComponentPropsWithoutRef } from "react"; -type Props = Omit, "onClose">; - export class BannerWidget { root: Root | undefined; element = document.createElement("div"); - constructor(props: Props) { + constructor(props: BannerWidgetParams) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); @@ -18,7 +16,7 @@ export class BannerWidget { this.render(props); } - render(props: Props) { + render(props: BannerWidgetParams) { this.root?.render( void; - appealText?: string; - quizHeaderText?: string; - buttonTextColor?: string; - buttonBackgroundColor?: string; - /** - * Открыть квиз через X секунд, 0 - сразу - */ - autoShowQuizTime?: number | null; - openOnLeaveAttempt?: boolean; - buttonFlash?: boolean; - hideOnMobile?: boolean; - withShadow?: boolean; - rounded?: boolean; - bannerFullWidth?: boolean; - pulsation?: boolean; -} - export default function QuizBanner({ quizId, position, @@ -47,7 +27,7 @@ export default function QuizBanner({ rounded = false, bannerFullWidth = false, pulsation = false, -}: Props) { +}: BannerWidgetComponentProps) { const isMobile = useMediaQuery("(max-width: 600px)"); const [isQuizShown, setIsQuizShown] = useState(false); const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); diff --git a/src/widgets/button/ButtonWidget.tsx b/src/widgets/button/ButtonWidget.tsx index d322a3e..d087a54 100644 --- a/src/widgets/button/ButtonWidget.tsx +++ b/src/widgets/button/ButtonWidget.tsx @@ -1,22 +1,14 @@ -import { ComponentPropsWithoutRef } from "react"; +import { ButtonWidgetFixedParams, ButtonWidgetParams } from "@/model/widget/button"; +import { createPortal } from "react-dom"; import { Root, createRoot } from "react-dom/client"; import { pollForSelector } from "../shared/pollForSelector"; import OpenQuizButton from "./OpenQuizButton"; -import { createPortal } from "react-dom"; -type ButtonWidgetProps = Omit, "fixedSide">; - export class ButtonWidget { root: Root | undefined; - constructor(props: ButtonWidgetProps & { - selector: string; - /** - * In seconds, null - polling disabled - */ - selectorPollingTimeLimit?: number | null; - }) { + constructor(props: ButtonWidgetParams) { const { selector, selectorPollingTimeLimit = 60 } = props; const element = document.querySelector(selector); @@ -38,7 +30,7 @@ export class ButtonWidget { }); } - render(props: ButtonWidgetProps) { + render(props: Omit) { this.root?.render(); } @@ -47,15 +39,11 @@ export class ButtonWidget { } } -type ButtonWidgetFixedProps = Omit, "selector"> & { - fixedSide: "left" | "right"; -}; - export class ButtonWidgetFixed { root: Root | undefined; element = document.createElement("div"); - constructor(props: ButtonWidgetFixedProps) { + constructor(props: ButtonWidgetFixedParams) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); @@ -64,7 +52,7 @@ export class ButtonWidgetFixed { this.render(props); } - render(props: ButtonWidgetFixedProps) { + render(props: ButtonWidgetFixedParams) { this.root?.render(createPortal(, document.body)); } diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index f60ba3a..71f204f 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -1,3 +1,4 @@ +import { ButtonWidgetComponentProps } from "@/model/widget/button"; import lightTheme from "@/utils/themes/light"; import { Button, ThemeProvider, useMediaQuery } from "@mui/material"; import { useEffect, useRef, useState } from "react"; @@ -9,24 +10,6 @@ import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; const WIDGET_DEFAULT_WIDTH = "600px"; const WIDGET_DEFAULT_HEIGHT = "80%"; -interface Props { - quizId: string; - fixedSide?: "left" | "right"; - dialogDimensions?: { width: string; height: string; }; - /** - * Открыть квиз через X секунд, 0 - сразу - */ - autoShowQuizTime?: number | null; - hideOnMobile?: boolean; - openOnLeaveAttempt?: boolean; - buttonFlash?: boolean; - withShadow?: boolean; - rounded?: boolean; - buttonText?: string; - buttonTextColor?: string; - buttonBackgroundColor?: string; -} - export default function OpenQuizButton({ quizId, fixedSide, @@ -40,7 +23,7 @@ export default function OpenQuizButton({ buttonText = "Пройти квиз", buttonTextColor, buttonBackgroundColor, -}: Props) { +}: ButtonWidgetComponentProps) { const isMobile = useMediaQuery("(max-width: 600px)"); const [isQuizShown, setIsQuizShown] = useState(false); const isQuizCompleted = useQuizCompletionStatus(quizId); diff --git a/src/widgets/container/ContainerWidget.tsx b/src/widgets/container/ContainerWidget.tsx index 8efc41d..260cef3 100644 --- a/src/widgets/container/ContainerWidget.tsx +++ b/src/widgets/container/ContainerWidget.tsx @@ -1,21 +1,13 @@ -import { ComponentPropsWithoutRef } from "react"; +import { ContainerWidgetParams } from "@/model/widget/container"; import { Root, createRoot } from "react-dom/client"; import { pollForSelector } from "../shared/pollForSelector"; import QuizContainer from "./QuizContainer"; -type Props = ComponentPropsWithoutRef; - export class ContainerWidget { root: Root | undefined; - constructor(props: Props & { - selector: string; - /** - * In seconds, null - polling disabled - */ - selectorPollingTimeLimit?: number | null; - }) { + constructor(props: ContainerWidgetParams) { const { selector, selectorPollingTimeLimit = 60 } = props; const element = document.querySelector(selector); @@ -37,7 +29,7 @@ export class ContainerWidget { }); } - render(props: Props) { + render(props: Omit) { this.root?.render(); } diff --git a/src/widgets/container/QuizContainer.tsx b/src/widgets/container/QuizContainer.tsx index 3d6f267..45dda99 100644 --- a/src/widgets/container/QuizContainer.tsx +++ b/src/widgets/container/QuizContainer.tsx @@ -1,16 +1,10 @@ import QuizAnswerer from "@/components/QuizAnswerer"; +import { ContainerWidgetComponentProps } from "@/model/widget/container"; import { Box, useMediaQuery } from "@mui/material"; -import { ComponentPropsWithoutRef } from "react"; import OpenQuizButton from "../button/OpenQuizButton"; -type Props = ComponentPropsWithoutRef & { - quizId: string; - showButtonOnMobile?: boolean; - dimensions?: { width: string; height: string; }; -}; - -export default function QuizContainer(props: Props) { +export default function QuizContainer(props: ContainerWidgetComponentProps) { const { quizId, dimensions, showButtonOnMobile = false } = props; const isMobile = useMediaQuery("(max-width: 600px)"); diff --git a/src/widgets/popup/PopupWidget.tsx b/src/widgets/popup/PopupWidget.tsx index d238eaa..92e18e6 100644 --- a/src/widgets/popup/PopupWidget.tsx +++ b/src/widgets/popup/PopupWidget.tsx @@ -1,15 +1,13 @@ +import { PopupWidgetParams } from "@/model/widget/popup"; import { Root, createRoot } from "react-dom/client"; -import { ComponentPropsWithoutRef } from "react"; import QuizPopup from "./QuizPopup"; -type Props = ComponentPropsWithoutRef; - export class PopupWidget { root: Root | undefined; element = document.createElement("div"); - constructor(props: Props) { + constructor(props: PopupWidgetParams) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); @@ -18,7 +16,7 @@ export class PopupWidget { this.render(props); } - render(props: Props) { + render(props: PopupWidgetParams) { this.root?.render(); } diff --git a/src/widgets/popup/QuizPopup.tsx b/src/widgets/popup/QuizPopup.tsx index 681c844..808b84a 100644 --- a/src/widgets/popup/QuizPopup.tsx +++ b/src/widgets/popup/QuizPopup.tsx @@ -2,29 +2,19 @@ import { useEffect, useRef, useState } from "react"; import QuizDialog from "../shared/QuizDialog"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; import { useMediaQuery } from "@mui/material"; +import { PopupWidgetComponentProps } from "@/model/widget/popup"; const WIDGET_DEFAULT_WIDTH = "600px"; const WIDGET_DEFAULT_HEIGHT = "80%"; -interface Props { - quizId: string; - dialogDimensions?: { width: string; height: string; }; - /** - * Открыть квиз через X секунд, 0 - сразу - */ - autoShowQuizTime?: number | null; - hideOnMobile?: boolean; - openOnLeaveAttempt?: boolean; -} - export default function QuizPopup({ quizId, dialogDimensions, autoShowQuizTime = null, hideOnMobile = false, openOnLeaveAttempt = false, -}: Props) { +}: PopupWidgetComponentProps) { const initialIsQuizShown = (autoShowQuizTime !== null || openOnLeaveAttempt) ? false : true; const [isQuizShown, setIsQuizShown] = useState(initialIsQuizShown); diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index aaa78a4..67b2c5c 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -6,31 +6,13 @@ import QuizDialog from "../shared/QuizDialog"; import RunningStripe from "../shared/RunningStripe"; import { useAutoOpenTimer } from "../shared/useAutoOpenTimer"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; +import { SideWidgetComponentProps } from "@/model/widget/side"; const PADDING = 10; const WIDGET_DEFAULT_WIDTH = "600px"; const WIDGET_DEFAULT_HEIGHT = "800px"; -interface Props { - quizId: string; - position: "left" | "right"; - buttonBackgroundColor?: string; - buttonTextColor?: string; - dialogDimensions?: { width: string; height: string; }; - fullScreen?: boolean; - buttonFlash?: boolean; - /** - * Скрывать виджет первые X секунд - */ - autoOpenTime?: number; - /** - * Открыть квиз через X секунд, 0 - сразу - */ - autoShowQuizTime?: number | null; - hideOnMobile?: boolean; -} - export default function QuizSideButton({ quizId, position, @@ -42,7 +24,7 @@ export default function QuizSideButton({ autoOpenTime = 0, autoShowQuizTime = null, hideOnMobile = false, -}: Props) { +}: SideWidgetComponentProps) { const [isQuizShown, setIsQuizShown] = useState(false); const isMobile = useMediaQuery("(max-width: 600px)"); const isQuizCompleted = useQuizCompletionStatus(quizId); diff --git a/src/widgets/side/SideWidget.tsx b/src/widgets/side/SideWidget.tsx index 2c7c4d3..c1c1863 100644 --- a/src/widgets/side/SideWidget.tsx +++ b/src/widgets/side/SideWidget.tsx @@ -1,15 +1,13 @@ +import { SideWidgetParams } from "@/model/widget/side"; import { Root, createRoot } from "react-dom/client"; import QuizSideButton from "./QuizSideButton"; -import { ComponentPropsWithoutRef } from "react"; -type Props = ComponentPropsWithoutRef; - export class SideWidget { root: Root | undefined; element = document.createElement("div"); - constructor(props: Props) { + constructor(props: SideWidgetParams) { this.element.style.setProperty("display", "none"); document.body.appendChild(this.element); @@ -18,7 +16,7 @@ export class SideWidget { this.render(props); } - render(props: Props) { + render(props: SideWidgetParams) { this.root?.render(); } From 0d91e1133d2afa63935a8fb675da89aafdfa6de1 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 13 May 2024 20:48:45 +0300 Subject: [PATCH 30/76] publish 1.0.39 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3fdc17..faa04c4 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@frontend/squzanswerer", - "version": "1.0.38", + "version": "1.0.39", "type": "module", "main": "./dist-package/index.js", "module": "./dist-package/index.js", From e4b39836de12ce4e83535f96226981b91934c871 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 13 May 2024 21:00:34 +0300 Subject: [PATCH 31/76] move mui.d.ts to package folder --- {src => lib/utils/themes}/mui.d.ts | 100 ++++++++++++++--------------- 1 file changed, 50 insertions(+), 50 deletions(-) rename {src => lib/utils/themes}/mui.d.ts (96%) mode change 100755 => 100644 diff --git a/src/mui.d.ts b/lib/utils/themes/mui.d.ts old mode 100755 new mode 100644 similarity index 96% rename from src/mui.d.ts rename to lib/utils/themes/mui.d.ts index 446edc4..f66d0c6 --- a/src/mui.d.ts +++ b/lib/utils/themes/mui.d.ts @@ -1,50 +1,50 @@ -import "@material-ui/styles"; - -declare module "@mui/material/styles" { - interface Palette { - lightPurple: Palette["primary"], - darkPurple: Palette["primary"], - brightPurple: Palette["primary"], - fadePurple: Palette["primary"], - grey1: Palette["primary"], - grey2: Palette["primary"], - grey3: Palette["primary"], - grey4: Palette["primary"], - orange: Palette["primary"], - navbarbg: Palette["primary"], - } - interface PaletteOptions { - lightPurple?: PaletteOptions["primary"], - darkPurple?: PaletteOptions["primary"], - brightPurple?: PaletteOptions["primary"], - fadePurple?: PaletteOptions["primary"], - grey1?: PaletteOptions["primary"], - grey2?: PaletteOptions["primary"], - grey3?: PaletteOptions["primary"], - grey4?: PaletteOptions["primary"], - orange?: PaletteOptions["primary"], - navbarbg?: PaletteOptions["primary"], - } - interface TypographyVariants { - infographic: React.CSSProperties; - p1: React.CSSProperties; - } - interface TypographyVariantsOptions { - infographic?: React.CSSProperties; - p1?: React.CSSProperties; - } -} - -declare module "@mui/material/Typography" { - interface TypographyPropsVariantOverrides { - infographic: true; - p1: true; - } -} - -type DataAttributeKey = `data-${string}`; -declare module 'react' { - interface HTMLAttributes extends AriaAttributes, DOMAttributes { - [dataAttribute: DataAttributeKey]: unknown; - } -} +import "@material-ui/styles"; + +declare module "@mui/material/styles" { + interface Palette { + lightPurple: Palette["primary"], + darkPurple: Palette["primary"], + brightPurple: Palette["primary"], + fadePurple: Palette["primary"], + grey1: Palette["primary"], + grey2: Palette["primary"], + grey3: Palette["primary"], + grey4: Palette["primary"], + orange: Palette["primary"], + navbarbg: Palette["primary"], + } + interface PaletteOptions { + lightPurple?: PaletteOptions["primary"], + darkPurple?: PaletteOptions["primary"], + brightPurple?: PaletteOptions["primary"], + fadePurple?: PaletteOptions["primary"], + grey1?: PaletteOptions["primary"], + grey2?: PaletteOptions["primary"], + grey3?: PaletteOptions["primary"], + grey4?: PaletteOptions["primary"], + orange?: PaletteOptions["primary"], + navbarbg?: PaletteOptions["primary"], + } + interface TypographyVariants { + infographic: React.CSSProperties; + p1: React.CSSProperties; + } + interface TypographyVariantsOptions { + infographic?: React.CSSProperties; + p1?: React.CSSProperties; + } +} + +declare module "@mui/material/Typography" { + interface TypographyPropsVariantOverrides { + infographic: true; + p1: true; + } +} + +type DataAttributeKey = `data-${string}`; +declare module 'react' { + interface HTMLAttributes extends AriaAttributes, DOMAttributes { + [dataAttribute: DataAttributeKey]: unknown; + } +} From 8c1913667c4c5b3783ce010e66c12d21224665a6 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 13 May 2024 21:00:40 +0300 Subject: [PATCH 32/76] remove useless font file --- lib/utils/fonts/importsFonts.ts | 39 --------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 lib/utils/fonts/importsFonts.ts diff --git a/lib/utils/fonts/importsFonts.ts b/lib/utils/fonts/importsFonts.ts deleted file mode 100644 index 8c42336..0000000 --- a/lib/utils/fonts/importsFonts.ts +++ /dev/null @@ -1,39 +0,0 @@ -import BlackItalic from "./Lato/Lato-BlackItalic.ttf" -import ExtraBold from "./Lato/Lato-ExtraBold.ttf" -import Light from "./Lato/Lato-Light.ttf" -import SemiBold from "./Lato/Lato-SemiBold.ttf" -import Black from "./Lato/Lato-Black.ttf" -import ExtraLightItalic from "./Lato/Lato-ExtraLightItalic.ttf" -import MediumItalic from "./Lato/Lato-MediumItalic.ttf" -import ThinItalic from "./Lato/Lato-ThinItalic.ttf" -import BoldItalic from "./Lato/Lato-BoldItalic.ttf" -import ExtraLight from "./Lato/Lato-ExtraLight.ttf" -import Medium from "./Lato/Lato-Medium.ttf" -import Thin from "./Lato/Lato-Thin.ttf" -import Bold from "./Lato/Lato-Bold.ttf" -import Italic from "./Lato/Lato-Italic.ttf" -import Regular from "./Lato/Lato-Regular.ttf" -import ExtraBoldItalic from "./Lato/Lato-ExtraBoldItalic.ttf" -import LightItalic from "./Lato/Lato-LightItalic.ttf" -import SemiBoldItalic from "./Lato/Lato-SemiBoldItalic.ttf" - -export const Latos = [ - {name: BlackItalic, format: "ttf"}, - {name: ExtraBold, format: "ttf"}, - {name: Light, format: "ttf"}, - {name: SemiBold, format: "ttf"}, - {name: Black, format: "ttf"}, - {name: ExtraLightItalic, format: "ttf"}, - {name: MediumItalic, format: "ttf"}, - {name: ThinItalic, format: "ttf"}, - {name: BoldItalic, format: "ttf"}, - {name: ExtraLight, format: "ttf"}, - {name: Medium, format: "ttf"}, - {name: Thin, format: "ttf"}, - {name: Bold, format: "ttf"}, - {name: Italic, format: "ttf"}, - {name: Regular, format: "ttf"}, - {name: ExtraBoldItalic, format: "ttf"}, - {name: LightItalic, format: "ttf"}, - {name: SemiBoldItalic, format: "ttf"} -] From 1b17dad45197dd769210dea082c17315277132b7 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 14 May 2024 12:16:53 +0300 Subject: [PATCH 33/76] fix types export --- lib/index.ts | 4 ++-- lib/model/widget/index.ts | 5 +++++ package.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 lib/model/widget/index.ts diff --git a/lib/index.ts b/lib/index.ts index 2e154a8..a1bcb08 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,5 +1,5 @@ import QuizAnswerer from "./components/QuizAnswerer"; -import type { QuizSettings } from "@model/settingsData"; +export type { QuizSettings } from "@model/settingsData"; +export type * from "./model/widget"; export { QuizAnswerer }; -export type { QuizSettings }; diff --git a/lib/model/widget/index.ts b/lib/model/widget/index.ts new file mode 100644 index 0000000..0b265ba --- /dev/null +++ b/lib/model/widget/index.ts @@ -0,0 +1,5 @@ +export * from "./banner"; +export * from "./button"; +export * from "./container"; +export * from "./popup"; +export * from "./side"; diff --git a/package.json b/package.json index faa04c4..fe8c78d 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@frontend/squzanswerer", - "version": "1.0.39", + "version": "1.0.40", "type": "module", "main": "./dist-package/index.js", "module": "./dist-package/index.js", From 8f1caa9b073c05d10407b97e0565dc378bf78133 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 14 May 2024 12:26:01 +0300 Subject: [PATCH 34/76] add widget type --- lib/model/widget/index.ts | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/model/widget/index.ts b/lib/model/widget/index.ts index 0b265ba..6fe70fe 100644 --- a/lib/model/widget/index.ts +++ b/lib/model/widget/index.ts @@ -3,3 +3,5 @@ export * from "./button"; export * from "./container"; export * from "./popup"; export * from "./side"; + +export type WidgetType = "container" | "button" | "popup" | "banner" | "side"; diff --git a/package.json b/package.json index fe8c78d..b4baafa 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@frontend/squzanswerer", - "version": "1.0.40", + "version": "1.0.41", "type": "module", "main": "./dist-package/index.js", "module": "./dist-package/index.js", From 4f86cbe2d1604bdfab14809e7945a45af779e0f4 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 20 May 2024 16:04:30 +0300 Subject: [PATCH 35/76] fix button widget button styles --- src/widgets/button/OpenQuizButton.tsx | 11 +++++------ src/widgets/shared/RunningStripe.tsx | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index 71f204f..a24105a 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -78,14 +78,13 @@ export default function OpenQuizButton({ sx={[ { overflow: "hidden", + py: "23px", + px: "40px", + fontSize: "20px", color: buttonTextColor, backgroundColor: buttonBackgroundColor, - }, - withShadow && { - boxShadow: "0px 0px 8px 0px rgba(0, 0, 0, 0.7)", - }, - !rounded && { - borderRadius: 0, + boxShadow: withShadow ? "2px 5px 20px 2px rgba(25, 6, 50, 0.4), 0 2px 10px 0 rgba(35, 17, 58, 0.1)" : "none", + borderRadius: rounded ? "30px" : 0, }, Boolean(fixedSide) && { position: "fixed", diff --git a/src/widgets/shared/RunningStripe.tsx b/src/widgets/shared/RunningStripe.tsx index 0faf39a..a52aed3 100644 --- a/src/widgets/shared/RunningStripe.tsx +++ b/src/widgets/shared/RunningStripe.tsx @@ -13,9 +13,9 @@ export default function RunningStripe({ sx = [] }: Props) { sx={[ { position: "absolute", - height: "30px", + height: "70px", width: "140px", - backgroundColor: "rgba(255 255 255 / 0.6)", + background: "linear-gradient(0deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.1) 100%)", animation: "runningStripe linear 3s infinite", transform: "rotate(-60deg)", "@keyframes runningStripe": { From 6fb351759dd8d47c4898f37a525973a9cd776793 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 21 May 2024 15:24:19 +0300 Subject: [PATCH 36/76] fix widget auto show features --- lib/model/widget/banner.ts | 4 ++++ lib/model/widget/side.ts | 4 ++-- package.json | 2 +- src/WidgetDev.tsx | 4 ++-- src/widgets/banner/QuizBanner.tsx | 5 ++++- src/widgets/side/QuizSideButton.tsx | 4 ++-- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/model/widget/banner.ts b/lib/model/widget/banner.ts index 7fc6732..621b4a4 100644 --- a/lib/model/widget/banner.ts +++ b/lib/model/widget/banner.ts @@ -6,6 +6,10 @@ export interface BannerWidgetComponentProps { quizHeaderText?: string; buttonTextColor?: string; buttonBackgroundColor?: string; + /** + * Показывать виджет через X секунд + */ + autoShowWidgetTime?: number; /** * Открыть квиз через X секунд, 0 - сразу */ diff --git a/lib/model/widget/side.ts b/lib/model/widget/side.ts index 3e07f3a..8506005 100644 --- a/lib/model/widget/side.ts +++ b/lib/model/widget/side.ts @@ -7,9 +7,9 @@ export interface SideWidgetComponentProps { fullScreen?: boolean; buttonFlash?: boolean; /** - * Скрывать виджет первые X секунд + * Показывать виджет через X секунд */ - autoOpenTime?: number; + autoShowWidgetTime?: number; /** * Открыть квиз через X секунд, 0 - сразу */ diff --git a/package.json b/package.json index b4baafa..d48182a 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@frontend/squzanswerer", - "version": "1.0.41", + "version": "1.0.42", "type": "module", "main": "./dist-package/index.js", "module": "./dist-package/index.js", diff --git a/src/WidgetDev.tsx b/src/WidgetDev.tsx index 29dd6b3..0a0678f 100644 --- a/src/WidgetDev.tsx +++ b/src/WidgetDev.tsx @@ -1,12 +1,12 @@ import lightTheme from "@/utils/themes/light"; import { Box, ThemeProvider, Typography } from "@mui/material"; import { useEffect, useRef } from "react"; -import { ContainerWidget as Widget } from "./widgets"; +import { BannerWidget as Widget } from "./widgets"; const widgetProps: ConstructorParameters[0] = { quizId: "3c49550d-8c77-4788-bc2d-42586a261514", - selector: "#widget-container", + position: "bottomright", }; export default function WidgetDev() { diff --git a/src/widgets/banner/QuizBanner.tsx b/src/widgets/banner/QuizBanner.tsx index 3fb1508..732cf31 100644 --- a/src/widgets/banner/QuizBanner.tsx +++ b/src/widgets/banner/QuizBanner.tsx @@ -7,6 +7,7 @@ import BannerIcon from "../shared/BannerIcon"; import QuizDialog from "../shared/QuizDialog"; import RunningStripe from "../shared/RunningStripe"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; +import { useAutoOpenTimer } from "../shared/useAutoOpenTimer"; const PADDING = 10; @@ -27,10 +28,12 @@ export default function QuizBanner({ rounded = false, bannerFullWidth = false, pulsation = false, + autoShowWidgetTime = 0, }: BannerWidgetComponentProps) { const isMobile = useMediaQuery("(max-width: 600px)"); const [isQuizShown, setIsQuizShown] = useState(false); const [isFlashEnabled, setIsFlashEnabled] = useState(buttonFlash); + const isWidgetHidden = useAutoOpenTimer(autoShowWidgetTime); const isQuizCompleted = useQuizCompletionStatus(quizId); const preventQuizAutoShowRef = useRef(false); const preventOpenOnLeaveAttemptRef = useRef(false); @@ -74,7 +77,7 @@ export default function QuizBanner({ return createPortal( - + (buttonFlash); - const isWidgetHidden = useAutoOpenTimer(autoOpenTime); + const isWidgetHidden = useAutoOpenTimer(autoShowWidgetTime); const preventQuizAutoShowRef = useRef(false); useEffect(function setAutoShowQuizTimer() { From ac0f06ff720faedb808ddcf78f987675ba2f18bd Mon Sep 17 00:00:00 2001 From: nflnkr Date: Tue, 21 May 2024 16:56:25 +0300 Subject: [PATCH 37/76] fix banner widget layout --- src/widgets/banner/QuizBanner.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/widgets/banner/QuizBanner.tsx b/src/widgets/banner/QuizBanner.tsx index 732cf31..d8f94b3 100644 --- a/src/widgets/banner/QuizBanner.tsx +++ b/src/widgets/banner/QuizBanner.tsx @@ -85,7 +85,7 @@ export default function QuizBanner({ position: "fixed", height: "120px", width: bannerFullWidth ? "100%" : "800px", - maxWidth: `calc(100% - ${PADDING * 2}px)`, + maxWidth: bannerFullWidth ? "100%" : `calc(100% - ${PADDING * 2}px)`, }, position === "topleft" && { top: bannerFullWidth ? 0 : PADDING, @@ -111,7 +111,7 @@ export default function QuizBanner({ width: "100%", pointerEvents: "none", willChange: "box-shadow", - borderRadius: rounded ? "8px" : 0, + borderRadius: rounded && !bannerFullWidth ? "8px" : 0, animation: "pena-pulsation linear 5s infinite", "@keyframes pena-pulsation": { "0%": { @@ -141,7 +141,7 @@ export default function QuizBanner({ px: "28px", color: buttonTextColor, backgroundColor: buttonBackgroundColor, - borderRadius: rounded ? "8px" : 0, + borderRadius: rounded && !bannerFullWidth ? "8px" : 0, justifyContent: "start", }, withShadow && { From 325e2f56b18d5ad0a1e12d12429e9b2b520a1597 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Sat, 25 May 2024 15:58:27 +0300 Subject: [PATCH 38/76] fix widget params type comments --- lib/model/widget/banner.ts | 2 +- lib/model/widget/button.ts | 2 +- lib/model/widget/popup.ts | 2 +- lib/model/widget/side.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/model/widget/banner.ts b/lib/model/widget/banner.ts index 621b4a4..70267ca 100644 --- a/lib/model/widget/banner.ts +++ b/lib/model/widget/banner.ts @@ -11,7 +11,7 @@ export interface BannerWidgetComponentProps { */ autoShowWidgetTime?: number; /** - * Открыть квиз через X секунд, 0 - сразу + * Открыть квиз через X секунд, 0 - сразу, null - не открывать */ autoShowQuizTime?: number | null; openOnLeaveAttempt?: boolean; diff --git a/lib/model/widget/button.ts b/lib/model/widget/button.ts index 962002b..5b03ae1 100644 --- a/lib/model/widget/button.ts +++ b/lib/model/widget/button.ts @@ -3,7 +3,7 @@ export interface ButtonWidgetComponentProps { fixedSide?: "left" | "right"; dialogDimensions?: { width: string; height: string; }; /** - * Открыть квиз через X секунд, 0 - сразу + * Открыть квиз через X секунд, 0 - сразу, null - не открывать */ autoShowQuizTime?: number | null; hideOnMobile?: boolean; diff --git a/lib/model/widget/popup.ts b/lib/model/widget/popup.ts index 1fa8feb..df3c432 100644 --- a/lib/model/widget/popup.ts +++ b/lib/model/widget/popup.ts @@ -2,7 +2,7 @@ export interface PopupWidgetComponentProps { quizId: string; dialogDimensions?: { width: string; height: string; }; /** - * Открыть квиз через X секунд, 0 - сразу + * Открыть квиз через X секунд, 0 - сразу, null - не открывать */ autoShowQuizTime?: number | null; hideOnMobile?: boolean; diff --git a/lib/model/widget/side.ts b/lib/model/widget/side.ts index 8506005..16e9e79 100644 --- a/lib/model/widget/side.ts +++ b/lib/model/widget/side.ts @@ -11,7 +11,7 @@ export interface SideWidgetComponentProps { */ autoShowWidgetTime?: number; /** - * Открыть квиз через X секунд, 0 - сразу + * Открыть квиз через X секунд, 0 - сразу, null - не открывать */ autoShowQuizTime?: number | null; hideOnMobile?: boolean; From 7b1fafe37286a86418796c72f08afc65efbbf86d Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 27 May 2024 12:51:27 +0300 Subject: [PATCH 39/76] banner widget accepts dimensions prop --- lib/model/widget/banner.ts | 1 + package.json | 2 +- src/widgets/banner/QuizBanner.tsx | 5 +++++ src/widgets/button/OpenQuizButton.tsx | 7 ++----- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/model/widget/banner.ts b/lib/model/widget/banner.ts index 70267ca..1deda81 100644 --- a/lib/model/widget/banner.ts +++ b/lib/model/widget/banner.ts @@ -2,6 +2,7 @@ export interface BannerWidgetComponentProps { quizId: string; position: "topleft" | "topright" | "bottomleft" | "bottomright"; onWidgetClose?: () => void; + dialogDimensions?: { width: string; height: string; }; appealText?: string; quizHeaderText?: string; buttonTextColor?: string; diff --git a/package.json b/package.json index d48182a..760cdc0 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@frontend/squzanswerer", - "version": "1.0.42", + "version": "1.0.43", "type": "module", "main": "./dist-package/index.js", "module": "./dist-package/index.js", diff --git a/src/widgets/banner/QuizBanner.tsx b/src/widgets/banner/QuizBanner.tsx index d8f94b3..e31325b 100644 --- a/src/widgets/banner/QuizBanner.tsx +++ b/src/widgets/banner/QuizBanner.tsx @@ -29,6 +29,7 @@ export default function QuizBanner({ bannerFullWidth = false, pulsation = false, autoShowWidgetTime = 0, + dialogDimensions, }: BannerWidgetComponentProps) { const isMobile = useMediaQuery("(max-width: 600px)"); const [isQuizShown, setIsQuizShown] = useState(false); @@ -188,6 +189,10 @@ export default function QuizBanner({ quizId={quizId} onClose={() => setIsQuizShown(false)} disableScrollLock + paperSx={{ + width: dialogDimensions?.width, + height: dialogDimensions?.height, + }} /> , document.body diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index a24105a..1710381 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -7,9 +7,6 @@ import RunningStripe from "../shared/RunningStripe"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; -const WIDGET_DEFAULT_WIDTH = "600px"; -const WIDGET_DEFAULT_HEIGHT = "80%"; - export default function OpenQuizButton({ quizId, fixedSide, @@ -110,8 +107,8 @@ export default function OpenQuizButton({ quizId={quizId} onClose={() => setIsQuizShown(false)} paperSx={{ - width: dialogDimensions?.width ?? WIDGET_DEFAULT_WIDTH, - height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, + width: dialogDimensions?.width, + height: dialogDimensions?.height, }} /> From 9271bbb80da9d575d27570804ac27e57c83e73ba Mon Sep 17 00:00:00 2001 From: nflnkr Date: Mon, 27 May 2024 17:24:04 +0300 Subject: [PATCH 40/76] add fullscreen param to widgets --- lib/model/widget/banner.ts | 1 + lib/model/widget/button.ts | 1 + lib/model/widget/popup.ts | 1 + package.json | 2 +- src/widgets/banner/QuizBanner.tsx | 17 +++++++++++++---- src/widgets/button/OpenQuizButton.tsx | 17 +++++++++++++---- src/widgets/popup/QuizPopup.tsx | 20 +++++++++++++------- src/widgets/side/QuizSideButton.tsx | 14 ++++++-------- 8 files changed, 49 insertions(+), 24 deletions(-) diff --git a/lib/model/widget/banner.ts b/lib/model/widget/banner.ts index 1deda81..4929416 100644 --- a/lib/model/widget/banner.ts +++ b/lib/model/widget/banner.ts @@ -22,6 +22,7 @@ export interface BannerWidgetComponentProps { rounded?: boolean; bannerFullWidth?: boolean; pulsation?: boolean; + fullScreen?: boolean; } export type BannerWidgetParams = Omit; diff --git a/lib/model/widget/button.ts b/lib/model/widget/button.ts index 5b03ae1..5e72ec6 100644 --- a/lib/model/widget/button.ts +++ b/lib/model/widget/button.ts @@ -14,6 +14,7 @@ export interface ButtonWidgetComponentProps { buttonText?: string; buttonTextColor?: string; buttonBackgroundColor?: string; + fullScreen?: boolean; } export type ButtonWidgetParams = Omit & { diff --git a/lib/model/widget/popup.ts b/lib/model/widget/popup.ts index df3c432..27cf9eb 100644 --- a/lib/model/widget/popup.ts +++ b/lib/model/widget/popup.ts @@ -7,6 +7,7 @@ export interface PopupWidgetComponentProps { autoShowQuizTime?: number | null; hideOnMobile?: boolean; openOnLeaveAttempt?: boolean; + fullScreen?: boolean; } export type PopupWidgetParams = PopupWidgetComponentProps; diff --git a/package.json b/package.json index 760cdc0..0c712c2 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@frontend/squzanswerer", - "version": "1.0.43", + "version": "1.0.44", "type": "module", "main": "./dist-package/index.js", "module": "./dist-package/index.js", diff --git a/src/widgets/banner/QuizBanner.tsx b/src/widgets/banner/QuizBanner.tsx index e31325b..cdda540 100644 --- a/src/widgets/banner/QuizBanner.tsx +++ b/src/widgets/banner/QuizBanner.tsx @@ -30,6 +30,7 @@ export default function QuizBanner({ pulsation = false, autoShowWidgetTime = 0, dialogDimensions, + fullScreen = false, }: BannerWidgetComponentProps) { const isMobile = useMediaQuery("(max-width: 600px)"); const [isQuizShown, setIsQuizShown] = useState(false); @@ -189,10 +190,18 @@ export default function QuizBanner({ quizId={quizId} onClose={() => setIsQuizShown(false)} disableScrollLock - paperSx={{ - width: dialogDimensions?.width, - height: dialogDimensions?.height, - }} + paperSx={[ + (isMobile || fullScreen) ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + m: 0, + } : { + width: dialogDimensions?.width, + height: dialogDimensions?.height, + }, + ]} /> , document.body diff --git a/src/widgets/button/OpenQuizButton.tsx b/src/widgets/button/OpenQuizButton.tsx index 1710381..5a0ba39 100644 --- a/src/widgets/button/OpenQuizButton.tsx +++ b/src/widgets/button/OpenQuizButton.tsx @@ -20,6 +20,7 @@ export default function OpenQuizButton({ buttonText = "Пройти квиз", buttonTextColor, buttonBackgroundColor, + fullScreen = false, }: ButtonWidgetComponentProps) { const isMobile = useMediaQuery("(max-width: 600px)"); const [isQuizShown, setIsQuizShown] = useState(false); @@ -106,10 +107,18 @@ export default function OpenQuizButton({ open={isQuizShown} quizId={quizId} onClose={() => setIsQuizShown(false)} - paperSx={{ - width: dialogDimensions?.width, - height: dialogDimensions?.height, - }} + paperSx={[ + (isMobile || fullScreen) ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + m: 0, + } : { + width: dialogDimensions?.width, + height: dialogDimensions?.height, + }, + ]} /> ); diff --git a/src/widgets/popup/QuizPopup.tsx b/src/widgets/popup/QuizPopup.tsx index 808b84a..bbcea4f 100644 --- a/src/widgets/popup/QuizPopup.tsx +++ b/src/widgets/popup/QuizPopup.tsx @@ -5,15 +5,13 @@ import { useMediaQuery } from "@mui/material"; import { PopupWidgetComponentProps } from "@/model/widget/popup"; -const WIDGET_DEFAULT_WIDTH = "600px"; -const WIDGET_DEFAULT_HEIGHT = "80%"; - export default function QuizPopup({ quizId, dialogDimensions, autoShowQuizTime = null, hideOnMobile = false, openOnLeaveAttempt = false, + fullScreen = false, }: PopupWidgetComponentProps) { const initialIsQuizShown = (autoShowQuizTime !== null || openOnLeaveAttempt) ? false : true; @@ -59,10 +57,18 @@ export default function QuizPopup({ open={isQuizShown} quizId={quizId} onClose={() => setIsQuizShown(false)} - paperSx={{ - width: dialogDimensions?.width ?? WIDGET_DEFAULT_WIDTH, - height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, - }} + paperSx={[ + (isMobile || fullScreen) ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + m: 0, + } : { + width: dialogDimensions?.width, + height: dialogDimensions?.height, + }, + ]} /> ); } diff --git a/src/widgets/side/QuizSideButton.tsx b/src/widgets/side/QuizSideButton.tsx index d1880ec..5af8d9a 100644 --- a/src/widgets/side/QuizSideButton.tsx +++ b/src/widgets/side/QuizSideButton.tsx @@ -64,7 +64,12 @@ export default function QuizSideButton({ { m: 0, }, - !(isMobile || fullScreen) && { + (isMobile || fullScreen) ? { + width: "100%", + height: "100%", + maxHeight: "100%", + borderRadius: 0, + } : { position: "absolute", bottom: PADDING, right: position === "right" ? PADDING : undefined, @@ -74,13 +79,6 @@ export default function QuizSideButton({ height: dialogDimensions?.height ?? WIDGET_DEFAULT_HEIGHT, maxHeight: `calc(100% - ${PADDING * 2}px)`, }, - (isMobile || fullScreen) && { - position: "relative", - width: "100%", - height: "100%", - maxHeight: "100%", - borderRadius: 0, - }, ]} /> From 4c3676d1db9b0191a64ce7b1b8aee8e6b313a3ef Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 31 May 2024 19:16:52 +0300 Subject: [PATCH 41/76] add lint-staged, husky and prettier --- .husky/.gitignore | 1 + .husky/pre-commit | 4 + package.json | 207 ++++++++++++++++++---------------- yarn.lock | 281 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 395 insertions(+), 98 deletions(-) create mode 100644 .husky/.gitignore create mode 100644 .husky/pre-commit diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..31354ec --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..12ac302 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged --allow-empty diff --git a/package.json b/package.json index 0c712c2..d01d328 100755 --- a/package.json +++ b/package.json @@ -1,97 +1,116 @@ { - "name": "@frontend/squzanswerer", - "version": "1.0.44", - "type": "module", - "main": "./dist-package/index.js", - "module": "./dist-package/index.js", - "types": "./dist-package/index.d.ts", - "license": "MIT", - "files": [ - "dist-package" - ], - "exports": { - ".": { - "import": "./dist-package/index.js" - } - }, - "publishConfig": { - "registry": "https://penahub.gitlab.yandexcloud.net/api/v4/projects/43/packages/npm/" - }, - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "build:widget": "tsc && vite build --config vite.config.widget.ts", - "build:package": "tsc && vite build --config vite.config.package.ts", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "cypress:open": "cypress open", - "prepublishOnly": "npm run build:package" - }, - "devDependencies": { - "@emoji-mart/data": "^1.1.2", - "@emoji-mart/react": "^1.1.1", - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", - "@mui/icons-material": "^5.10.14", - "@mui/material": "^5.10.14", - "@mui/x-date-pickers": "^6.16.1", - "@types/node": "^16.7.13", - "@types/react": "^18.2.43", - "@types/react-dom": "^18.2.17", - "@typescript-eslint/eslint-plugin": "^6.14.0", - "@typescript-eslint/parser": "^6.14.0", - "@vitejs/plugin-react": "^4.2.1", - "axios": "^1.5.1", - "cypress": "^13.6.1", - "emoji-mart": "^5.5.2", - "eslint": "^8.55.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.5", - "immer": "^10.0.3", - "moment": "^2.30.1", - "nanoid": "^5.0.3", - "notistack": "^3.0.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^4.0.12", - "react-router-dom": "^6.21.3", - "swr": "^2.2.4", - "typescript": "^5.2.2", - "use-debounce": "^9.0.4", - "vite": "^5.0.8", - "vite-plugin-dts": "^3.7.2", - "zustand": "^4.3.8" - }, - "peerDependencies": { - "@emoji-mart/data": "^1.1.2", - "@emoji-mart/react": "^1.1.1", - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", - "@mui/icons-material": "^5.10.14", - "@mui/material": "^5.10.14", - "@mui/x-date-pickers": "^6.16.1", - "axios": "^1.5.1", - "emoji-mart": "^5.5.2", - "immer": "^10.0.3", - "moment": "^2.30.1", - "nanoid": "^5.0.3", - "notistack": "^3.0.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^4.0.12", - "react-router-dom": "^6.21.3", - "swr": "^2.2.4", - "use-debounce": "^9.0.4", - "zustand": "^4.3.8" - }, - "dependencies": { - "bowser": "1.9.4", - "country-flag-emoji-polyfill": "^0.1.8", - "current-device": "^0.10.2", - "hex-rgb": "^5.0.0", - "mobile-detect": "^1.4.5", - "mui-tel-input": "^5.1.2", - "react-imask": "^7.6.0", - "react-phone-number-input": "^3.4.1" + "name": "@frontend/squzanswerer", + "version": "1.0.44", + "type": "module", + "main": "./dist-package/index.js", + "module": "./dist-package/index.js", + "types": "./dist-package/index.d.ts", + "license": "MIT", + "files": [ + "dist-package" + ], + "exports": { + ".": { + "import": "./dist-package/index.js" } + }, + "publishConfig": { + "registry": "https://penahub.gitlab.yandexcloud.net/api/v4/projects/43/packages/npm/" + }, + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "build:widget": "tsc && vite build --config vite.config.widget.ts", + "build:package": "tsc && vite build --config vite.config.package.ts", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "cypress:open": "cypress open", + "prepublishOnly": "npm run build:package", + "prepare": "husky install" + }, + "devDependencies": { + "@emoji-mart/data": "^1.1.2", + "@emoji-mart/react": "^1.1.1", + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", + "@mui/icons-material": "^5.10.14", + "@mui/material": "^5.10.14", + "@mui/x-date-pickers": "^6.16.1", + "@types/node": "^16.7.13", + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.1", + "axios": "^1.5.1", + "cypress": "^13.6.1", + "emoji-mart": "^5.5.2", + "eslint": "^8.55.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "husky": "^9.0.11", + "immer": "^10.0.3", + "lint-staged": "^15.2.5", + "moment": "^2.30.1", + "nanoid": "^5.0.3", + "notistack": "^3.0.1", + "prettier": "^3.2.5", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.12", + "react-router-dom": "^6.21.3", + "swr": "^2.2.4", + "typescript": "^5.2.2", + "use-debounce": "^9.0.4", + "vite": "^5.0.8", + "vite-plugin-dts": "^3.7.2", + "zustand": "^4.3.8" + }, + "peerDependencies": { + "@emoji-mart/data": "^1.1.2", + "@emoji-mart/react": "^1.1.1", + "@emotion/react": "^11.10.5", + "@emotion/styled": "^11.10.5", + "@mui/icons-material": "^5.10.14", + "@mui/material": "^5.10.14", + "@mui/x-date-pickers": "^6.16.1", + "axios": "^1.5.1", + "emoji-mart": "^5.5.2", + "immer": "^10.0.3", + "moment": "^2.30.1", + "nanoid": "^5.0.3", + "notistack": "^3.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-error-boundary": "^4.0.12", + "react-router-dom": "^6.21.3", + "swr": "^2.2.4", + "use-debounce": "^9.0.4", + "zustand": "^4.3.8" + }, + "dependencies": { + "bowser": "1.9.4", + "country-flag-emoji-polyfill": "^0.1.8", + "current-device": "^0.10.2", + "hex-rgb": "^5.0.0", + "mobile-detect": "^1.4.5", + "mui-tel-input": "^5.1.2", + "react-imask": "^7.6.0", + "react-phone-number-input": "^3.4.1" + }, + "prettier": { + "semi": true, + "trailingComma": "es5", + "singleQuote": false, + "printWidth": 120, + "tabWidth": 2, + "useTabs": false, + "endOfLine": "auto", + "bracketSpacing": true, + "arrowParens": "always", + "jsxSingleQuote": false + }, + "lint-staged": { + "*": "prettier --write --ignore-unknown" + } } diff --git a/yarn.lock b/yarn.lock index c77b4df..5451ba1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1225,11 +1225,21 @@ ansi-escapes@^4.3.0: dependencies: type-fest "^0.21.3" +ansi-escapes@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-6.2.1.tgz#76c54ce9b081dad39acec4b5d53377913825fb0f" + integrity sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1244,6 +1254,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.0.0, ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + arch@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" @@ -1380,6 +1395,13 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + browserslist@^4.22.2: version "4.23.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" @@ -1451,6 +1473,11 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + check-more-types@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" @@ -1478,6 +1505,13 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + cli-table3@~0.6.1: version "0.6.4" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.4.tgz#d1c536b8a3f2e7bec58f67ac9e5769b1b30088b0" @@ -1495,6 +1529,14 @@ cli-truncate@^2.1.0: slice-ansi "^3.0.0" string-width "^4.2.0" +cli-truncate@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-4.0.0.tgz#6cc28a2924fee9e25ce91e973db56c7066e6172a" + integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== + dependencies: + slice-ansi "^5.0.0" + string-width "^7.0.0" + client-only@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" @@ -1534,7 +1576,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.16: +colorette@^2.0.16, colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -1556,6 +1598,11 @@ commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@~12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + common-tags@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -1612,7 +1659,7 @@ country-flag-icons@^1.5.11: resolved "https://registry.yarnpkg.com/country-flag-icons/-/country-flag-icons-1.5.11.tgz#04c0556728e517a6207946656355698ac6237080" integrity sha512-B+mvFywunkRJs270k7kCBjhogvIA0uNn6GAXv6m2cPn3rrwqZzZVr2gBWcz+Cz7OGVWlcbERlYRIX0S6OGr8Bw== -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1710,6 +1757,13 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" +debug@~4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -1769,6 +1823,11 @@ emoji-mart@^5.5.2: resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.6.0.tgz#71b3ed0091d3e8c68487b240d9d6d9a73c27f023" integrity sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow== +emoji-regex@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1967,6 +2026,11 @@ eventemitter2@6.4.7: resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + execa@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" @@ -1982,6 +2046,21 @@ execa@4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@~8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^8.0.1" + human-signals "^5.0.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^4.1.0" + strip-final-newline "^3.0.0" + executable@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" @@ -2076,6 +2155,13 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -2170,6 +2256,11 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +get-east-asian-width@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" + integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== + get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" @@ -2188,6 +2279,11 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + getos@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" @@ -2346,6 +2442,16 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +husky@^9.0.11: + version "9.0.11" + resolved "https://registry.yarnpkg.com/husky/-/husky-9.0.11.tgz#fc91df4c756050de41b3e478b2158b87c1e79af9" + integrity sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw== + ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -2445,6 +2551,18 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-fullwidth-code-point@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz#9609efced7c2f97da7b60145ef481c787c7ba704" + integrity sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA== + dependencies: + get-east-asian-width "^1.0.0" + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -2475,6 +2593,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -2613,11 +2736,32 @@ libphonenumber-js@^1.10.55, libphonenumber-js@^1.10.61: resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz#2596683e1876bfee74082bb49339fe0a85ae34f9" integrity sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw== +lilconfig@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.1.tgz#9d8a246fa753106cfc205fd2d77042faca56e5e3" + integrity sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lint-staged@^15.2.5: + version "15.2.5" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.5.tgz#8c342f211bdb34ffd3efd1311248fa6b50b43b50" + integrity sha512-j+DfX7W9YUvdzEZl3Rk47FhDF6xwDBV5wwsCPw6BwWZVPYJemusQmvb9bRsW23Sqsaa+vRloAWogbK4BUuU2zA== + dependencies: + chalk "~5.3.0" + commander "~12.1.0" + debug "~4.3.4" + execa "~8.0.1" + lilconfig "~3.1.1" + listr2 "~8.2.1" + micromatch "~4.0.7" + pidtree "~0.6.0" + string-argv "~0.3.2" + yaml "~2.4.2" + listr2@^3.8.3: version "3.14.0" resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" @@ -2632,6 +2776,18 @@ listr2@^3.8.3: through "^2.3.8" wrap-ansi "^7.0.0" +listr2@~8.2.1: + version "8.2.1" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-8.2.1.tgz#06a1a6efe85f23c5324180d7c1ddbd96b5eefd6d" + integrity sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g== + dependencies: + cli-truncate "^4.0.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^6.0.0" + rfdc "^1.3.1" + wrap-ansi "^9.0.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -2682,6 +2838,17 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +log-update@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.0.0.tgz#0ddeb7ac6ad658c944c1de902993fce7c33f5e59" + integrity sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw== + dependencies: + ansi-escapes "^6.2.0" + cli-cursor "^4.0.0" + slice-ansi "^7.0.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" + loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -2728,6 +2895,14 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +micromatch@~4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -2745,6 +2920,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + minimatch@9.0.3: version "9.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" @@ -2846,6 +3026,13 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2870,6 +3057,13 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -2945,6 +3139,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -2975,6 +3174,11 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pidtree@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + pify@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -2994,6 +3198,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier@^3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368" + integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A== + pretty-bytes@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -3186,12 +3395,20 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfdc@^1.3.0: +rfdc@^1.3.0, rfdc@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== @@ -3315,6 +3532,11 @@ signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3338,6 +3560,22 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +slice-ansi@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.0.tgz#cd6b4655e298a8d1bdeb04250a433094b347b9a9" + integrity sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg== + dependencies: + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" + source-map-js@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" @@ -3373,7 +3611,7 @@ sshpk@^1.14.1: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -string-argv@~0.3.1: +string-argv@~0.3.1, string-argv@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== @@ -3387,6 +3625,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.1.0.tgz#d994252935224729ea3719c49f7206dc9c46550a" + integrity sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -3394,11 +3641,23 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -3687,6 +3946,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.0.tgz#1a3dc8b70d85eeb8398ddfb1e4a02cd186e58b3e" + integrity sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -3707,6 +3975,11 @@ yaml@^1.10.0: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@~2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" + integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" From 0baf300f4e9b4e4ced889f45f7350a9e3ab79566 Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 31 May 2024 19:24:59 +0300 Subject: [PATCH 42/76] fix console errors --- lib/assets/icons/NameplateLogo.tsx | 102 ++++++++++++++++++++++------- src/widgets/shared/BannerIcon.tsx | 64 ++++++++++++------ 2 files changed, 123 insertions(+), 43 deletions(-) diff --git a/lib/assets/icons/NameplateLogo.tsx b/lib/assets/icons/NameplateLogo.tsx index 1338a72..097c662 100644 --- a/lib/assets/icons/NameplateLogo.tsx +++ b/lib/assets/icons/NameplateLogo.tsx @@ -2,27 +2,85 @@ import { FC, SVGProps } from "react"; export const NameplateLogo: FC> = (props) => ( - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + ); diff --git a/src/widgets/shared/BannerIcon.tsx b/src/widgets/shared/BannerIcon.tsx index fd3b402..beac75b 100644 --- a/src/widgets/shared/BannerIcon.tsx +++ b/src/widgets/shared/BannerIcon.tsx @@ -1,25 +1,47 @@ import { Box } from "@mui/material"; - export default function BannerIcon() { - - return ( - - - - - - - - - ); + return ( + + + + + + + + + ); } From 6c2e44ca664cc5f0498ed922dc95bff8a608e41d Mon Sep 17 00:00:00 2001 From: nflnkr Date: Fri, 31 May 2024 19:41:18 +0300 Subject: [PATCH 43/76] run prettier on whole project --- .eslintrc.cjs | 70 ++- README.md | 33 +- cypress.config.ts | 20 +- deployments/main/docker-compose.yaml | 15 +- deployments/staging/docker-compose.yaml | 15 +- lib/api/quizRelase.ts | 330 ++++++------ lib/assets/icons/ArrowDownIcon.tsx | 55 +- lib/assets/icons/BlankImage.tsx | 34 +- lib/assets/icons/CalendarIcon.tsx | 6 +- lib/assets/icons/Checkbox.tsx | 21 +- lib/assets/icons/CloseBold.tsx | 33 +- .../icons/ContactFormIcon/AddressIcon.tsx | 12 +- .../icons/ContactFormIcon/EmailIcon.tsx | 19 +- lib/assets/icons/ContactFormIcon/NameIcon.tsx | 23 +- .../icons/ContactFormIcon/PhoneIcon.tsx | 14 +- lib/assets/icons/ContactFormIcon/TextIcon.tsx | 47 +- lib/assets/icons/Info.tsx | 16 +- lib/assets/icons/NameplateLogoFQ.tsx | 37 +- lib/assets/icons/NameplateLogoFQDark.tsx | 62 ++- lib/assets/icons/UploadIcon.tsx | 56 +- lib/assets/icons/questionsPage/FlagIcon.tsx | 21 +- .../icons/questionsPage/hashtagIcon.tsx | 10 +- lib/assets/icons/questionsPage/heartIcon.tsx | 10 +- .../icons/questionsPage/lightbulbIcon.tsx | 20 +- lib/assets/icons/questionsPage/likeIcon.tsx | 10 +- lib/assets/icons/questionsPage/tropfyIcon.tsx | 26 +- lib/components/QuizAnswerer.tsx | 59 +-- .../ViewPublicationPage/ApologyPage.tsx | 50 +- .../ContactForm/ContactForm.tsx | 358 ++++++------- .../ContactForm/ContactTextBlock/index.tsx | 123 +++-- .../CountrySelector/CountrySelector.tsx | 124 +++-- .../ContactForm/CustomInput/CustomInput.tsx | 145 +++-- .../ContactForm/Inputs/Inputs.tsx | 186 ++++--- lib/components/ViewPublicationPage/Footer.tsx | 86 ++- .../ViewPublicationPage/Question.tsx | 18 +- .../ViewPublicationPage/QuestionSelect.tsx | 198 +++---- .../ViewPublicationPage/ResultForm.tsx | 466 ++++++++--------- .../QuizPreviewLayoutByType.tsx | 5 +- .../StartPageDesktop.tsx | 492 +++++++++-------- .../StartPageMobile.tsx | 495 +++++++++--------- .../StartPageViewPublication/index.tsx | 194 +++---- .../ViewPublicationPage.tsx | 44 +- .../questions/Date/index.tsx | 14 +- .../questions/Emoji/EmojiVariant.tsx | 42 +- .../questions/Emoji/index.tsx | 17 +- .../questions/File/UploadFile.tsx | 54 +- .../questions/File/UploadedFile.tsx | 9 +- .../questions/File/index.tsx | 46 +- .../questions/Images/ImageVariant.tsx | 22 +- .../questions/Images/index.tsx | 20 +- .../questions/Number/index.tsx | 181 ++----- .../questions/Page/index.tsx | 5 +- .../questions/Rating/index.tsx | 66 +-- .../questions/Select/index.tsx | 9 +- .../questions/Text/TextNormal.tsx | 47 +- .../questions/Text/TextSpecial.tsx | 69 +-- .../questions/Text/index.tsx | 28 +- .../questions/Variant/VariantItem.tsx | 29 +- .../questions/Variant/index.tsx | 46 +- .../questions/Varimg/VarimgVariant.tsx | 25 +- .../questions/Varimg/index.tsx | 20 +- .../ViewPublicationPage/tools/NextButton.tsx | 47 +- .../ViewPublicationPage/tools/PrevButton.tsx | 60 ++- .../ViewPublicationPage/tools/Select.tsx | 31 +- .../tools/checkEmptyData.ts | 23 +- .../ViewPublicationPage/tools/fileUpload.ts | 76 +-- .../tools/replaceSpacesToEmptyLines.ts | 6 +- lib/contexts/QuizDataContext.ts | 12 +- lib/contexts/RootContainerWidthContext.ts | 7 +- lib/model/api/getQuizData.ts | 88 ++-- lib/model/metrics.ts | 9 +- lib/model/questionTypes/date.ts | 6 +- lib/model/questionTypes/emoji.ts | 7 +- lib/model/questionTypes/file.ts | 6 +- lib/model/questionTypes/images.ts | 63 +-- lib/model/questionTypes/number.ts | 6 +- lib/model/questionTypes/page.ts | 6 +- lib/model/questionTypes/rating.ts | 6 +- lib/model/questionTypes/result.ts | 12 +- lib/model/questionTypes/select.ts | 7 +- lib/model/questionTypes/shared.ts | 172 +++--- lib/model/questionTypes/text.ts | 6 +- lib/model/questionTypes/variant.ts | 7 +- lib/model/questionTypes/varimg.ts | 47 +- lib/model/settingsData.ts | 11 +- lib/model/widget/banner.ts | 48 +- lib/model/widget/button.ts | 44 +- lib/model/widget/container.ts | 16 +- lib/model/widget/popup.ts | 18 +- lib/model/widget/side.ts | 32 +- lib/stores/quizView.ts | 143 +++-- lib/ui_kit/CustomCheckbox.tsx | 1 - lib/ui_kit/CustomSlider.tsx | 5 +- lib/ui_kit/CustomTextField.tsx | 115 ++-- lib/ui_kit/LoadingSkeleton.tsx | 24 +- lib/ui_kit/RadioCheck.tsx | 39 +- lib/ui_kit/RadioIcon.tsx | 36 +- lib/utils/defineDomain.ts | 13 +- lib/utils/designList.ts | 43 +- lib/utils/emailRegexp.ts | 2 +- lib/utils/handleComponentError.ts | 49 +- lib/utils/hooks/metrics/useVKMetrics.ts | 6 +- lib/utils/hooks/metrics/useVkMetricsGoals.ts | 12 +- lib/utils/hooks/metrics/useYandexMetrics.ts | 6 +- .../hooks/metrics/useYandexMetricsGoals.ts | 12 +- lib/utils/hooks/useQuestionFlowControl.ts | 108 +--- lib/utils/hooks/useUADevice.ts | 14 +- lib/utils/notReachable.ts | 2 +- lib/utils/parse-error.ts | 19 +- lib/utils/phoneMasksByCountry.tsx | 149 +++--- .../themes/Publication/genericPublication.ts | 77 ++- .../themes/Publication/themePublication.ts | 47 +- lib/utils/themes/dark.ts | 85 ++- lib/utils/themes/fontFace.ts | 1 - lib/utils/themes/generic.ts | 251 ++++----- lib/utils/themes/light.ts | 97 ++-- lib/utils/themes/mui.d.ts | 80 +-- public/site.webmanifest | 38 +- src/App.tsx | 20 +- src/WidgetDev.tsx | 109 ++-- src/main.tsx | 43 +- src/widget.tsx | 31 +- src/widgets/banner/BannerWidget.tsx | 36 +- src/widgets/banner/QuizBanner.tsx | 391 +++++++------- src/widgets/button/ButtonWidget.tsx | 79 ++- src/widgets/button/OpenQuizButton.tsx | 227 ++++---- src/widgets/container/ContainerWidget.tsx | 49 +- src/widgets/container/QuizContainer.tsx | 37 +- src/widgets/popup/PopupWidget.tsx | 33 +- src/widgets/popup/QuizPopup.tsx | 123 +++-- src/widgets/shared/QuizDialog.tsx | 124 ++--- src/widgets/shared/RunningStripe.tsx | 56 +- src/widgets/shared/pollForSelector.ts | 34 +- src/widgets/shared/useAutoOpenTimer.ts | 22 +- src/widgets/shared/useQuizCompletionStatus.ts | 18 +- src/widgets/side/QuizSideButton.tsx | 194 +++---- src/widgets/side/SideWidget.tsx | 31 +- tsconfig.json | 95 ++-- vite.config.package.ts | 72 +-- vite.config.ts | 30 +- vite.config.widget.ts | 56 +- widget-test.html | 174 +++--- 142 files changed, 4210 insertions(+), 5034 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1684a63..491be2b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,40 +1,30 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react-hooks/recommended", - ], - ignorePatterns: ["dist", ".eslintrc.cjs"], - parser: "@typescript-eslint/parser", - plugins: ["react-refresh"], - rules: { - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-empty-interface": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-inferrable-types": "off", - "@typescript-eslint/no-misused-promises": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unused-vars": [ - "warn", - { "vars": "all", "args": "none" } - ], - "@typescript-eslint/restrict-template-expressions": "off", - "no-debugger": "off", - "no-empty-function": "off", - "no-empty-pattern": "off", - "no-empty": "off", - "prefer-const": "warn", - }, -}; +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react-hooks/recommended"], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/no-misused-promises": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unused-vars": ["warn", { vars: "all", args: "none" }], + "@typescript-eslint/restrict-template-expressions": "off", + "no-debugger": "off", + "no-empty-function": "off", + "no-empty-pattern": "off", + "no-empty": "off", + "prefer-const": "warn", + }, +}; diff --git a/README.md b/README.md index 1d63886..f65f0e0 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,68 @@ ## Правила + - Запрещено использовать vh/vw css-юниты и их производные + ## Виджет + ### Сборка + ```bash yarn build:widget ``` + ### Использование + ```html ``` + ## Npm-пакет + ### Перед использованием и публикацией + ```bash npm config set //penahub.gitlab.yandexcloud.net/api/v4/packages/npm/:_authToken=INSTANCE_TOKEN npm config set //penahub.gitlab.yandexcloud.net/api/v4/projects/43/packages/npm/:_authToken=PROJECT_TOKEN ``` + ### Публикация + 1. Инкрементировать версию в package.json -2. +2. + ```bash yarn publish ``` + 3. Нажать enter при запросе версии + ### Установка + Добавить в корень проекта файл .yarnrc с содержимым + ``` "@frontend:registry" "https://penahub.gitlab.yandexcloud.net/api/v4/packages/npm/" ``` + ```bash yarn add @frontend/squzanswerer ``` + Peer dependencies: + ```bash yarn add @emoji-mart/data @emoji-mart/react @emotion/react @emotion/styled @mui/icons-material @mui/material @mui/x-date-pickers axios emoji-mart immer moment nanoid notistack react-dom react-error-boundary react-router-dom react swr use-debounce zustand ``` + ### Использование + ```ts import { QuizView } from "@frontend/squzanswerer"; diff --git a/cypress.config.ts b/cypress.config.ts index 87067ad..a5f6f74 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,10 +1,10 @@ -import { defineConfig } from "cypress"; - -export default defineConfig({ - e2e: { - baseUrl: 'http://localhost:3000', - viewportWidth: 1440, - viewportHeight: 900, - supportFile: false, - }, -}); +import { defineConfig } from "cypress"; + +export default defineConfig({ + e2e: { + baseUrl: "http://localhost:3000", + viewportWidth: 1440, + viewportHeight: 900, + supportFile: false, + }, +}); diff --git a/deployments/main/docker-compose.yaml b/deployments/main/docker-compose.yaml index 8dacb5e..a93a068 100644 --- a/deployments/main/docker-compose.yaml +++ b/deployments/main/docker-compose.yaml @@ -1,8 +1,7 @@ -services: - respondent: - container_name: respondent - restart: unless-stopped - image: $CI_REGISTRY_IMAGE/main:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID - hostname: respondent - tty: true - +services: + respondent: + container_name: respondent + restart: unless-stopped + image: $CI_REGISTRY_IMAGE/main:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + hostname: respondent + tty: true diff --git a/deployments/staging/docker-compose.yaml b/deployments/staging/docker-compose.yaml index d258d2e..1b71007 100644 --- a/deployments/staging/docker-compose.yaml +++ b/deployments/staging/docker-compose.yaml @@ -1,8 +1,7 @@ -services: - respondent: - container_name: respondent - restart: unless-stopped - image: $CI_REGISTRY_IMAGE/staging:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID - hostname: respondent - tty: true - +services: + respondent: + container_name: respondent + restart: unless-stopped + image: $CI_REGISTRY_IMAGE/staging:$CI_COMMIT_REF_SLUG.$CI_PIPELINE_ID + hostname: respondent + tty: true diff --git a/lib/api/quizRelase.ts b/lib/api/quizRelase.ts index 77b1e07..eab13fe 100644 --- a/lib/api/quizRelase.ts +++ b/lib/api/quizRelase.ts @@ -1,6 +1,6 @@ import { GetQuizDataResponse, parseQuizData } from "@model/api/getQuizData"; import axios from "axios"; -import MobileDetect from 'mobile-detect'; +import MobileDetect from "mobile-detect"; import device from "current-device"; import type { AxiosError } from "axios"; @@ -10,226 +10,240 @@ import * as Bowser from "bowser"; import { domain } from "../utils/defineDomain"; let SESSIONS = ""; - const md = new MobileDetect(window.navigator.userAgent); const userAgent = navigator.userAgent; //операционная система let OSDevice: string | undefined; -if (userAgent.toLowerCase().includes("linux")) { OSDevice = "Linux"; } -if (userAgent.toLowerCase().includes("windows")) { OSDevice = "Windows"; } -if (/iPad|iPhone|iPod/.test(userAgent)) { OSDevice = "IOS"; } -if (userAgent.toLowerCase().includes("macintosh")) { OSDevice = "Mac OS"; } -if (OSDevice === undefined) { OSDevice = userAgent; } +if (userAgent.toLowerCase().includes("linux")) { + OSDevice = "Linux"; +} +if (userAgent.toLowerCase().includes("windows")) { + OSDevice = "Windows"; +} +if (/iPad|iPhone|iPod/.test(userAgent)) { + OSDevice = "IOS"; +} +if (userAgent.toLowerCase().includes("macintosh")) { + OSDevice = "Mac OS"; +} +if (OSDevice === undefined) { + OSDevice = userAgent; +} //браузер let browserUser: string; if (Bowser.name === "Chrome") { - browserUser = "Chrome"; + browserUser = "Chrome"; } else if (Bowser.name === "Firefox") { - browserUser = "Firefox"; + browserUser = "Firefox"; } else if (Bowser.name === "Safari") { - browserUser = "Safari"; + browserUser = "Safari"; } else if (Bowser.name === "Yandex Browser") { - browserUser = "Yandex Browser"; -} else { browserUser = userAgent; } + browserUser = "Yandex Browser"; +} else { + browserUser = userAgent; +} const DeviceType = device.type; let Device = md.mobile(); -if (Device === null) { Device = userAgent; } +if (Device === null) { + Device = userAgent; +} type PublicationMakeRequestParams = { - url: string; - body: FormData; - method: "POST"; + url: string; + body: FormData; + method: "POST"; }; export const publicationMakeRequest = ({ url, body }: PublicationMakeRequestParams) => { - return axios(url, { - data: body, - headers: { - "X-Sessionkey": SESSIONS, - "Content-Type": "multipart/form-data", - "DeviceType": DeviceType, - "Device": Device, - "OS": OSDevice, - "Browser": browserUser - }, - method: "POST", - }); + return axios(url, { + data: body, + headers: { + "X-Sessionkey": SESSIONS, + "Content-Type": "multipart/form-data", + DeviceType: DeviceType, + Device: Device, + OS: OSDevice, + Browser: browserUser, + }, + method: "POST", + }); }; export async function getData(quizId: string): Promise<{ - data: GetQuizDataResponse | null; - isRecentlyCompleted: boolean; - error?: AxiosError; + data: GetQuizDataResponse | null; + isRecentlyCompleted: boolean; + error?: AxiosError; }> { - try { - const { data, headers } = await axios( - domain + `/answer/settings`, - { - method: "POST", - headers: { - "X-Sessionkey": SESSIONS, - "Content-Type": "application/json", - "DeviceType": DeviceType, - "Device": Device, - "OS": OSDevice, - "Browser": userAgent - }, - data: { - quiz_id: quizId, - limit: 100, - page: 0, - need_config: true, - }, - } - ); - const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); + try { + const { data, headers } = await axios(domain + `/answer/settings`, { + method: "POST", + headers: { + "X-Sessionkey": SESSIONS, + "Content-Type": "application/json", + DeviceType: DeviceType, + Device: Device, + OS: OSDevice, + Browser: userAgent, + }, + data: { + quiz_id: quizId, + limit: 100, + page: 0, + need_config: true, + }, + }); + const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - if (typeof sessions[quizId] === "number") { - // unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше - if (Date.now() - sessions[quizId] < 86400000) { - return { data, isRecentlyCompleted: true }; - } - } - - SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS; - - return { data, isRecentlyCompleted: false }; - } catch (nativeError) { - const error = nativeError as AxiosError; - - return { data: null, isRecentlyCompleted: false, error: error }; + if (typeof sessions[quizId] === "number") { + // unix время. Если меньше суток прошло - выводить ошибку, иначе пустить дальше + if (Date.now() - sessions[quizId] < 86400000) { + return { data, isRecentlyCompleted: true }; + } } + + SESSIONS = headers["x-sessionkey"] ? headers["x-sessionkey"] : SESSIONS; + + return { data, isRecentlyCompleted: false }; + } catch (nativeError) { + const error = nativeError as AxiosError; + + return { data: null, isRecentlyCompleted: false, error: error }; + } } export async function getQuizData(quizId: string) { - if (!quizId) throw new Error("No quiz id"); + if (!quizId) throw new Error("No quiz id"); - const response = await getData(quizId); - const quizDataResponse = response.data; + const response = await getData(quizId); + const quizDataResponse = response.data; - if (response.error) { - throw response.error; - } - if (!quizDataResponse) { - throw new Error("Quiz not found"); - } + if (response.error) { + throw response.error; + } + if (!quizDataResponse) { + throw new Error("Quiz not found"); + } - const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse)); + const quizSettings = replaceSpacesToEmptyLines(parseQuizData(quizDataResponse)); - const res = JSON.parse(JSON.stringify({ data: quizSettings }).replaceAll(/\\" \\"/g, '""').replaceAll(/" "/g, '""')).data as QuizSettings; - res.recentlyCompleted = response.isRecentlyCompleted; - return res; + const res = JSON.parse( + JSON.stringify({ data: quizSettings }) + .replaceAll(/\\" \\"/g, '""') + .replaceAll(/" "/g, '""') + ).data as QuizSettings; + res.recentlyCompleted = response.isRecentlyCompleted; + return res; } type SendAnswerProps = { - questionId: string; - body: string | string[]; - qid: string; - preview: boolean; + questionId: string; + body: string | string[]; + qid: string; + preview: boolean; }; export function sendAnswer({ questionId, body, qid, preview }: SendAnswerProps) { - if (preview) return; - const formData = new FormData(); + if (preview) return; + const formData = new FormData(); - const answers = [ - { - question_id: questionId, - content: body, //тут массив с ответом - }, - ]; - formData.append("answers", JSON.stringify(answers)); - console.log("QID", qid); - formData.append("qid", qid); + const answers = [ + { + question_id: questionId, + content: body, //тут массив с ответом + }, + ]; + formData.append("answers", JSON.stringify(answers)); + console.log("QID", qid); + formData.append("qid", qid); - return publicationMakeRequest({ - url: domain + `/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: domain + `/answer/answer`, + body: formData, + method: "POST", + }); } //body ={file, filename} type SendFileParams = { - questionId: string; - body: { - name: string; - file: File; - preview: boolean; - }; - qid: string; + questionId: string; + body: { + name: string; + file: File; + preview: boolean; + }; + qid: string; }; type Answer = { - question_id: string; - content: string; + question_id: string; + content: string; }; export function sendFile({ questionId, body, qid }: SendFileParams) { - if (body.preview) return; - const formData = new FormData(); + if (body.preview) return; + const formData = new FormData(); - const answers: Answer[] = [ - { - question_id: questionId, - content: "file:" + body.name, - }, - ]; + const answers: Answer[] = [ + { + question_id: questionId, + content: "file:" + body.name, + }, + ]; - formData.append("answers", JSON.stringify(answers)); - formData.append(body.name, body.file); - console.log("QID", qid); - formData.append("qid", qid); + formData.append("answers", JSON.stringify(answers)); + formData.append(body.name, body.file); + console.log("QID", qid); + formData.append("qid", qid); - return publicationMakeRequest({ - url: domain + `/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: domain + `/answer/answer`, + body: formData, + method: "POST", + }); } //форма контактов export type SendFCParams = { - questionId: string; - body: { - name?: string; - email?: string; - phone?: string; - address?: string; - customs?: Record; - }; - qid: string; - preview: boolean; + questionId: string; + body: { + name?: string; + email?: string; + phone?: string; + address?: string; + customs?: Record; + }; + qid: string; + preview: boolean; }; export function sendFC({ questionId, body, qid, preview }: SendFCParams) { - if (preview) return; - const formData = new FormData(); + if (preview) return; + const formData = new FormData(); - // const keysBody = Object.keys(body) - // const content:any = {} - // fields.forEach((key) => { - // if (keysBody.includes(key)) content[key] = body.key - // }) + // const keysBody = Object.keys(body) + // const content:any = {} + // fields.forEach((key) => { + // if (keysBody.includes(key)) content[key] = body.key + // }) - const answers = [ - { - question_id: questionId, - content: JSON.stringify(body), - result: true, - qid, - }, - ]; + const answers = [ + { + question_id: questionId, + content: JSON.stringify(body), + result: true, + qid, + }, + ]; - formData.append("answers", JSON.stringify(answers)); - formData.append("qid", qid); + formData.append("answers", JSON.stringify(answers)); + formData.append("qid", qid); - return publicationMakeRequest({ - url: domain + `/answer/answer`, - body: formData, - method: "POST", - }); + return publicationMakeRequest({ + url: domain + `/answer/answer`, + body: formData, + method: "POST", + }); } diff --git a/lib/assets/icons/ArrowDownIcon.tsx b/lib/assets/icons/ArrowDownIcon.tsx index c62184b..2d6f602 100644 --- a/lib/assets/icons/ArrowDownIcon.tsx +++ b/lib/assets/icons/ArrowDownIcon.tsx @@ -1,29 +1,32 @@ -import {Box, SxProps, Theme, useTheme} from "@mui/material"; +import { Box, SxProps, Theme, useTheme } from "@mui/material"; -interface Color{ - color?: string +interface Color { + color?: string; } -export default function ArrowDownIcon( - props: any, - {color = "#7E2AEA"}: Color -) { - const theme = useTheme(); +export default function ArrowDownIcon(props: any, { color = "#7E2AEA" }: Color) { + const theme = useTheme(); - return ( - - - - - - ); -} \ No newline at end of file + return ( + + + + + + ); +} diff --git a/lib/assets/icons/BlankImage.tsx b/lib/assets/icons/BlankImage.tsx index 6e976bc..93f2630 100644 --- a/lib/assets/icons/BlankImage.tsx +++ b/lib/assets/icons/BlankImage.tsx @@ -1,10 +1,26 @@ export default function BlankImage() { - - return ( - - - - - - ); -} + return ( + + + + + + ); +} diff --git a/lib/assets/icons/CalendarIcon.tsx b/lib/assets/icons/CalendarIcon.tsx index 1e1a930..2a127cd 100644 --- a/lib/assets/icons/CalendarIcon.tsx +++ b/lib/assets/icons/CalendarIcon.tsx @@ -1,6 +1,6 @@ -import { Box, SxProps, Theme } from "@mui/material"; +import { Box, SxProps, Theme } from "@mui/material"; interface Props { - sx?: SxProps; + sx?: SxProps; } export default function CalendarIcon({ sx }: Props) { return ( @@ -22,7 +22,7 @@ export default function CalendarIcon({ sx }: Props) { "&:active rect": { stroke: "#FB5607", }, - ...sx + ...sx, }} > diff --git a/lib/assets/icons/Checkbox.tsx b/lib/assets/icons/Checkbox.tsx index 52f5a8a..d95ca7a 100644 --- a/lib/assets/icons/Checkbox.tsx +++ b/lib/assets/icons/Checkbox.tsx @@ -5,7 +5,7 @@ type CheckboxIconProps = { color?: string; }; -export const CheckboxIcon = ({ checked = false, color = "#7E2AEA", }: CheckboxIconProps) => { +export const CheckboxIcon = ({ checked = false, color = "#7E2AEA" }: CheckboxIconProps) => { const theme = useTheme(); return ( @@ -17,26 +17,13 @@ export const CheckboxIcon = ({ checked = false, color = "#7E2AEA", }: CheckboxIc display: "flex", justifyContent: "center", alignItems: "center", - backgroundColor: checked - ? color - : "#F2F3F7", + backgroundColor: checked ? color : "#F2F3F7", border: `1px solid #9A9AAF`, }} > {checked && ( - - + + )} diff --git a/lib/assets/icons/CloseBold.tsx b/lib/assets/icons/CloseBold.tsx index 84942f7..e58a885 100644 --- a/lib/assets/icons/CloseBold.tsx +++ b/lib/assets/icons/CloseBold.tsx @@ -8,13 +8,7 @@ export default function CloseBold({ width }: Props) { const theme = useTheme(); return ( - + @@ -55,30 +49,13 @@ export default function CloseBold({ width }: Props) { values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" /> - + - - - + + + diff --git a/lib/assets/icons/ContactFormIcon/AddressIcon.tsx b/lib/assets/icons/ContactFormIcon/AddressIcon.tsx index cd9abf0..fa22355 100644 --- a/lib/assets/icons/ContactFormIcon/AddressIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/AddressIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function AddressIcon({ color, backgroundColor }: Props) { @@ -14,18 +14,12 @@ export default function AddressIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - + - - - - - + + + + ); } diff --git a/lib/assets/icons/ContactFormIcon/NameIcon.tsx b/lib/assets/icons/ContactFormIcon/NameIcon.tsx index 8aa0dbe..ae04cab 100644 --- a/lib/assets/icons/ContactFormIcon/NameIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/NameIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function NameIcon({ color, backgroundColor }: Props) { @@ -14,15 +14,26 @@ export default function NameIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - - - - + + + + ); } diff --git a/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx b/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx index 394bb4f..89982b3 100644 --- a/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/PhoneIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function PhoneIcon({ color, backgroundColor }: Props) { @@ -14,14 +14,18 @@ export default function PhoneIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - - - + + + ); } diff --git a/lib/assets/icons/ContactFormIcon/TextIcon.tsx b/lib/assets/icons/ContactFormIcon/TextIcon.tsx index f9c644f..73527e4 100644 --- a/lib/assets/icons/ContactFormIcon/TextIcon.tsx +++ b/lib/assets/icons/ContactFormIcon/TextIcon.tsx @@ -2,7 +2,7 @@ import { Box } from "@mui/material"; interface Props { color: string; - backgroundColor: string + backgroundColor: string; } export default function TextIcon({ color, backgroundColor }: Props) { @@ -14,24 +14,13 @@ export default function TextIcon({ color, backgroundColor }: Props) { justifyContent: "center", height: "58px", width: "45px", - backgroundColor: {backgroundColor}, + backgroundColor: { backgroundColor }, borderBottomLeftRadius: "12px", borderTopLeftRadius: "12px", }} > - - + + - - - - + + + + ); diff --git a/lib/assets/icons/Info.tsx b/lib/assets/icons/Info.tsx index f319760..e3611fe 100644 --- a/lib/assets/icons/Info.tsx +++ b/lib/assets/icons/Info.tsx @@ -6,23 +6,13 @@ type InfoProps = { sx?: SxProps; onClick?: () => void; className?: string; - color?: string + color?: string; }; export default function Info({ width = 20, height = 20, sx, onClick, className, color = "#7e2aea" }: InfoProps) { return ( - - + + > = (props) => ( - + - + - - - - - + + + + + diff --git a/lib/assets/icons/NameplateLogoFQDark.tsx b/lib/assets/icons/NameplateLogoFQDark.tsx index 9916f39..a893b47 100644 --- a/lib/assets/icons/NameplateLogoFQDark.tsx +++ b/lib/assets/icons/NameplateLogoFQDark.tsx @@ -1,24 +1,46 @@ import { FC, SVGProps } from "react"; export const NameplateLogoFQDark: FC> = (props) => ( - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + ); diff --git a/lib/assets/icons/UploadIcon.tsx b/lib/assets/icons/UploadIcon.tsx index 573fcbf..8c6dc39 100644 --- a/lib/assets/icons/UploadIcon.tsx +++ b/lib/assets/icons/UploadIcon.tsx @@ -1,27 +1,39 @@ import { Box, useTheme } from "@mui/material"; -interface Props{ - color?: string +interface Props { + color?: string; } -export default function UploadIcon({color= "#9A9AAF"}: Props) { - const theme = useTheme(); +export default function UploadIcon({ color = "#9A9AAF" }: Props) { + const theme = useTheme(); - return ( - - - - - - - - ); -} \ No newline at end of file + return ( + + + + + + + + ); +} diff --git a/lib/assets/icons/questionsPage/FlagIcon.tsx b/lib/assets/icons/questionsPage/FlagIcon.tsx index 9eebf28..35c5265 100644 --- a/lib/assets/icons/questionsPage/FlagIcon.tsx +++ b/lib/assets/icons/questionsPage/FlagIcon.tsx @@ -16,24 +16,9 @@ export default function FlagIcon({ color, width = 30 }: Props) { justifyContent: "center", }} > - - - + + + - + - + - - + + - + - + - - + + ( - () => window.innerWidth - ); + const [rootContainerWidth, setRootContainerWidth] = useState(() => window.innerWidth); const rootContainerRef = useRef(null); const { data, error, isLoading } = useSWR( quizSettings ? null : ["quizData", quizId], @@ -73,12 +59,8 @@ function QuizAnswererInner({ refreshInterval: 0, } ); - const vkMetrics = useVkMetricsGoals( - quizSettings?.settings.cfg.vkMetricsNumber - ); - const yandexMetrics = useYandexMetricsGoals( - quizSettings?.settings.cfg.yandexMetricsNumber - ); + const vkMetrics = useVkMetricsGoals(quizSettings?.settings.cfg.vkMetricsNumber); + const yandexMetrics = useYandexMetricsGoals(quizSettings?.settings.cfg.yandexMetricsNumber); useEffect(() => { setTimeout(() => { @@ -88,15 +70,13 @@ function QuizAnswererInner({ }, []); useLayoutEffect(() => { - if (rootContainerRef.current) - setRootContainerWidth(rootContainerRef.current.clientWidth); + if (rootContainerRef.current) setRootContainerWidth(rootContainerRef.current.clientWidth); }, []); useEffect(() => { const handleWindowResize = () => { startTransition(() => { - if (rootContainerRef.current) - setRootContainerWidth(rootContainerRef.current.clientWidth); + if (rootContainerRef.current) setRootContainerWidth(rootContainerRef.current.clientWidth); }); }; window.addEventListener("resize", handleWindowResize); @@ -112,8 +92,7 @@ function QuizAnswererInner({ quizSettings ??= data; if (!quizSettings) throw new Error("Quiz data is null"); - if (quizSettings.questions.length === 0) - return ; + if (quizSettings.questions.length === 0) return ; if (!quizId) return ; const quizContainer = ( @@ -126,10 +105,7 @@ function QuizAnswererInner({ position: "relative", }} > - + @@ -138,9 +114,7 @@ function QuizAnswererInner({ return ( - + {disableGlobalCss ? ( + - + diff --git a/lib/components/ViewPublicationPage/ApologyPage.tsx b/lib/components/ViewPublicationPage/ApologyPage.tsx index f869827..a7c3e6b 100644 --- a/lib/components/ViewPublicationPage/ApologyPage.tsx +++ b/lib/components/ViewPublicationPage/ApologyPage.tsx @@ -4,30 +4,32 @@ import { FallbackProps } from "react-error-boundary"; type Props = Partial; export const ApologyPage = ({ error }: Props) => { - let message = "Что-то пошло не так"; + let message = "Что-то пошло не так"; - if (error.response?.data === "quiz is inactive") message = "Квиз не активирован"; - if (error.message === "No questions found") message = "Нет созданных вопросов"; - if (error.message === "Quiz already completed") message = "Вы уже прошли этот опрос"; - if (error.message === "No quiz id") message = "Отсутствует id квиза"; - if (error.response?.data === "Invalid request data") message = "Такого квиза не существует"; + if (error.response?.data === "quiz is inactive") message = "Квиз не активирован"; + if (error.message === "No questions found") message = "Нет созданных вопросов"; + if (error.message === "Quiz already completed") message = "Вы уже прошли этот опрос"; + if (error.message === "No quiz id") message = "Отсутствует id квиза"; + if (error.response?.data === "Invalid request data") message = "Такого квиза не существует"; - return ( - - {message} - - ); + return ( + + + {message} + + + ); }; diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx index 990b34c..12f8a67 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactForm.tsx @@ -21,10 +21,7 @@ import { DESIGN_LIST } from "@utils/designList"; import { NameplateLogo } from "@icons/NameplateLogo"; -import type { - FormContactFieldData, - FormContactFieldName, -} from "@model/settingsData"; +import type { FormContactFieldData, FormContactFieldName } from "@model/settingsData"; import type { QuizQuestionResult } from "@model/questionTypes/result"; import type { AnyTypedQuizQuestion } from "@model/questionTypes/shared"; @@ -69,18 +66,12 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { currentQuestion.type === "result" ? currentQuestion : questions.find((question): question is QuizQuestionResult => { - if (settings?.cfg.haveRoot) { - return ( - question.type === "result" && - question.content.rule.parentId === currentQuestion.content.id - ); - } else { - return ( - question.type === "result" && - question.content.rule.parentId === "line" - ); - } - }); + if (settings?.cfg.haveRoot) { + return question.type === "result" && question.content.rule.parentId === currentQuestion.content.id; + } else { + return question.type === "result" && question.content.rule.parentId === "line"; + } + }); if (!resultQuestion) throw new Error("Result question not found"); @@ -103,10 +94,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { }); const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); - localStorage.setItem( - "sessions", - JSON.stringify({ ...sessions, [quizId]: new Date().getTime() }) - ); + localStorage.setItem("sessions", JSON.stringify({ ...sessions, [quizId]: new Date().getTime() })); } catch (e) { enqueueSnackbar("ответ не был засчитан"); } @@ -116,9 +104,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { const FCcopy: Record = settings.cfg.formContact.fields || settings.cfg.formContact; - const filteredFC: Partial< - Record - > = {}; + const filteredFC: Partial> = {}; for (const i in FCcopy) { const field = FCcopy[i as keyof typeof FCcopy]; if (field.used) { @@ -133,13 +119,7 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { } if (fireOnce.current) { - if ( - name.length === 0 && - email.length === 0 && - phone.length === 0 && - text.length === 0 && - adress.length === 0 - ) + if (name.length === 0 && email.length === 0 && phone.length === 0 && text.length === 0 && adress.length === 0) return enqueueSnackbar("Пожалуйста, заполните поля"); //почта валидна, хоть одно поле заполнено @@ -189,172 +169,168 @@ export const ContactForm = ({ currentQuestion, onShowResult }: Props) => { yandexMetrics.contactsFormOpened(); }, []); - return ( + return ( + 500 ? "100%" : "auto", + overflow: "auto", + "&::-webkit-scrollbar": { + width: "0", + display: "none", + msOverflowStyle: "none", + }, + scrollbarWidth: "none", + msOverflowStyle: "none", + backgroundPosition: "center", + backgroundSize: "cover", + backgroundImage: + settings.cfg.design && !isMobile + ? quizThemes[settings.cfg.theme].isLight + ? `url(${DESIGN_LIST[settings.cfg.theme]})` + : `linear-gradient(90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%), url(${ + DESIGN_LIST[settings.cfg.theme] + })` + : null, + }} + > + + + + + + + + { + setReady(target.checked); + }} + checked={ready} + colorIcon={theme.palette.primary.main} + sx={{ marginRight: "0" }} + /> + + С  + + Положением об обработке персональных данных{" "} + +  и  + + {" "} + Политикой конфиденциальности{" "} + +  ознакомлен + + + + + + {show_badge && ( + 500 ? "100%" : "auto", - overflow: "auto", - "&::-webkit-scrollbar": { - width: "0", - display: "none", - msOverflowStyle: "none", - }, - scrollbarWidth: "none", - msOverflowStyle: "none", - backgroundPosition: "center", - backgroundSize: "cover", - backgroundImage: - settings.cfg.design && !isMobile - ? quizThemes[settings.cfg.theme].isLight - ? `url(${DESIGN_LIST[settings.cfg.theme]})` - : `linear-gradient(90deg, rgba(39, 38, 38, 0.95) 7.66%, rgba(42, 42, 46, 0.85) 42.12%, rgba(51, 54, 71, 0.4) 100%), url(${DESIGN_LIST[settings.cfg.theme] - })` - : null, - }} - > - - - - - - - - - { - setReady(target.checked); - }} - checked={ready} - colorIcon={theme.palette.primary.main} - sx={{marginRight: "0"}} - /> - - С  - - Положением об обработке персональных - данных{" "} - -  и  - - {" "} - Политикой конфиденциальности{" "} - -  ознакомлен - - - - - - {show_badge && ( - - - - )} - + + )} - ); + + + ); }; diff --git a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx index e76fda9..f250b1a 100644 --- a/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/ContactTextBlock/index.tsx @@ -1,66 +1,65 @@ -import {Box, Typography, useTheme} from "@mui/material"; -import {useRootContainerSize} from "@contexts/RootContainerWidthContext.ts"; -import {QuizSettingsConfig} from "@model/settingsData.ts"; -import {FC} from "react"; +import { Box, Typography, useTheme } from "@mui/material"; +import { useRootContainerSize } from "@contexts/RootContainerWidthContext.ts"; +import { QuizSettingsConfig } from "@model/settingsData.ts"; +import { FC } from "react"; type ContactTextBlockProps = { - settings: QuizSettingsConfig; -} + settings: QuizSettingsConfig; +}; -export const ContactTextBlock: FC = ({settings}) => { - const theme = useTheme(); - const isMobile = useRootContainerSize() < 850; - const isTablet = useRootContainerSize() < 1000; - return ( - = ({ settings }) => { + const theme = useTheme(); + const isMobile = useRootContainerSize() < 850; + const isTablet = useRootContainerSize() < 1000; + return ( + + + - - - {settings.cfg.formContact.title || - "Заполните форму, чтобы получить результаты теста"} - - {settings.cfg.formContact.desc && ( - - {settings.cfg.formContact.desc} - - )} - - - ) -} + {settings.cfg.formContact.title || "Заполните форму, чтобы получить результаты теста"} + + {settings.cfg.formContact.desc && ( + + {settings.cfg.formContact.desc} + + )} + + + ); +}; diff --git a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx index 4b019a0..0cc9702 100644 --- a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx @@ -1,67 +1,65 @@ -import {MenuItem, Select, SelectChangeEvent, useTheme} from "@mui/material"; -import {Dispatch, FC, SetStateAction, useState} from "react"; -import {phoneMasksByCountry} from "@utils/phoneMasksByCountry.tsx"; -import {Value} from "react-phone-number-input"; +import { MenuItem, Select, SelectChangeEvent, useTheme } from "@mui/material"; +import { Dispatch, FC, SetStateAction, useState } from "react"; +import { phoneMasksByCountry } from "@utils/phoneMasksByCountry.tsx"; +import { Value } from "react-phone-number-input"; type CountrySelectorProps = { - setMask: Dispatch>; -} - -export const CountrySelector: FC = ({setMask}) => { - const theme = useTheme(); - const [country, setCountry] = useState('RU'); - - const handleChange = (e: SelectChangeEvent) => { - setCountry(e.target.value); - setMask(phoneMasksByCountry[e.target.value][1]); - }; - return ( - - ); + setMask: Dispatch>; }; +export const CountrySelector: FC = ({ setMask }) => { + const theme = useTheme(); + const [country, setCountry] = useState("RU"); + + const handleChange = (e: SelectChangeEvent) => { + setCountry(e.target.value); + setMask(phoneMasksByCountry[e.target.value][1]); + }; + return ( + + ); +}; diff --git a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx index 23c4704..312c14b 100644 --- a/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx @@ -1,91 +1,70 @@ -import { - Box, - InputAdornment, - TextField as MuiTextField, - TextFieldProps, - Typography, - useTheme -} from "@mui/material"; -import {useRootContainerSize} from "@contexts/RootContainerWidthContext.ts"; -import {useQuizData} from "@contexts/QuizDataContext.ts"; -import {useIMask} from "react-imask"; -import {quizThemes} from "@utils/themes/Publication/themePublication.ts"; -import {FC, useState} from "react"; -import { - CountrySelector -} from "@/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx"; -import {phoneMasksByCountry} from "@utils/phoneMasksByCountry.tsx"; +import { Box, InputAdornment, TextField as MuiTextField, TextFieldProps, Typography, useTheme } from "@mui/material"; +import { useRootContainerSize } from "@contexts/RootContainerWidthContext.ts"; +import { useQuizData } from "@contexts/QuizDataContext.ts"; +import { useIMask } from "react-imask"; +import { quizThemes } from "@utils/themes/Publication/themePublication.ts"; +import { FC, useState } from "react"; +import { CountrySelector } from "@/components/ViewPublicationPage/ContactForm/CustomInput/CountrySelector/CountrySelector.tsx"; +import { phoneMasksByCountry } from "@utils/phoneMasksByCountry.tsx"; type InputProps = { - title: string; - desc: string; - Icon: FC<{ color: string; backgroundColor: string }>; - onChange: TextFieldProps["onChange"]; - id: string; - isPhone?:boolean + title: string; + desc: string; + Icon: FC<{ color: string; backgroundColor: string }>; + onChange: TextFieldProps["onChange"]; + id: string; + isPhone?: boolean; }; const TextField = MuiTextField as unknown as FC; +export const CustomInput = ({ title, desc, Icon, onChange, isPhone }: InputProps) => { + const theme = useTheme(); + const isMobile = useRootContainerSize() < 600; + const { settings } = useQuizData(); + const [mask, setMask] = useState(phoneMasksByCountry["RU"][1]); + const { ref } = useIMask({ mask }); + return ( + + + {title} + -export const CustomInput = ({ title, desc, Icon, onChange ,isPhone}: InputProps) => { - const theme = useTheme(); - const isMobile = useRootContainerSize() < 600; - const { settings } = useQuizData(); - const [mask, setMask] = useState(phoneMasksByCountry['RU'][1]); - const { ref } = useIMask({mask}); - return ( - - - {title} - - - - - - ), - endAdornment: ( - - {isPhone && ( - )} - - ), - }} - /> - - - ); + + + + ), + endAdornment: ( + {isPhone && } + ), + }} + /> + + ); }; diff --git a/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx b/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx index 6f9b539..bb94104 100644 --- a/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx +++ b/lib/components/ViewPublicationPage/ContactForm/Inputs/Inputs.tsx @@ -1,107 +1,105 @@ -import {useQuizData} from "@contexts/QuizDataContext.ts"; +import { useQuizData } from "@contexts/QuizDataContext.ts"; import NameIcon from "@icons/ContactFormIcon/NameIcon.tsx"; import EmailIcon from "@icons/ContactFormIcon/EmailIcon.tsx"; import TextIcon from "@icons/ContactFormIcon/TextIcon.tsx"; import AddressIcon from "@icons/ContactFormIcon/AddressIcon.tsx"; -import {Dispatch, SetStateAction} from "react"; -import { - CustomInput -} from "@/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx"; +import { Dispatch, SetStateAction } from "react"; +import { CustomInput } from "@/components/ViewPublicationPage/ContactForm/CustomInput/CustomInput.tsx"; import PhoneIcon from "@icons/ContactFormIcon/PhoneIcon.tsx"; type InputsProps = { - name: string; - setName: Dispatch>; - email: string; - setEmail: Dispatch>; - phone: string; - setPhone: Dispatch>; - text: string; - setText: Dispatch>; - adress: string; - setAdress: Dispatch>; + name: string; + setName: Dispatch>; + email: string; + setEmail: Dispatch>; + phone: string; + setPhone: Dispatch>; + text: string; + setText: Dispatch>; + adress: string; + setAdress: Dispatch>; }; export const Inputs = ({ - name, - setName, - email, - setEmail, - phone, - setPhone, - text, - setText, - adress, - setAdress, - }: InputsProps) => { - const { settings } = useQuizData(); - const FC = settings.cfg.formContact.fields; + name, + setName, + email, + setEmail, + phone, + setPhone, + text, + setText, + adress, + setAdress, +}: InputsProps) => { + const { settings } = useQuizData(); + const FC = settings.cfg.formContact.fields; - if (!FC) return null; - const Name = ( - setName(target.value)} - id={name} - title={FC["name"].innerText || "Введите имя"} - desc={FC["name"].text || "Имя"} - Icon={NameIcon} - /> - ); - const Email = ( - setEmail(target.value.replaceAll(/\s/g, ""))} - id={email} - title={FC["email"].innerText || "Введите Email"} - desc={FC["email"].text || "Email"} - Icon={EmailIcon} - /> - ); - const Phone = ( - setPhone(target.value)} - id={phone} - title={FC["phone"].innerText || "Введите номер телефона"} - desc={FC["phone"].text || "Номер телефона"} - Icon={PhoneIcon} - isPhone={true} - /> - ); - const Text = ( - setText(target.value)} - id={text} - title={FC["text"].text || "Введите фамилию"} - desc={FC["text"].innerText || "Фамилия"} - Icon={TextIcon} - /> - ); - const Adress = ( - setAdress(target.value)} - id={adress} - title={FC["address"].innerText || "Введите адрес"} - desc={FC["address"].text || "Адрес"} - Icon={AddressIcon} - /> - ); + if (!FC) return null; + const Name = ( + setName(target.value)} + id={name} + title={FC["name"].innerText || "Введите имя"} + desc={FC["name"].text || "Имя"} + Icon={NameIcon} + /> + ); + const Email = ( + setEmail(target.value.replaceAll(/\s/g, ""))} + id={email} + title={FC["email"].innerText || "Введите Email"} + desc={FC["email"].text || "Email"} + Icon={EmailIcon} + /> + ); + const Phone = ( + setPhone(target.value)} + id={phone} + title={FC["phone"].innerText || "Введите номер телефона"} + desc={FC["phone"].text || "Номер телефона"} + Icon={PhoneIcon} + isPhone={true} + /> + ); + const Text = ( + setText(target.value)} + id={text} + title={FC["text"].text || "Введите фамилию"} + desc={FC["text"].innerText || "Фамилия"} + Icon={TextIcon} + /> + ); + const Adress = ( + setAdress(target.value)} + id={adress} + title={FC["address"].innerText || "Введите адрес"} + desc={FC["address"].text || "Адрес"} + Icon={AddressIcon} + /> + ); - if (Object.values(FC).some((data) => data.used)) { - return ( - <> - {FC["name"].used ? Name : <>} - {FC["email"].used ? Email : <>} - {FC["phone"].used ? Phone : <>} - {FC["text"].used ? Text : <>} - {FC["address"].used ? Adress : <>} - - ); - } else { - return ( - <> - {Name} - {Email} - {Phone} - - ); - } + if (Object.values(FC).some((data) => data.used)) { + return ( + <> + {FC["name"].used ? Name : <>} + {FC["email"].used ? Email : <>} + {FC["phone"].used ? Phone : <>} + {FC["text"].used ? Text : <>} + {FC["address"].used ? Adress : <>} + + ); + } else { + return ( + <> + {Name} + {Email} + {Phone} + + ); + } }; diff --git a/lib/components/ViewPublicationPage/Footer.tsx b/lib/components/ViewPublicationPage/Footer.tsx index fa5b6ac..fa2f621 100644 --- a/lib/components/ViewPublicationPage/Footer.tsx +++ b/lib/components/ViewPublicationPage/Footer.tsx @@ -6,53 +6,49 @@ import { useQuizData } from "@contexts/QuizDataContext"; import Stepper from "@ui_kit/Stepper"; type FooterProps = { - stepNumber: number | null; - nextButton: ReactNode; - prevButton: ReactNode; + stepNumber: number | null; + nextButton: ReactNode; + prevButton: ReactNode; }; export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => { - const theme = useTheme(); - const { questions, settings } = useQuizData(); - const questionsAmount = questions.filter( - ({ type }) => type !== "result" - ).length; + const theme = useTheme(); + const { questions, settings } = useQuizData(); + const questionsAmount = questions.filter(({ type }) => type !== "result").length; - return ( - - - {stepNumber !== null && ( - - - Вопрос {stepNumber} из {questionsAmount} - - - - )} - {prevButton} - {nextButton} - - - ); + return ( + + + {stepNumber !== null && ( + + + Вопрос {stepNumber} из {questionsAmount} + + + + )} + {prevButton} + {nextButton} + + + ); }; diff --git a/lib/components/ViewPublicationPage/Question.tsx b/lib/components/ViewPublicationPage/Question.tsx index 5a9f7bb..7579d72 100644 --- a/lib/components/ViewPublicationPage/Question.tsx +++ b/lib/components/ViewPublicationPage/Question.tsx @@ -48,9 +48,7 @@ export const Question = ({ height: "100%", backgroundPosition: "center", backgroundSize: "cover", - backgroundImage: settings.cfg.design - ? `url(${DESIGN_LIST[settings.cfg.theme]})` - : null, + backgroundImage: settings.cfg.design ? `url(${DESIGN_LIST[settings.cfg.theme]})` : null, }} > {questionSelect} -