@thecb/components 11.3.1 → 11.3.2-beta.1
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/dist/index.cjs.js +152 -54
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +19 -6
- package/dist/index.esm.js +152 -54
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/molecules/turnstile-widget/TurnstileWidget.js +142 -30
- package/src/hooks/use-turnstile-script/index.js +19 -6
- package/src/.DS_Store +0 -0
- package/src/components/.DS_Store +0 -0
- package/src/components/atoms/.DS_Store +0 -0
package/package.json
CHANGED
|
@@ -3,20 +3,46 @@ import React, {
|
|
|
3
3
|
useEffect,
|
|
4
4
|
useRef,
|
|
5
5
|
forwardRef,
|
|
6
|
-
useImperativeHandle
|
|
6
|
+
useImperativeHandle,
|
|
7
|
+
useCallback
|
|
7
8
|
} from "react";
|
|
8
9
|
import { Box } from "../../atoms/layouts";
|
|
10
|
+
import Text from "../../atoms/text";
|
|
9
11
|
import styled from "styled-components";
|
|
10
12
|
import { useTurnstileScript } from "../../../hooks";
|
|
11
13
|
import { noop } from "../../../util/general";
|
|
14
|
+
import { ERROR_COLOR } from "../../../constants/colors";
|
|
12
15
|
|
|
13
16
|
const TurnstileWidgetContainer = styled(Box)`
|
|
14
17
|
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
15
19
|
padding: ${({ padding }) => padding};
|
|
16
20
|
justify-content: ${({ justify }) => justify};
|
|
21
|
+
align-items: ${({ align }) => align};
|
|
17
22
|
width: 100%;
|
|
18
23
|
`;
|
|
19
24
|
|
|
25
|
+
const FATAL_ERROR_CODES = [
|
|
26
|
+
// Configuration errors
|
|
27
|
+
/^100/,
|
|
28
|
+
/^105/,
|
|
29
|
+
/^110100$/,
|
|
30
|
+
/^110110$/,
|
|
31
|
+
/^110200$/,
|
|
32
|
+
/^110420$/,
|
|
33
|
+
/^110430$/,
|
|
34
|
+
/^400020$/,
|
|
35
|
+
/^400030$/,
|
|
36
|
+
/^400040$/,
|
|
37
|
+
// Browser/environment errors
|
|
38
|
+
/^110500$/,
|
|
39
|
+
/^110510$/,
|
|
40
|
+
/^200010$/,
|
|
41
|
+
/^200100$/,
|
|
42
|
+
// Internal errors
|
|
43
|
+
/^120/
|
|
44
|
+
];
|
|
45
|
+
|
|
20
46
|
const TurnstileWidget = forwardRef(
|
|
21
47
|
(
|
|
22
48
|
{
|
|
@@ -27,64 +53,150 @@ const TurnstileWidget = forwardRef(
|
|
|
27
53
|
onExpired = noop,
|
|
28
54
|
padding = "1rem",
|
|
29
55
|
justify = "flex-end",
|
|
56
|
+
align = "flex-end",
|
|
30
57
|
size = "normal",
|
|
31
58
|
tabindex = 0,
|
|
32
|
-
retry = "
|
|
33
|
-
theme = "
|
|
34
|
-
extraStyles = ""
|
|
59
|
+
retry = "never",
|
|
60
|
+
theme = "light",
|
|
61
|
+
extraStyles = "",
|
|
62
|
+
maxErrorRetries = 3
|
|
35
63
|
},
|
|
36
64
|
ref
|
|
37
65
|
) => {
|
|
38
66
|
if (!verifyURL || !siteKey) return null;
|
|
39
67
|
|
|
40
68
|
const widgetRef = useRef(null);
|
|
41
|
-
const
|
|
42
|
-
const
|
|
69
|
+
const widgetIdRef = useRef(null);
|
|
70
|
+
const retryCountRef = useRef(0);
|
|
71
|
+
const [error, setError] = useState(null);
|
|
72
|
+
|
|
73
|
+
const { scriptLoaded, scriptError } = useTurnstileScript(verifyURL);
|
|
74
|
+
|
|
75
|
+
// Clear the error state and reset retry count
|
|
76
|
+
const clearError = () => {
|
|
77
|
+
setError(null);
|
|
78
|
+
retryCountRef.current = 0;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Handle errors based on the retry strategy
|
|
82
|
+
const handleError = errorCode => {
|
|
83
|
+
retryCountRef.current += 1;
|
|
84
|
+
const currentRetryCount = retryCountRef.current;
|
|
85
|
+
|
|
86
|
+
// If we haven't exceeded max retries, reset and try again
|
|
87
|
+
if (currentRetryCount <= maxErrorRetries) {
|
|
88
|
+
window.turnstile.reset(widgetIdRef.current);
|
|
89
|
+
setError(null);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Max retries exceeded - show appropriate error message
|
|
94
|
+
if (errorCode) {
|
|
95
|
+
const errorMessage = getErrorMessage(errorCode);
|
|
96
|
+
setError(errorMessage);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const getErrorMessage = errorCode => {
|
|
101
|
+
const isFatalError = FATAL_ERROR_CODES.some(pattern =>
|
|
102
|
+
pattern.test(errorCode)
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (isFatalError) {
|
|
106
|
+
return "Browser or system error. Please refresh the page or update your browser.";
|
|
107
|
+
}
|
|
108
|
+
return "Something went wrong. Please refresh and try again.";
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Reset the Turnstile widget when requested
|
|
112
|
+
const resetWidget = useCallback(() => {
|
|
113
|
+
if (widgetIdRef.current && window.turnstile) {
|
|
114
|
+
window.turnstile.reset(widgetIdRef.current);
|
|
115
|
+
clearError();
|
|
116
|
+
}
|
|
117
|
+
}, [clearError]);
|
|
43
118
|
|
|
44
119
|
// Allow the parent component to reset the Turnstile widget
|
|
45
120
|
useImperativeHandle(
|
|
46
121
|
ref,
|
|
47
122
|
() => ({
|
|
48
|
-
reset:
|
|
49
|
-
if (widgetId && window.turnstile) {
|
|
50
|
-
window.turnstile.reset(widgetId);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
123
|
+
reset: resetWidget
|
|
53
124
|
}),
|
|
54
|
-
[
|
|
125
|
+
[resetWidget]
|
|
55
126
|
);
|
|
56
127
|
|
|
57
128
|
useEffect(() => {
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
129
|
+
if (scriptError) {
|
|
130
|
+
setError(
|
|
131
|
+
"Unable to load security verification. Please refresh the page."
|
|
132
|
+
);
|
|
133
|
+
onError?.({ type: "script_load_failed", error: scriptError });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (widgetIdRef.current || !window.turnstile || !scriptLoaded) return;
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
widgetIdRef.current = window.turnstile.render(widgetRef.current, {
|
|
61
141
|
sitekey: siteKey,
|
|
62
142
|
size: size,
|
|
63
143
|
retry: retry,
|
|
64
144
|
tabindex: tabindex,
|
|
65
145
|
theme: theme,
|
|
66
|
-
callback: token =>
|
|
67
|
-
|
|
68
|
-
|
|
146
|
+
callback: token => {
|
|
147
|
+
clearError();
|
|
148
|
+
onSuccess(token);
|
|
149
|
+
},
|
|
150
|
+
"error-callback": errorCode => {
|
|
151
|
+
if (retry === "never") {
|
|
152
|
+
handleError(errorCode);
|
|
153
|
+
}
|
|
154
|
+
onError?.(errorCode);
|
|
155
|
+
},
|
|
156
|
+
"expired-callback": () => {
|
|
157
|
+
clearError();
|
|
158
|
+
onExpired?.();
|
|
159
|
+
}
|
|
69
160
|
});
|
|
70
|
-
|
|
161
|
+
} catch (error) {
|
|
162
|
+
setError(
|
|
163
|
+
"Unable to load security verification. Please refresh the page."
|
|
164
|
+
);
|
|
165
|
+
onError?.({ type: "render_failed", error });
|
|
71
166
|
}
|
|
167
|
+
|
|
72
168
|
return () => {
|
|
73
|
-
if (
|
|
74
|
-
window.turnstile.remove(
|
|
75
|
-
|
|
169
|
+
if (widgetIdRef.current && window.turnstile) {
|
|
170
|
+
window.turnstile.remove(widgetIdRef.current);
|
|
171
|
+
widgetIdRef.current = null;
|
|
172
|
+
clearError();
|
|
76
173
|
}
|
|
77
174
|
};
|
|
78
|
-
}, [
|
|
175
|
+
}, [scriptLoaded, siteKey]);
|
|
79
176
|
|
|
80
177
|
return (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
178
|
+
<>
|
|
179
|
+
<TurnstileWidgetContainer
|
|
180
|
+
padding={padding}
|
|
181
|
+
justify={justify}
|
|
182
|
+
align={align}
|
|
183
|
+
extraStyles={extraStyles}
|
|
184
|
+
>
|
|
185
|
+
<div ref={widgetRef} />
|
|
186
|
+
{error && (
|
|
187
|
+
<Text
|
|
188
|
+
color={ERROR_COLOR}
|
|
189
|
+
variant="pXS"
|
|
190
|
+
extraStyles={`
|
|
191
|
+
word-break: break-word;
|
|
192
|
+
text-align: end;
|
|
193
|
+
`}
|
|
194
|
+
>
|
|
195
|
+
{error}
|
|
196
|
+
</Text>
|
|
197
|
+
)}
|
|
198
|
+
</TurnstileWidgetContainer>
|
|
199
|
+
</>
|
|
88
200
|
);
|
|
89
201
|
}
|
|
90
202
|
);
|
|
@@ -6,31 +6,44 @@ import { useEffect, useState } from "react";
|
|
|
6
6
|
* @param {string} verifyURL - The URL of the Turnstile verification script.
|
|
7
7
|
*/
|
|
8
8
|
const useTurnstileScript = verifyURL => {
|
|
9
|
-
const [
|
|
9
|
+
const [scriptLoaded, setScriptLoaded] = useState(false);
|
|
10
|
+
const [scriptError, setScriptError] = useState(false);
|
|
11
|
+
|
|
12
|
+
const handleScriptError = () => {
|
|
13
|
+
setScriptError(true);
|
|
14
|
+
setScriptLoaded(false);
|
|
15
|
+
};
|
|
10
16
|
|
|
11
17
|
useEffect(() => {
|
|
12
18
|
if (typeof window === "undefined") {
|
|
13
|
-
|
|
19
|
+
setScriptLoaded(false);
|
|
14
20
|
return;
|
|
15
21
|
}
|
|
16
22
|
if (window.turnstile && window.turnstile.render) {
|
|
17
|
-
|
|
23
|
+
setScriptLoaded(true);
|
|
18
24
|
return;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
const script = document.createElement("script");
|
|
22
28
|
script.src = `${verifyURL}?render=explicit`;
|
|
23
29
|
script.onload = () => {
|
|
24
|
-
|
|
30
|
+
setScriptLoaded(true);
|
|
31
|
+
};
|
|
32
|
+
script.onerror = () => {
|
|
33
|
+
handleScriptError();
|
|
34
|
+
};
|
|
35
|
+
script.onabort = () => {
|
|
36
|
+
handleScriptError();
|
|
25
37
|
};
|
|
26
38
|
script.defer = true;
|
|
27
39
|
document.getElementsByTagName("head")[0].appendChild(script);
|
|
28
40
|
|
|
29
41
|
return () => {
|
|
30
|
-
|
|
42
|
+
setScriptLoaded(false);
|
|
43
|
+
setScriptError(false);
|
|
31
44
|
};
|
|
32
45
|
}, [verifyURL]);
|
|
33
46
|
|
|
34
|
-
return
|
|
47
|
+
return { scriptLoaded, scriptError };
|
|
35
48
|
};
|
|
36
49
|
export default useTurnstileScript;
|
package/src/.DS_Store
DELETED
|
Binary file
|
package/src/components/.DS_Store
DELETED
|
Binary file
|
|
Binary file
|