@thecb/components 11.2.17 → 11.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecb/components",
3
- "version": "11.2.17",
3
+ "version": "11.3.0",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -46,3 +46,4 @@ export { default as PopupMenu } from "./popup-menu";
46
46
  export { default as MultipleSelectFilter } from "./multiple-select-filter";
47
47
  export { default as ContactCard } from "./contact-card";
48
48
  export { default as HeroImage } from "./hero-image";
49
+ export { default as TurnstileWidget } from "./turnstile-widget/TurnstileWidget";
@@ -0,0 +1,92 @@
1
+ import React, {
2
+ useState,
3
+ useEffect,
4
+ useRef,
5
+ forwardRef,
6
+ useImperativeHandle
7
+ } from "react";
8
+ import { Box } from "../../atoms/layouts";
9
+ import styled from "styled-components";
10
+ import { useTurnstileScript } from "../../../hooks";
11
+ import { noop } from "../../../util/general";
12
+
13
+ const TurnstileWidgetContainer = styled(Box)`
14
+ display: flex;
15
+ padding: ${({ padding }) => padding};
16
+ justify-content: ${({ justify }) => justify};
17
+ width: 100%;
18
+ `;
19
+
20
+ const TurnstileWidget = forwardRef(
21
+ (
22
+ {
23
+ verifyURL, // URL to load the Turnstile script from
24
+ siteKey, // Turnstile site key
25
+ onSuccess = noop,
26
+ onError = noop,
27
+ onExpired = noop,
28
+ padding = "1rem",
29
+ justify = "flex-end",
30
+ size = "normal",
31
+ tabindex = 0,
32
+ retry = "auto",
33
+ theme = "auto",
34
+ extraStyles = ""
35
+ },
36
+ ref
37
+ ) => {
38
+ if (!verifyURL || !siteKey) return null;
39
+
40
+ const widgetRef = useRef(null);
41
+ const [widgetId, setWidgetId] = useState(null);
42
+ const isTurnstileLoaded = useTurnstileScript(verifyURL);
43
+
44
+ // Allow the parent component to reset the Turnstile widget
45
+ useImperativeHandle(
46
+ ref,
47
+ () => ({
48
+ reset: () => {
49
+ if (widgetId && window.turnstile) {
50
+ window.turnstile.reset(widgetId);
51
+ }
52
+ }
53
+ }),
54
+ [widgetId]
55
+ );
56
+
57
+ useEffect(() => {
58
+ if (!widgetRef.current || !window.turnstile) return;
59
+ if (!widgetId) {
60
+ const newWidgetId = window.turnstile.render(widgetRef.current, {
61
+ sitekey: siteKey,
62
+ size: size,
63
+ retry: retry,
64
+ tabindex: tabindex,
65
+ theme: theme,
66
+ callback: token => onSuccess(token),
67
+ "error-callback": () => onError(),
68
+ "expired-callback": () => onExpired()
69
+ });
70
+ setWidgetId(newWidgetId);
71
+ }
72
+ return () => {
73
+ if (widgetId && window.turnstile) {
74
+ window.turnstile.remove(widgetId);
75
+ setWidgetId(null);
76
+ }
77
+ };
78
+ }, [isTurnstileLoaded, siteKey, widgetId]);
79
+
80
+ return (
81
+ <TurnstileWidgetContainer
82
+ padding={padding}
83
+ justify={justify}
84
+ extraStyles={extraStyles}
85
+ >
86
+ <div ref={widgetRef} />
87
+ </TurnstileWidgetContainer>
88
+ );
89
+ }
90
+ );
91
+
92
+ export default TurnstileWidget;
@@ -0,0 +1,3 @@
1
+ import TurnstileWidget from "./TurnstileWidget";
2
+
3
+ export default TurnstileWidget;
@@ -2,3 +2,4 @@ export { default as useOutsideClick } from "./use-outside-click";
2
2
  export { default as useScrollTo } from "./use-scroll-to";
3
3
  export { default as useToastNotification } from "./use-toast-notification";
4
4
  export { default as useConditionallyAddValidator } from "./use-conditionally-add-validator";
5
+ export { default as useTurnstileScript } from "./use-turnstile-script";
@@ -0,0 +1,36 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ /**
4
+ * A custom hook to dynamically load the Cloudflare Turnstile script.
5
+ *
6
+ * @param {string} verifyURL - The URL of the Turnstile verification script.
7
+ */
8
+ const useTurnstileScript = verifyURL => {
9
+ const [isLoaded, setIsLoaded] = useState(false);
10
+
11
+ useEffect(() => {
12
+ if (typeof window === "undefined") {
13
+ setIsLoaded(false);
14
+ return;
15
+ }
16
+ if (window.turnstile && window.turnstile.render) {
17
+ setIsLoaded(true);
18
+ return;
19
+ }
20
+
21
+ const script = document.createElement("script");
22
+ script.src = `${verifyURL}?render=explicit`;
23
+ script.onload = () => {
24
+ setIsLoaded(true);
25
+ };
26
+ script.defer = true;
27
+ document.getElementsByTagName("head")[0].appendChild(script);
28
+
29
+ return () => {
30
+ setIsLoaded(false);
31
+ };
32
+ }, [verifyURL]);
33
+
34
+ return isLoaded;
35
+ };
36
+ export default useTurnstileScript;
Binary file