move widget types to package

This commit is contained in:
nflnkr 2024-05-13 20:36:27 +03:00
parent 8276355ed5
commit 81cbe420c5
16 changed files with 127 additions and 126 deletions

2
.gitignore vendored

@ -11,7 +11,7 @@ node_modules
dist dist
dist-package dist-package
dist-ssr dist-ssr
widget /widget
*.local *.local
# Editor directories and files # Editor directories and files

@ -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<BannerWidgetComponentProps, "onWidgetClose">;

@ -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<ButtonWidgetComponentProps, "fixedSide"> & {
selector: string;
/**
* In seconds, null - polling disabled
*/
selectorPollingTimeLimit?: number | null;
};
export type ButtonWidgetFixedParams = Omit<ButtonWidgetComponentProps, "selector"> & {
fixedSide: "left" | "right";
};

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

12
lib/model/widget/popup.ts Normal file

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

20
lib/model/widget/side.ts Normal file

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

@ -1,15 +1,13 @@
import { BannerWidgetParams } from "@/model/widget/banner";
import { Root, createRoot } from "react-dom/client"; import { Root, createRoot } from "react-dom/client";
import QuizBanner from "./QuizBanner"; import QuizBanner from "./QuizBanner";
import { ComponentPropsWithoutRef } from "react";
type Props = Omit<ComponentPropsWithoutRef<typeof QuizBanner>, "onClose">;
export class BannerWidget { export class BannerWidget {
root: Root | undefined; root: Root | undefined;
element = document.createElement("div"); element = document.createElement("div");
constructor(props: Props) { constructor(props: BannerWidgetParams) {
this.element.style.setProperty("display", "none"); this.element.style.setProperty("display", "none");
document.body.appendChild(this.element); document.body.appendChild(this.element);
@ -18,7 +16,7 @@ export class BannerWidget {
this.render(props); this.render(props);
} }
render(props: Props) { render(props: BannerWidgetParams) {
this.root?.render( this.root?.render(
<QuizBanner <QuizBanner
{...props} {...props}

@ -1,3 +1,4 @@
import { BannerWidgetComponentProps } from "@/model/widget/banner";
import lightTheme from "@/utils/themes/light"; import lightTheme from "@/utils/themes/light";
import { Box, Button, Fade, IconButton, ThemeProvider, Typography, useMediaQuery } from "@mui/material"; import { Box, Button, Fade, IconButton, ThemeProvider, Typography, useMediaQuery } from "@mui/material";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
@ -10,27 +11,6 @@ import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus";
const PADDING = 10; const PADDING = 10;
interface Props {
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 default function QuizBanner({ export default function QuizBanner({
quizId, quizId,
position, position,
@ -47,7 +27,7 @@ export default function QuizBanner({
rounded = false, rounded = false,
bannerFullWidth = false, bannerFullWidth = false,
pulsation = false, pulsation = false,
}: Props) { }: BannerWidgetComponentProps) {
const isMobile = useMediaQuery("(max-width: 600px)"); const isMobile = useMediaQuery("(max-width: 600px)");
const [isQuizShown, setIsQuizShown] = useState<boolean>(false); const [isQuizShown, setIsQuizShown] = useState<boolean>(false);
const [isFlashEnabled, setIsFlashEnabled] = useState<boolean>(buttonFlash); const [isFlashEnabled, setIsFlashEnabled] = useState<boolean>(buttonFlash);

@ -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 { Root, createRoot } from "react-dom/client";
import { pollForSelector } from "../shared/pollForSelector"; import { pollForSelector } from "../shared/pollForSelector";
import OpenQuizButton from "./OpenQuizButton"; import OpenQuizButton from "./OpenQuizButton";
import { createPortal } from "react-dom";
type ButtonWidgetProps = Omit<ComponentPropsWithoutRef<typeof OpenQuizButton>, "fixedSide">;
export class ButtonWidget { export class ButtonWidget {
root: Root | undefined; root: Root | undefined;
constructor(props: ButtonWidgetProps & { constructor(props: ButtonWidgetParams) {
selector: string;
/**
* In seconds, null - polling disabled
*/
selectorPollingTimeLimit?: number | null;
}) {
const { selector, selectorPollingTimeLimit = 60 } = props; const { selector, selectorPollingTimeLimit = 60 } = props;
const element = document.querySelector(selector); const element = document.querySelector(selector);
@ -38,7 +30,7 @@ export class ButtonWidget {
}); });
} }
render(props: ButtonWidgetProps) { render(props: Omit<ButtonWidgetParams, "selector" | "selectorPollingTimeLimit">) {
this.root?.render(<OpenQuizButton {...props} />); this.root?.render(<OpenQuizButton {...props} />);
} }
@ -47,15 +39,11 @@ export class ButtonWidget {
} }
} }
type ButtonWidgetFixedProps = Omit<ComponentPropsWithoutRef<typeof OpenQuizButton>, "selector"> & {
fixedSide: "left" | "right";
};
export class ButtonWidgetFixed { export class ButtonWidgetFixed {
root: Root | undefined; root: Root | undefined;
element = document.createElement("div"); element = document.createElement("div");
constructor(props: ButtonWidgetFixedProps) { constructor(props: ButtonWidgetFixedParams) {
this.element.style.setProperty("display", "none"); this.element.style.setProperty("display", "none");
document.body.appendChild(this.element); document.body.appendChild(this.element);
@ -64,7 +52,7 @@ export class ButtonWidgetFixed {
this.render(props); this.render(props);
} }
render(props: ButtonWidgetFixedProps) { render(props: ButtonWidgetFixedParams) {
this.root?.render(createPortal(<OpenQuizButton {...props} />, document.body)); this.root?.render(createPortal(<OpenQuizButton {...props} />, document.body));
} }

@ -1,3 +1,4 @@
import { ButtonWidgetComponentProps } from "@/model/widget/button";
import lightTheme from "@/utils/themes/light"; import lightTheme from "@/utils/themes/light";
import { Button, ThemeProvider, useMediaQuery } from "@mui/material"; import { Button, ThemeProvider, useMediaQuery } from "@mui/material";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
@ -9,24 +10,6 @@ import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus";
const WIDGET_DEFAULT_WIDTH = "600px"; const WIDGET_DEFAULT_WIDTH = "600px";
const WIDGET_DEFAULT_HEIGHT = "80%"; 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({ export default function OpenQuizButton({
quizId, quizId,
fixedSide, fixedSide,
@ -40,7 +23,7 @@ export default function OpenQuizButton({
buttonText = "Пройти квиз", buttonText = "Пройти квиз",
buttonTextColor, buttonTextColor,
buttonBackgroundColor, buttonBackgroundColor,
}: Props) { }: ButtonWidgetComponentProps) {
const isMobile = useMediaQuery("(max-width: 600px)"); const isMobile = useMediaQuery("(max-width: 600px)");
const [isQuizShown, setIsQuizShown] = useState<boolean>(false); const [isQuizShown, setIsQuizShown] = useState<boolean>(false);
const isQuizCompleted = useQuizCompletionStatus(quizId); const isQuizCompleted = useQuizCompletionStatus(quizId);

@ -1,21 +1,13 @@
import { ComponentPropsWithoutRef } from "react"; import { ContainerWidgetParams } from "@/model/widget/container";
import { Root, createRoot } from "react-dom/client"; import { Root, createRoot } from "react-dom/client";
import { pollForSelector } from "../shared/pollForSelector"; import { pollForSelector } from "../shared/pollForSelector";
import QuizContainer from "./QuizContainer"; import QuizContainer from "./QuizContainer";
type Props = ComponentPropsWithoutRef<typeof QuizContainer>;
export class ContainerWidget { export class ContainerWidget {
root: Root | undefined; root: Root | undefined;
constructor(props: Props & { constructor(props: ContainerWidgetParams) {
selector: string;
/**
* In seconds, null - polling disabled
*/
selectorPollingTimeLimit?: number | null;
}) {
const { selector, selectorPollingTimeLimit = 60 } = props; const { selector, selectorPollingTimeLimit = 60 } = props;
const element = document.querySelector(selector); const element = document.querySelector(selector);
@ -37,7 +29,7 @@ export class ContainerWidget {
}); });
} }
render(props: Props) { render(props: Omit<ContainerWidgetParams, "selector" | "selectorPollingTimeLimit">) {
this.root?.render(<QuizContainer {...props} />); this.root?.render(<QuizContainer {...props} />);
} }

@ -1,16 +1,10 @@
import QuizAnswerer from "@/components/QuizAnswerer"; import QuizAnswerer from "@/components/QuizAnswerer";
import { ContainerWidgetComponentProps } from "@/model/widget/container";
import { Box, useMediaQuery } from "@mui/material"; import { Box, useMediaQuery } from "@mui/material";
import { ComponentPropsWithoutRef } from "react";
import OpenQuizButton from "../button/OpenQuizButton"; import OpenQuizButton from "../button/OpenQuizButton";
type Props = ComponentPropsWithoutRef<typeof OpenQuizButton> & { export default function QuizContainer(props: ContainerWidgetComponentProps) {
quizId: string;
showButtonOnMobile?: boolean;
dimensions?: { width: string; height: string; };
};
export default function QuizContainer(props: Props) {
const { quizId, dimensions, showButtonOnMobile = false } = props; const { quizId, dimensions, showButtonOnMobile = false } = props;
const isMobile = useMediaQuery("(max-width: 600px)"); const isMobile = useMediaQuery("(max-width: 600px)");

@ -1,15 +1,13 @@
import { PopupWidgetParams } from "@/model/widget/popup";
import { Root, createRoot } from "react-dom/client"; import { Root, createRoot } from "react-dom/client";
import { ComponentPropsWithoutRef } from "react";
import QuizPopup from "./QuizPopup"; import QuizPopup from "./QuizPopup";
type Props = ComponentPropsWithoutRef<typeof QuizPopup>;
export class PopupWidget { export class PopupWidget {
root: Root | undefined; root: Root | undefined;
element = document.createElement("div"); element = document.createElement("div");
constructor(props: Props) { constructor(props: PopupWidgetParams) {
this.element.style.setProperty("display", "none"); this.element.style.setProperty("display", "none");
document.body.appendChild(this.element); document.body.appendChild(this.element);
@ -18,7 +16,7 @@ export class PopupWidget {
this.render(props); this.render(props);
} }
render(props: Props) { render(props: PopupWidgetParams) {
this.root?.render(<QuizPopup {...props} />); this.root?.render(<QuizPopup {...props} />);
} }

@ -2,29 +2,19 @@ import { useEffect, useRef, useState } from "react";
import QuizDialog from "../shared/QuizDialog"; import QuizDialog from "../shared/QuizDialog";
import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus";
import { useMediaQuery } from "@mui/material"; import { useMediaQuery } from "@mui/material";
import { PopupWidgetComponentProps } from "@/model/widget/popup";
const WIDGET_DEFAULT_WIDTH = "600px"; const WIDGET_DEFAULT_WIDTH = "600px";
const WIDGET_DEFAULT_HEIGHT = "80%"; 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({ export default function QuizPopup({
quizId, quizId,
dialogDimensions, dialogDimensions,
autoShowQuizTime = null, autoShowQuizTime = null,
hideOnMobile = false, hideOnMobile = false,
openOnLeaveAttempt = false, openOnLeaveAttempt = false,
}: Props) { }: PopupWidgetComponentProps) {
const initialIsQuizShown = (autoShowQuizTime !== null || openOnLeaveAttempt) ? false : true; const initialIsQuizShown = (autoShowQuizTime !== null || openOnLeaveAttempt) ? false : true;
const [isQuizShown, setIsQuizShown] = useState<boolean>(initialIsQuizShown); const [isQuizShown, setIsQuizShown] = useState<boolean>(initialIsQuizShown);

@ -6,31 +6,13 @@ import QuizDialog from "../shared/QuizDialog";
import RunningStripe from "../shared/RunningStripe"; import RunningStripe from "../shared/RunningStripe";
import { useAutoOpenTimer } from "../shared/useAutoOpenTimer"; import { useAutoOpenTimer } from "../shared/useAutoOpenTimer";
import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus"; import { useQuizCompletionStatus } from "../shared/useQuizCompletionStatus";
import { SideWidgetComponentProps } from "@/model/widget/side";
const PADDING = 10; const PADDING = 10;
const WIDGET_DEFAULT_WIDTH = "600px"; const WIDGET_DEFAULT_WIDTH = "600px";
const WIDGET_DEFAULT_HEIGHT = "800px"; 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({ export default function QuizSideButton({
quizId, quizId,
position, position,
@ -42,7 +24,7 @@ export default function QuizSideButton({
autoOpenTime = 0, autoOpenTime = 0,
autoShowQuizTime = null, autoShowQuizTime = null,
hideOnMobile = false, hideOnMobile = false,
}: Props) { }: SideWidgetComponentProps) {
const [isQuizShown, setIsQuizShown] = useState<boolean>(false); const [isQuizShown, setIsQuizShown] = useState<boolean>(false);
const isMobile = useMediaQuery("(max-width: 600px)"); const isMobile = useMediaQuery("(max-width: 600px)");
const isQuizCompleted = useQuizCompletionStatus(quizId); const isQuizCompleted = useQuizCompletionStatus(quizId);

@ -1,15 +1,13 @@
import { SideWidgetParams } from "@/model/widget/side";
import { Root, createRoot } from "react-dom/client"; import { Root, createRoot } from "react-dom/client";
import QuizSideButton from "./QuizSideButton"; import QuizSideButton from "./QuizSideButton";
import { ComponentPropsWithoutRef } from "react";
type Props = ComponentPropsWithoutRef<typeof QuizSideButton>;
export class SideWidget { export class SideWidget {
root: Root | undefined; root: Root | undefined;
element = document.createElement("div"); element = document.createElement("div");
constructor(props: Props) { constructor(props: SideWidgetParams) {
this.element.style.setProperty("display", "none"); this.element.style.setProperty("display", "none");
document.body.appendChild(this.element); document.body.appendChild(this.element);
@ -18,7 +16,7 @@ export class SideWidget {
this.render(props); this.render(props);
} }
render(props: Props) { render(props: SideWidgetParams) {
this.root?.render(<QuizSideButton {...props} />); this.root?.render(<QuizSideButton {...props} />);
} }