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