banner.tsx
· 3.4 KiB · TypeScript
Raw
import { useCallback, useEffect, useState } from "react";
// Check that the Enzuzo script has loaded
// Polling is the only option for global variables that are not part of React
function useEnzuzoLoaded() {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
if (isLoaded) return;
const interval = setInterval(() => {
if (typeof window !== "undefined" && window.__ENZUZO_STARTED__) {
setIsLoaded(true);
clearInterval(interval);
}
}, 1_000);
return () => clearInterval(interval);
}, [isLoaded]);
return isLoaded;
}
// Hook to capture a single element
function useCapturedElement<T extends HTMLElement>(selector: string) {
const [element, setElement] = useState<T | null>(null);
const [content, setContent] = useState<string>("");
const isEnzuzoLoaded = useEnzuzoLoaded();
useEffect(() => {
if (!isEnzuzoLoaded) return;
const el = document.querySelector(selector) as T | null;
if (!el) return;
setElement(el);
setContent(el.textContent?.trim() || "");
}, [selector, isEnzuzoLoaded]);
const trigger = useCallback(() => {
if (!element || !("click" in element)) return;
const event = new MouseEvent("click", {
bubbles: true,
cancelable: true,
view: window
});
element.dispatchEvent(event);
}, [element]);
return { element, content, trigger };
}
function AcceptButton() {
const selector = "#ez-cookie-notification__accept";
const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector);
return <button onClick={trigger}>{content}</button>;
}
function DeclineButton() {
const selector = "#ez-cookie-notification__decline";
const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector);
return <button onClick={trigger}>{content}</button>;
}
function SettingsButton() {
const selector = "#notificationManagerLink";
const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector);
return <button onClick={trigger}>{content}</button>;
}
function CloseButton() {
const selector = "#close-notification";
const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector);
return <button onClick={trigger}>{content}</button>;
}
function PrivacyPolicyLink() {
const selector = "#notificationPolicyLink";
const { element, content } = useCapturedElement<HTMLAnchorElement>(selector);
return <a href={element?.href}>{content}</a>;
}
function CookieBannerText() {
const selector = ".enzuzo-notification-desc p";
const { content } = useCapturedElement<HTMLElement>(selector);
return <p>{content}</p>;
}
export function CookieBanner() {
const [isOpen, setIsOpen] = useState(true);
const { element } = useCapturedElement<HTMLDivElement>("#ez-cookie-notification");
useEffect(() => {
if (!element) return;
if (element.style.display !== "none") setIsOpen(true);
element.style.display = "none";
}, [element]);
if (!isOpen) return null;
const bannerStyles = {
position: "fixed" as const,
top: "16px",
right: "16px",
width: "500px",
backgroundColor: "white",
borderRadius: "8px",
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
border: "2px solid red",
padding: "24px",
zIndex: 2147483647
};
return (
<div style={bannerStyles}>
<CloseButton />
<CookieBannerText />
<PrivacyPolicyLink />
<SettingsButton />
<AcceptButton />
<DeclineButton />
</div>
);
}
| 1 | import { useCallback, useEffect, useState } from "react"; |
| 2 | |
| 3 | // Check that the Enzuzo script has loaded |
| 4 | // Polling is the only option for global variables that are not part of React |
| 5 | function useEnzuzoLoaded() { |
| 6 | const [isLoaded, setIsLoaded] = useState(false); |
| 7 | |
| 8 | useEffect(() => { |
| 9 | if (isLoaded) return; |
| 10 | const interval = setInterval(() => { |
| 11 | if (typeof window !== "undefined" && window.__ENZUZO_STARTED__) { |
| 12 | setIsLoaded(true); |
| 13 | clearInterval(interval); |
| 14 | } |
| 15 | }, 1_000); |
| 16 | return () => clearInterval(interval); |
| 17 | }, [isLoaded]); |
| 18 | |
| 19 | return isLoaded; |
| 20 | } |
| 21 | |
| 22 | // Hook to capture a single element |
| 23 | function useCapturedElement<T extends HTMLElement>(selector: string) { |
| 24 | const [element, setElement] = useState<T | null>(null); |
| 25 | const [content, setContent] = useState<string>(""); |
| 26 | const isEnzuzoLoaded = useEnzuzoLoaded(); |
| 27 | |
| 28 | useEffect(() => { |
| 29 | if (!isEnzuzoLoaded) return; |
| 30 | const el = document.querySelector(selector) as T | null; |
| 31 | if (!el) return; |
| 32 | setElement(el); |
| 33 | setContent(el.textContent?.trim() || ""); |
| 34 | }, [selector, isEnzuzoLoaded]); |
| 35 | |
| 36 | const trigger = useCallback(() => { |
| 37 | if (!element || !("click" in element)) return; |
| 38 | const event = new MouseEvent("click", { |
| 39 | bubbles: true, |
| 40 | cancelable: true, |
| 41 | view: window |
| 42 | }); |
| 43 | element.dispatchEvent(event); |
| 44 | }, [element]); |
| 45 | |
| 46 | return { element, content, trigger }; |
| 47 | } |
| 48 | |
| 49 | function AcceptButton() { |
| 50 | const selector = "#ez-cookie-notification__accept"; |
| 51 | const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector); |
| 52 | return <button onClick={trigger}>{content}</button>; |
| 53 | } |
| 54 | |
| 55 | function DeclineButton() { |
| 56 | const selector = "#ez-cookie-notification__decline"; |
| 57 | const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector); |
| 58 | return <button onClick={trigger}>{content}</button>; |
| 59 | } |
| 60 | |
| 61 | function SettingsButton() { |
| 62 | const selector = "#notificationManagerLink"; |
| 63 | const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector); |
| 64 | return <button onClick={trigger}>{content}</button>; |
| 65 | } |
| 66 | |
| 67 | function CloseButton() { |
| 68 | const selector = "#close-notification"; |
| 69 | const { content, trigger } = useCapturedElement<HTMLButtonElement>(selector); |
| 70 | return <button onClick={trigger}>{content}</button>; |
| 71 | } |
| 72 | |
| 73 | function PrivacyPolicyLink() { |
| 74 | const selector = "#notificationPolicyLink"; |
| 75 | const { element, content } = useCapturedElement<HTMLAnchorElement>(selector); |
| 76 | return <a href={element?.href}>{content}</a>; |
| 77 | } |
| 78 | |
| 79 | function CookieBannerText() { |
| 80 | const selector = ".enzuzo-notification-desc p"; |
| 81 | const { content } = useCapturedElement<HTMLElement>(selector); |
| 82 | return <p>{content}</p>; |
| 83 | } |
| 84 | |
| 85 | export function CookieBanner() { |
| 86 | const [isOpen, setIsOpen] = useState(true); |
| 87 | const { element } = useCapturedElement<HTMLDivElement>("#ez-cookie-notification"); |
| 88 | |
| 89 | useEffect(() => { |
| 90 | if (!element) return; |
| 91 | if (element.style.display !== "none") setIsOpen(true); |
| 92 | element.style.display = "none"; |
| 93 | }, [element]); |
| 94 | |
| 95 | if (!isOpen) return null; |
| 96 | |
| 97 | const bannerStyles = { |
| 98 | position: "fixed" as const, |
| 99 | top: "16px", |
| 100 | right: "16px", |
| 101 | width: "500px", |
| 102 | backgroundColor: "white", |
| 103 | borderRadius: "8px", |
| 104 | boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)", |
| 105 | border: "2px solid red", |
| 106 | padding: "24px", |
| 107 | zIndex: 2147483647 |
| 108 | }; |
| 109 | |
| 110 | return ( |
| 111 | <div style={bannerStyles}> |
| 112 | <CloseButton /> |
| 113 | <CookieBannerText /> |
| 114 | <PrivacyPolicyLink /> |
| 115 | <SettingsButton /> |
| 116 | <AcceptButton /> |
| 117 | <DeclineButton /> |
| 118 | </div> |
| 119 | ); |
| 120 | } |
| 121 |