add widget selector polling
split button widget class into fixed and normal
This commit is contained in:
parent
b349e9d023
commit
d1c70c264d
@ -1,31 +1,70 @@
|
||||
import { Root, createRoot } from "react-dom/client";
|
||||
import OpenQuizButton from "./OpenQuizButton";
|
||||
import { ComponentPropsWithoutRef } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { pollForSelector } from "../pollForSelector";
|
||||
|
||||
|
||||
export class ButtonWidget {
|
||||
root: Root | undefined;
|
||||
element = document.createElement("div");
|
||||
|
||||
constructor({ quizId, selector, fixedSide }: ComponentPropsWithoutRef<typeof OpenQuizButton>) {
|
||||
if (!fixedSide && !selector) throw new Error("ButtonWidget: Either selector or fixedSide params must be provided");
|
||||
constructor({ quizId, selector, selectorPollingTimeLimit = 60 }: {
|
||||
quizId: string;
|
||||
selector: string;
|
||||
/**
|
||||
* In seconds, null - polling disabled
|
||||
*/
|
||||
selectorPollingTimeLimit?: number | null;
|
||||
}) {
|
||||
const element = document.querySelector(selector);
|
||||
if (element) {
|
||||
this.root = createRoot(element);
|
||||
this.root.render(<OpenQuizButton quizId={quizId} />);
|
||||
|
||||
this.element.style.setProperty("display", "none");
|
||||
document.body.appendChild(this.element);
|
||||
return;
|
||||
}
|
||||
|
||||
this.root = createRoot(this.element);
|
||||
if (!selectorPollingTimeLimit) {
|
||||
console.error(`Не удалось найти элемент ${selector} для вставки виджета`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.root.render(
|
||||
<OpenQuizButton
|
||||
selector={selector}
|
||||
fixedSide={fixedSide}
|
||||
quizId={quizId}
|
||||
/>
|
||||
);
|
||||
pollForSelector(selector, selectorPollingTimeLimit, (element) => {
|
||||
this.root = createRoot(element);
|
||||
this.root.render(<OpenQuizButton quizId={quizId} />);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.root) this.root.unmount();
|
||||
this.element.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ButtonWidgetFixed {
|
||||
root: Root | undefined;
|
||||
element = document.createElement("div");
|
||||
|
||||
constructor({ quizId, side }: {
|
||||
quizId: string;
|
||||
side: "left" | "right";
|
||||
}) {
|
||||
this.element.style.setProperty("display", "none");
|
||||
document.body.appendChild(this.element);
|
||||
|
||||
this.root = createRoot(this.element);
|
||||
|
||||
this.root.render(createPortal(
|
||||
<OpenQuizButton
|
||||
fixedSide={side}
|
||||
quizId={quizId}
|
||||
/>,
|
||||
document.body
|
||||
));
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.root) this.root.unmount();
|
||||
this.element.remove();
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,18 @@
|
||||
import lightTheme from "@/utils/themes/light";
|
||||
import { Button, ThemeProvider } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import QuizDialog from "../QuizDialog";
|
||||
|
||||
|
||||
interface Props {
|
||||
selector?: string;
|
||||
fixedSide?: "left" | "right";
|
||||
quizId: string;
|
||||
}
|
||||
|
||||
export default function OpenQuizButton({ selector, quizId, fixedSide }: Props) {
|
||||
export default function OpenQuizButton({ quizId, fixedSide }: Props) {
|
||||
const [isQuizDialogOpen, setIsQuizDialogOpen] = useState<boolean>(false);
|
||||
|
||||
const portalContainer = !fixedSide && selector ? document.querySelector(selector)! : document.body;
|
||||
|
||||
return createPortal(
|
||||
return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<Button
|
||||
className="pena-quiz-widget-button"
|
||||
@ -24,7 +20,7 @@ export default function OpenQuizButton({ selector, quizId, fixedSide }: Props) {
|
||||
variant="contained"
|
||||
sx={[
|
||||
{
|
||||
// generic styles
|
||||
// normal styles
|
||||
},
|
||||
Boolean(fixedSide) && {
|
||||
position: "fixed",
|
||||
@ -49,7 +45,6 @@ export default function OpenQuizButton({ selector, quizId, fixedSide }: Props) {
|
||||
quizId={quizId}
|
||||
onClose={() => setIsQuizDialogOpen(false)}
|
||||
/>
|
||||
</ThemeProvider>,
|
||||
portalContainer
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,26 +1,48 @@
|
||||
import QuizAnswerer from "@/components/QuizAnswerer";
|
||||
import { Root, createRoot } from "react-dom/client";
|
||||
import { pollForSelector } from "../pollForSelector";
|
||||
|
||||
|
||||
export class ContainerWidget {
|
||||
root: Root | undefined;
|
||||
|
||||
constructor({ selector, quizId }: {
|
||||
constructor({ selector, quizId, selectorPollingTimeLimit = 60 }: {
|
||||
quizId: string;
|
||||
selector: string;
|
||||
/**
|
||||
* In seconds, null - polling disabled
|
||||
*/
|
||||
selectorPollingTimeLimit?: number | null;
|
||||
}) {
|
||||
const element = document.querySelector(selector);
|
||||
if (!element) throw new Error("Element for widget doesn't exist");
|
||||
if (element) {
|
||||
this.root = createRoot(element);
|
||||
this.root.render(
|
||||
<QuizAnswerer
|
||||
quizId={quizId}
|
||||
changeFaviconAndTitle={false}
|
||||
disableGlobalCss
|
||||
/>
|
||||
);
|
||||
|
||||
this.root = createRoot(element);
|
||||
return;
|
||||
}
|
||||
|
||||
this.root.render(
|
||||
<QuizAnswerer
|
||||
quizId={quizId}
|
||||
changeFaviconAndTitle={false}
|
||||
disableGlobalCss
|
||||
/>
|
||||
);
|
||||
if (!selectorPollingTimeLimit) {
|
||||
console.error(`Не удалось найти элемент ${selector} для вставки виджета`);
|
||||
return;
|
||||
}
|
||||
|
||||
pollForSelector(selector, selectorPollingTimeLimit, (element) => {
|
||||
this.root = createRoot(element);
|
||||
this.root.render(
|
||||
<QuizAnswerer
|
||||
quizId={quizId}
|
||||
changeFaviconAndTitle={false}
|
||||
disableGlobalCss
|
||||
/>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
25
src/widgets/pollForSelector.ts
Normal file
25
src/widgets/pollForSelector.ts
Normal file
@ -0,0 +1,25 @@
|
||||
const SELECTOR_POLLING_INTERVAL = 5000;
|
||||
|
||||
export function pollForSelector(
|
||||
selector: string,
|
||||
selectorPollingTimeLimit: number,
|
||||
onSuccess: (element: Element) => void,
|
||||
) {
|
||||
const deadline = Date.now() + selectorPollingTimeLimit * 1000;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
const element = document.querySelector(selector);
|
||||
|
||||
if (Date.now() > deadline) {
|
||||
clearInterval(interval);
|
||||
console.error(`Не удалось найти элемент ${selector} для вставки виджета`);
|
||||
return;
|
||||
}
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(interval);
|
||||
onSuccess(element);
|
||||
}, SELECTOR_POLLING_INTERVAL);
|
||||
}
|
@ -8,8 +8,8 @@
|
||||
<title>Quiz</title>
|
||||
<style>
|
||||
#widget-container {
|
||||
width: 400px;
|
||||
height: 300px;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
p {
|
||||
font-size: x-large;
|
||||
@ -80,7 +80,7 @@
|
||||
import { ContainerWidget } from "./widget/widget.js";
|
||||
|
||||
new ContainerWidget({
|
||||
selector: "widget-container",
|
||||
selector: "#widget-container",
|
||||
quizId: "3c49550d-8c77-4788-bc2d-42586a261514",
|
||||
});
|
||||
</script> -->
|
||||
@ -96,9 +96,17 @@
|
||||
|
||||
new ButtonWidget({
|
||||
quizId: "3c49550d-8c77-4788-bc2d-42586a261514",
|
||||
fixedSide: "right",
|
||||
selector: "#button-container",
|
||||
});
|
||||
</script> -->
|
||||
<script type="module">
|
||||
import { ButtonWidgetFixed } from "./widget/widget.js";
|
||||
|
||||
new ButtonWidgetFixed({
|
||||
quizId: "3c49550d-8c77-4788-bc2d-42586a261514",
|
||||
side: "left",
|
||||
});
|
||||
</script>
|
||||
<!-- <script type="module">
|
||||
import { BannerWidget } from "./widget/widget.js";
|
||||
|
||||
@ -107,14 +115,14 @@
|
||||
position: "bottomright",
|
||||
});
|
||||
</script> -->
|
||||
<script type="module">
|
||||
<!-- <script type="module">
|
||||
import { SideWidget } from "./widget/widget.js";
|
||||
|
||||
new SideWidget({
|
||||
quizId: "3c49550d-8c77-4788-bc2d-42586a261514",
|
||||
position: "right",
|
||||
});
|
||||
</script>
|
||||
</script> -->
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user