@siratik/wp-component-library 0.1.0 → 0.1.2
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/LICENSE +5 -0
- package/README.md +51 -5
- package/components/attachment-image/README.md +118 -0
- package/components/attachment-image/index.js +105 -0
- package/components/attributes-panel/README.md +92 -0
- package/components/attributes-panel/index.js +324 -0
- package/components/badge/README.md +61 -0
- package/components/badge/index.js +105 -0
- package/components/block-appender/README.md +158 -0
- package/components/block-appender/index.js +135 -0
- package/components/block-extension/index.js +214 -0
- package/components/business-hours/SpecialHoursControl.js +211 -0
- package/components/business-hours/WeeklyHoursControl.js +419 -0
- package/components/business-hours/index.js +8 -0
- package/components/button/README.md +172 -0
- package/components/button/index.js +84 -0
- package/components/captcha-placeholder/CloudflareTurnstile.js +111 -0
- package/components/captcha-placeholder/GoogleRecaptcha.js +132 -0
- package/components/captcha-placeholder/HCaptcha.js +99 -0
- package/components/captcha-placeholder/README.md +228 -0
- package/components/captcha-placeholder/index.js +39 -0
- package/components/captcha-placeholder/styles.js +33 -0
- package/components/geocoder/AddressEntryControl.js +390 -0
- package/components/geocoder/GeocoderSearchControl.js +353 -0
- package/components/geocoder/countryOptions.js +66 -0
- package/components/geocoder/geocoder.js +400 -0
- package/components/geocoder/index.js +16 -0
- package/components/icon-picker/Icon.js +53 -0
- package/components/icon-picker/IconPicker.js +70 -0
- package/components/icon-picker/IconPickerModal.js +474 -0
- package/components/icon-picker/IconPickerToolbarButton.js +60 -0
- package/components/icon-picker/InlineIconPicker.js +92 -0
- package/components/icon-picker/api.js +181 -0
- package/components/icon-picker/config-bootstrap.js +108 -0
- package/components/icon-picker/hooks.js +157 -0
- package/components/icon-picker/index.js +39 -0
- package/components/icon-picker/load-script.js +33 -0
- package/components/icon-picker/loaders.js +79 -0
- package/components/icon-picker/store.js +223 -0
- package/components/icon-picker/styles.js +232 -0
- package/components/icon-picker/utils.js +138 -0
- package/components/layout/README.md +268 -0
- package/components/link-tools-panel/README.md +49 -0
- package/components/link-tools-panel/index.js +198 -0
- package/components/link-tools-panel/styles.js +90 -0
- package/components/media/README.md +288 -0
- package/components/media/SettingsMediaUpload.js +187 -0
- package/components/media/editor-media-upload.js +65 -0
- package/components/media/image-controls.js +196 -0
- package/components/media/index.js +13 -0
- package/components/media/inspector-media-upload.js +290 -0
- package/components/media/toolbar-media-upload.js +53 -0
- package/components/media/utils/aspect-ratios.js +118 -0
- package/components/meta/MetaPanel.js +75 -0
- package/components/meta/README.md +46 -0
- package/components/meta/index.js +6 -0
- package/components/meta/meta-gated-editor.js +282 -0
- package/components/query/README.md +340 -0
- package/components/query/cardTemplatePartPanel.js +69 -0
- package/components/query/cardTemplatePartSelect.js +165 -0
- package/components/query/cardTemplatePartUtils.js +119 -0
- package/components/query/columnCountControl.js +63 -0
- package/components/query/displayTypeSelect.js +52 -0
- package/components/query/examples.js +166 -0
- package/components/query/hideCurrentPostControl.js +47 -0
- package/components/query/index.js +20 -0
- package/components/query/manualPostSelector.js +125 -0
- package/components/query/manualTermSelector.js +141 -0
- package/components/query/orderBySelect.js +95 -0
- package/components/query/postTypeSelect.js +67 -0
- package/components/query/postsPerPageControl.js +51 -0
- package/components/query/selectionModeControl.js +61 -0
- package/components/query/taxonomySelect.js +139 -0
- package/components/query/utils.js +75 -0
- package/components/repeater/README.md +159 -0
- package/components/repeater/index.js +829 -0
- package/components/segmented-bar/README.md +30 -0
- package/components/segmented-bar/index.js +68 -0
- package/components/sortable-select/README.md +153 -0
- package/components/sortable-select/custom-sortable-token.js +127 -0
- package/components/sortable-select/index.js +693 -0
- package/components/style-picker/README.md +36 -0
- package/components/style-picker/index.js +92 -0
- package/components/variable-field/README.md +299 -0
- package/components/variable-field/index.js +592 -0
- package/components/variable-field/value-to-chip-html.js +38 -0
- package/components/variable-inserter/README.md +68 -0
- package/components/variable-inserter/index.js +168 -0
- package/constants/statusColors.js +16 -6
- package/editor.js +16 -0
- package/index.js +127 -0
- package/package.json +27 -16
- package/utils/kitaris-localize.js +128 -0
- package/utils/slug-aware-color-setter.js +27 -0
- package/build/index.asset.php +0 -20
- package/build/index.js +0 -1233
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __ } from "@wordpress/i18n";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* External dependencies
|
|
8
|
+
*/
|
|
9
|
+
import styled from "@emotion/styled";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import { CaptchaContainer, InvisibleNotice } from "./styles";
|
|
15
|
+
|
|
16
|
+
const Turnstile = styled.div`
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
background: ${props => {
|
|
20
|
+
if (props.theme === 'dark') { return '#1f1f1f'; }
|
|
21
|
+
if (props.mode === 'non-interactive') { return '#f9fafb'; }
|
|
22
|
+
return 'white';
|
|
23
|
+
}};
|
|
24
|
+
border: 1px solid ${props => props.theme === 'dark' ? '#444' : '#ddd'};
|
|
25
|
+
color: ${props => props.theme === 'dark' ? '#fff' : 'inherit'};
|
|
26
|
+
border-radius: 8px;
|
|
27
|
+
padding: 12px 16px;
|
|
28
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
29
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
30
|
+
position: relative;
|
|
31
|
+
min-width: 300px;
|
|
32
|
+
cursor: not-allowed;
|
|
33
|
+
|
|
34
|
+
${props => props.theme === 'auto' && `
|
|
35
|
+
@media (prefers-color-scheme: dark) {
|
|
36
|
+
background: #1f1f1f;
|
|
37
|
+
border-color: #444;
|
|
38
|
+
color: #fff;
|
|
39
|
+
}
|
|
40
|
+
`}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const TurnstileCheckbox = styled.div`
|
|
44
|
+
width: 24px;
|
|
45
|
+
height: 24px;
|
|
46
|
+
border: 2px solid #d1d5db;
|
|
47
|
+
border-radius: 4px;
|
|
48
|
+
margin-right: 12px;
|
|
49
|
+
background: white;
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const TurnstileLabel = styled.div`
|
|
53
|
+
flex: 1;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
color: ${props => props.theme === 'dark' ? '#fff' : '#374151'};
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const TurnstileSpinner = styled.div`
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
gap: 12px;
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const TurnstileSpinnerInner = styled.div`
|
|
65
|
+
width: 24px;
|
|
66
|
+
height: 24px;
|
|
67
|
+
border: 3px solid #e5e7eb;
|
|
68
|
+
border-top-color: #f97316;
|
|
69
|
+
border-radius: 50%;
|
|
70
|
+
animation: spin 1s linear infinite;
|
|
71
|
+
|
|
72
|
+
@keyframes spin {
|
|
73
|
+
to { transform: rotate(360deg); }
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const TurnstileLogo = styled.div`
|
|
78
|
+
font-size: 12px;
|
|
79
|
+
color: #f97316;
|
|
80
|
+
font-weight: 600;
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
export function CloudflareTurnstile({ theme, mode }) {
|
|
84
|
+
if (mode === 'invisible') {
|
|
85
|
+
return (
|
|
86
|
+
<InvisibleNotice>
|
|
87
|
+
{__("Invisible captcha - runs automatically on form submission", "wp-component-library")}
|
|
88
|
+
</InvisibleNotice>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<CaptchaContainer>
|
|
94
|
+
<Turnstile theme={theme} mode={mode}>
|
|
95
|
+
{mode === 'managed' && (
|
|
96
|
+
<>
|
|
97
|
+
<TurnstileCheckbox />
|
|
98
|
+
<TurnstileLabel theme={theme}>{__("Verify you are human", "wp-component-library")}</TurnstileLabel>
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
{mode === 'non-interactive' && (
|
|
102
|
+
<TurnstileSpinner>
|
|
103
|
+
<TurnstileSpinnerInner />
|
|
104
|
+
<TurnstileLabel theme={theme}>{__("Verifying…", "wp-component-library")}</TurnstileLabel>
|
|
105
|
+
</TurnstileSpinner>
|
|
106
|
+
)}
|
|
107
|
+
<TurnstileLogo>Cloudflare</TurnstileLogo>
|
|
108
|
+
</Turnstile>
|
|
109
|
+
</CaptchaContainer>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __ } from "@wordpress/i18n";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* External dependencies
|
|
8
|
+
*/
|
|
9
|
+
import styled from "@emotion/styled";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import { CaptchaContainer, CheckboxBorder, RECAPTCHA_LOGO_BASE64 } from "./styles";
|
|
15
|
+
|
|
16
|
+
const RecaptchaV2 = styled.div`
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
background: ${props => props.theme === 'dark' ? '#222' : '#f9f9f9'};
|
|
20
|
+
border: 1px solid ${props => props.theme === 'dark' ? '#555' : '#d3d3d3'};
|
|
21
|
+
color: ${props => props.theme === 'dark' ? '#e8eaed' : 'inherit'};
|
|
22
|
+
border-radius: 3px;
|
|
23
|
+
padding: ${props => props.size === 'compact' ? '6px 8px' : '10px 12px'};
|
|
24
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
25
|
+
font-family: Roboto, Arial, sans-serif;
|
|
26
|
+
position: relative;
|
|
27
|
+
cursor: not-allowed;
|
|
28
|
+
font-size: ${props => props.size === 'compact' ? '12px' : '14px'};
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const RecaptchaLabel = styled.div`
|
|
32
|
+
margin-right: 12px;
|
|
33
|
+
font-size: 14px;
|
|
34
|
+
color: #555;
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const RecaptchaLogo = styled.div`
|
|
38
|
+
margin-left: auto;
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 8px;
|
|
42
|
+
|
|
43
|
+
img {
|
|
44
|
+
width: 32px;
|
|
45
|
+
height: 32px;
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const RecaptchaText = styled.div`
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
font-size: 10px;
|
|
53
|
+
color: #555;
|
|
54
|
+
line-height: 1.2;
|
|
55
|
+
|
|
56
|
+
span {
|
|
57
|
+
font-weight: 500;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
small {
|
|
61
|
+
color: #1a73e8;
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const RecaptchaV3Badge = styled.div`
|
|
66
|
+
display: inline-flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
background: white;
|
|
69
|
+
border: 1px solid #ddd;
|
|
70
|
+
border-radius: 3px;
|
|
71
|
+
padding: 8px 12px;
|
|
72
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
73
|
+
font-size: 9px;
|
|
74
|
+
color: #555;
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
const RecaptchaV3Logo = styled.div`
|
|
78
|
+
margin-right: 8px;
|
|
79
|
+
|
|
80
|
+
img {
|
|
81
|
+
width: 24px;
|
|
82
|
+
height: 24px;
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
const RecaptchaV3Text = styled.div`
|
|
87
|
+
line-height: 1.2;
|
|
88
|
+
|
|
89
|
+
strong {
|
|
90
|
+
font-size: 10px;
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
const RecaptchaV3Links = styled.div`
|
|
95
|
+
color: #1a73e8;
|
|
96
|
+
margin-top: 2px;
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
export function GoogleRecaptchaV2({ theme, size }) {
|
|
100
|
+
return (
|
|
101
|
+
<CaptchaContainer>
|
|
102
|
+
<RecaptchaV2 theme={theme} size={size}>
|
|
103
|
+
<CheckboxBorder />
|
|
104
|
+
<RecaptchaLabel>{__("I'm not a robot", "wp-component-library")}</RecaptchaLabel>
|
|
105
|
+
<RecaptchaLogo>
|
|
106
|
+
<img src={RECAPTCHA_LOGO_BASE64} alt="reCAPTCHA" />
|
|
107
|
+
<RecaptchaText>
|
|
108
|
+
<span>reCAPTCHA</span>
|
|
109
|
+
<small>Privacy - Terms</small>
|
|
110
|
+
</RecaptchaText>
|
|
111
|
+
</RecaptchaLogo>
|
|
112
|
+
</RecaptchaV2>
|
|
113
|
+
</CaptchaContainer>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function GoogleRecaptchaV3() {
|
|
118
|
+
return (
|
|
119
|
+
<CaptchaContainer>
|
|
120
|
+
<RecaptchaV3Badge>
|
|
121
|
+
<RecaptchaV3Logo>
|
|
122
|
+
<img src={RECAPTCHA_LOGO_BASE64} alt="reCAPTCHA" />
|
|
123
|
+
</RecaptchaV3Logo>
|
|
124
|
+
<RecaptchaV3Text>
|
|
125
|
+
protected by<br/>
|
|
126
|
+
<strong>reCAPTCHA</strong>
|
|
127
|
+
<RecaptchaV3Links>Privacy - Terms</RecaptchaV3Links>
|
|
128
|
+
</RecaptchaV3Text>
|
|
129
|
+
</RecaptchaV3Badge>
|
|
130
|
+
</CaptchaContainer>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __ } from "@wordpress/i18n";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* External dependencies
|
|
8
|
+
*/
|
|
9
|
+
import styled from "@emotion/styled";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import { CaptchaContainer } from "./styles";
|
|
15
|
+
|
|
16
|
+
const HCaptchaWrapper = styled.div`
|
|
17
|
+
display: inline-flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
background: ${props => props.theme === 'dark' ? '#333' : '#fafafa'};
|
|
20
|
+
border: 1px solid ${props => props.theme === 'dark' ? '#555' : '#e0e0e0'};
|
|
21
|
+
color: ${props => props.theme === 'dark' ? '#fff' : 'inherit'};
|
|
22
|
+
border-radius: 4px;
|
|
23
|
+
padding: ${props => props.size === 'compact' ? '8px 10px' : '12px 14px'};
|
|
24
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
25
|
+
position: relative;
|
|
26
|
+
cursor: not-allowed;
|
|
27
|
+
font-size: ${props => props.size === 'compact' ? '13px' : '15px'};
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const HCaptchaCheckbox = styled.div`
|
|
31
|
+
width: 28px;
|
|
32
|
+
height: 28px;
|
|
33
|
+
border: 2px solid #0074bf;
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
margin-right: 12px;
|
|
36
|
+
background: white;
|
|
37
|
+
position: relative;
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const HCaptchaLabel = styled.div`
|
|
41
|
+
margin-right: 12px;
|
|
42
|
+
font-size: 15px;
|
|
43
|
+
color: ${props => props.theme === 'dark' ? '#fff' : '#333'};
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const HCaptchaLogo = styled.div`
|
|
47
|
+
margin-left: auto;
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: 8px;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const HCaptchaLogoSvg = styled.div`
|
|
54
|
+
width: 24px;
|
|
55
|
+
height: 24px;
|
|
56
|
+
background: #0074bf;
|
|
57
|
+
color: white;
|
|
58
|
+
border-radius: 4px;
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
font-weight: bold;
|
|
63
|
+
font-size: 16px;
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const HCaptchaText = styled.div`
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
font-size: 10px;
|
|
70
|
+
color: #666;
|
|
71
|
+
line-height: 1.2;
|
|
72
|
+
|
|
73
|
+
span {
|
|
74
|
+
font-weight: 500;
|
|
75
|
+
color: ${props => props.theme === 'dark' ? '#fff' : '#333'};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
small {
|
|
79
|
+
color: #0074bf;
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
export function HCaptcha({ theme, size }) {
|
|
84
|
+
return (
|
|
85
|
+
<CaptchaContainer>
|
|
86
|
+
<HCaptchaWrapper theme={theme} size={size}>
|
|
87
|
+
<HCaptchaCheckbox />
|
|
88
|
+
<HCaptchaLabel theme={theme}>{__("I am human", "wp-component-library")}</HCaptchaLabel>
|
|
89
|
+
<HCaptchaLogo>
|
|
90
|
+
<HCaptchaLogoSvg>h</HCaptchaLogoSvg>
|
|
91
|
+
<HCaptchaText theme={theme}>
|
|
92
|
+
<span>hCaptcha</span>
|
|
93
|
+
<small>Privacy - Terms</small>
|
|
94
|
+
</HCaptchaText>
|
|
95
|
+
</HCaptchaLogo>
|
|
96
|
+
</HCaptchaWrapper>
|
|
97
|
+
</CaptchaContainer>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# CaptchaPlaceholder Component
|
|
2
|
+
|
|
3
|
+
A unified captcha component that provides a consistent interface for rendering different captcha providers. Supports Google reCAPTCHA (v2 & v3), hCaptcha, and Cloudflare Turnstile with customizable themes and sizes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Multiple captcha provider support
|
|
8
|
+
- Unified API across all providers
|
|
9
|
+
- Theme customization (light, dark, auto)
|
|
10
|
+
- Size variants (normal, compact)
|
|
11
|
+
- Provider-specific configuration options
|
|
12
|
+
- Clean abstraction with modular architecture
|
|
13
|
+
|
|
14
|
+
## Supported Providers
|
|
15
|
+
|
|
16
|
+
- **Google reCAPTCHA v2** - Traditional checkbox captcha
|
|
17
|
+
- **Google reCAPTCHA v3** - Invisible behavioral analysis
|
|
18
|
+
- **hCaptcha** - Privacy-focused reCAPTCHA alternative
|
|
19
|
+
- **Cloudflare Turnstile** - Modern captcha solution from Cloudflare
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import { CaptchaPlaceholder } from '@siratik/wp-component-library/components/captcha-placeholder';
|
|
25
|
+
|
|
26
|
+
// Google reCAPTCHA v2
|
|
27
|
+
<CaptchaPlaceholder
|
|
28
|
+
provider="google-recaptcha"
|
|
29
|
+
version="v2"
|
|
30
|
+
theme="light"
|
|
31
|
+
size="normal"
|
|
32
|
+
/>
|
|
33
|
+
|
|
34
|
+
// Google reCAPTCHA v3 (invisible)
|
|
35
|
+
<CaptchaPlaceholder
|
|
36
|
+
provider="google-recaptcha"
|
|
37
|
+
version="v3"
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
// hCaptcha
|
|
41
|
+
<CaptchaPlaceholder
|
|
42
|
+
provider="hcaptcha"
|
|
43
|
+
theme="dark"
|
|
44
|
+
size="compact"
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
// Cloudflare Turnstile
|
|
48
|
+
<CaptchaPlaceholder
|
|
49
|
+
provider="cloudflare-turnstile"
|
|
50
|
+
theme="auto"
|
|
51
|
+
mode="managed"
|
|
52
|
+
/>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Props
|
|
56
|
+
|
|
57
|
+
| Prop | Type | Default | Description |
|
|
58
|
+
|------|------|---------|-------------|
|
|
59
|
+
| `provider` | `string` | Required | Captcha provider: "google-recaptcha", "hcaptcha", or "cloudflare-turnstile" |
|
|
60
|
+
| `theme` | `string` | `"light"` | Visual theme: "light", "dark", or "auto" |
|
|
61
|
+
| `size` | `string` | `"normal"` | Captcha size: "normal" or "compact" |
|
|
62
|
+
| `version` | `string` | `"v2"` | Google reCAPTCHA version: "v2" or "v3" (Google only) |
|
|
63
|
+
| `mode` | `string` | `"managed"` | Cloudflare Turnstile mode: "managed", "non-interactive", or "invisible" |
|
|
64
|
+
|
|
65
|
+
## Provider-Specific Configuration
|
|
66
|
+
|
|
67
|
+
### Google reCAPTCHA
|
|
68
|
+
|
|
69
|
+
#### Version 2 (Checkbox)
|
|
70
|
+
```jsx
|
|
71
|
+
<CaptchaPlaceholder
|
|
72
|
+
provider="google-recaptcha"
|
|
73
|
+
version="v2"
|
|
74
|
+
theme="light" // or "dark"
|
|
75
|
+
size="normal" // or "compact"
|
|
76
|
+
/>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
#### Version 3 (Invisible)
|
|
80
|
+
```jsx
|
|
81
|
+
<CaptchaPlaceholder
|
|
82
|
+
provider="google-recaptcha"
|
|
83
|
+
version="v3"
|
|
84
|
+
// v3 is invisible, theme and size are ignored
|
|
85
|
+
/>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### hCaptcha
|
|
89
|
+
```jsx
|
|
90
|
+
<CaptchaPlaceholder
|
|
91
|
+
provider="hcaptcha"
|
|
92
|
+
theme="dark" // "light", "dark", or "auto"
|
|
93
|
+
size="compact" // "normal" or "compact"
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Cloudflare Turnstile
|
|
98
|
+
```jsx
|
|
99
|
+
<CaptchaPlaceholder
|
|
100
|
+
provider="cloudflare-turnstile"
|
|
101
|
+
theme="auto" // "light", "dark", or "auto"
|
|
102
|
+
mode="managed" // "managed", "non-interactive", or "invisible"
|
|
103
|
+
/>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Theme Options
|
|
107
|
+
|
|
108
|
+
- **light** - Light background, dark text
|
|
109
|
+
- **dark** - Dark background, light text
|
|
110
|
+
- **auto** - Automatically matches user's system preference (Turnstile & hCaptcha only)
|
|
111
|
+
|
|
112
|
+
## Size Options
|
|
113
|
+
|
|
114
|
+
- **normal** - Standard size captcha
|
|
115
|
+
- **compact** - Smaller footprint for space-constrained layouts
|
|
116
|
+
|
|
117
|
+
## Cloudflare Turnstile Modes
|
|
118
|
+
|
|
119
|
+
- **managed** - Cloudflare decides when to show challenges
|
|
120
|
+
- **non-interactive** - Never shows interactive challenges
|
|
121
|
+
- **invisible** - Completely hidden from view
|
|
122
|
+
|
|
123
|
+
## Examples
|
|
124
|
+
|
|
125
|
+
### Form with reCAPTCHA v2
|
|
126
|
+
```jsx
|
|
127
|
+
function ContactForm() {
|
|
128
|
+
return (
|
|
129
|
+
<form>
|
|
130
|
+
<input type="email" placeholder="Email" />
|
|
131
|
+
<textarea placeholder="Message" />
|
|
132
|
+
|
|
133
|
+
<CaptchaPlaceholder
|
|
134
|
+
provider="google-recaptcha"
|
|
135
|
+
version="v2"
|
|
136
|
+
theme="light"
|
|
137
|
+
/>
|
|
138
|
+
|
|
139
|
+
<button type="submit">Send</button>
|
|
140
|
+
</form>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Dark Mode Support
|
|
146
|
+
```jsx
|
|
147
|
+
function DarkModeForm({ isDarkMode }) {
|
|
148
|
+
return (
|
|
149
|
+
<form>
|
|
150
|
+
<CaptchaPlaceholder
|
|
151
|
+
provider="hcaptcha"
|
|
152
|
+
theme={isDarkMode ? "dark" : "light"}
|
|
153
|
+
size="normal"
|
|
154
|
+
/>
|
|
155
|
+
</form>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Invisible Protection
|
|
161
|
+
```jsx
|
|
162
|
+
function SecureAction() {
|
|
163
|
+
return (
|
|
164
|
+
<div>
|
|
165
|
+
<button onClick={handleAction}>
|
|
166
|
+
Perform Secure Action
|
|
167
|
+
</button>
|
|
168
|
+
|
|
169
|
+
<CaptchaPlaceholder
|
|
170
|
+
provider="cloudflare-turnstile"
|
|
171
|
+
mode="invisible"
|
|
172
|
+
/>
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Compact Layout
|
|
179
|
+
```jsx
|
|
180
|
+
function SidebarForm() {
|
|
181
|
+
return (
|
|
182
|
+
<aside className="sidebar">
|
|
183
|
+
<form>
|
|
184
|
+
<input type="text" />
|
|
185
|
+
|
|
186
|
+
<CaptchaPlaceholder
|
|
187
|
+
provider="hcaptcha"
|
|
188
|
+
size="compact"
|
|
189
|
+
theme="auto"
|
|
190
|
+
/>
|
|
191
|
+
|
|
192
|
+
<button>Submit</button>
|
|
193
|
+
</form>
|
|
194
|
+
</aside>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Integration Notes
|
|
200
|
+
|
|
201
|
+
### Site Keys Required
|
|
202
|
+
Each provider requires site keys to be configured:
|
|
203
|
+
- Google reCAPTCHA: Obtain from [Google reCAPTCHA Admin](https://www.google.com/recaptcha/admin)
|
|
204
|
+
- hCaptcha: Get from [hCaptcha Dashboard](https://dashboard.hcaptcha.com)
|
|
205
|
+
- Cloudflare Turnstile: Available in [Cloudflare Dashboard](https://dash.cloudflare.com)
|
|
206
|
+
|
|
207
|
+
### Server-Side Validation
|
|
208
|
+
Remember to validate captcha responses on your server:
|
|
209
|
+
1. Capture the captcha response token
|
|
210
|
+
2. Send to your server with form data
|
|
211
|
+
3. Verify with the provider's API
|
|
212
|
+
4. Process form only if verification succeeds
|
|
213
|
+
|
|
214
|
+
### Styling
|
|
215
|
+
The component renders provider-specific markup. Additional styling can be applied via wrapper elements:
|
|
216
|
+
```jsx
|
|
217
|
+
<div className="captcha-wrapper">
|
|
218
|
+
<CaptchaPlaceholder {...props} />
|
|
219
|
+
</div>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Notes
|
|
223
|
+
|
|
224
|
+
- Returns `null` for unsupported providers
|
|
225
|
+
- Each provider component is loaded separately for optimal bundle size
|
|
226
|
+
- Theme and size support varies by provider
|
|
227
|
+
- Ensure you comply with each provider's terms of service
|
|
228
|
+
- Consider privacy implications when choosing a provider
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { GoogleRecaptchaV2, GoogleRecaptchaV3 } from './GoogleRecaptcha';
|
|
5
|
+
import { HCaptcha } from './HCaptcha';
|
|
6
|
+
import { CloudflareTurnstile } from './CloudflareTurnstile';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Captcha placeholder component
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} props Component props
|
|
12
|
+
* @param {string} props.provider - The captcha provider (google-recaptcha, hcaptcha, cloudflare-turnstile)
|
|
13
|
+
* @param {string} props.theme - Theme for the captcha (light, dark, auto)
|
|
14
|
+
* @param {string} props.size - Size for the captcha (normal, compact)
|
|
15
|
+
* @param {string} props.version - Version for Google reCAPTCHA (v2, v3)
|
|
16
|
+
* @param {string} props.mode - Mode for Cloudflare Turnstile (managed, non-interactive, invisible)
|
|
17
|
+
* @returns {WPElement} Captcha placeholder element
|
|
18
|
+
*/
|
|
19
|
+
export function CaptchaPlaceholder({ provider, theme = 'light', size = 'normal', version = 'v2', mode = 'managed' }) {
|
|
20
|
+
if (provider === 'google-recaptcha' && version === 'v2') {
|
|
21
|
+
return <GoogleRecaptchaV2 theme={theme} size={size} />;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (provider === 'google-recaptcha' && version === 'v3') {
|
|
25
|
+
return <GoogleRecaptchaV3 />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (provider === 'hcaptcha') {
|
|
29
|
+
return <HCaptcha theme={theme} size={size} />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (provider === 'cloudflare-turnstile') {
|
|
33
|
+
return <CloudflareTurnstile theme={theme} mode={mode} />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default CaptchaPlaceholder;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import styled from "@emotion/styled";
|
|
5
|
+
|
|
6
|
+
export const CaptchaContainer = styled.div`
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: flex-start;
|
|
10
|
+
margin-bottom: 16px;
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
export const CheckboxBorder = styled.div`
|
|
14
|
+
width: 24px;
|
|
15
|
+
height: 24px;
|
|
16
|
+
border: 2px solid #c1c1c1;
|
|
17
|
+
border-radius: 2px;
|
|
18
|
+
margin-right: 12px;
|
|
19
|
+
background: white;
|
|
20
|
+
position: relative;
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
export const InvisibleNotice = styled.div`
|
|
24
|
+
padding: 12px 16px;
|
|
25
|
+
background: #f0f9ff;
|
|
26
|
+
border: 1px solid #bae6fd;
|
|
27
|
+
border-radius: 4px;
|
|
28
|
+
color: #0369a1;
|
|
29
|
+
font-size: 13px;
|
|
30
|
+
text-align: center;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
export const RECAPTCHA_LOGO_BASE64 = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEyIDJDNi40OCAyIDIgNi40OCAyIDEyczQuNDggMTAgMTAgMTAgMTAtNC40OCAxMC0xMFMxNy41MiAyIDEyIDJ6bS0yIDE1bC01LTUgMS40MS0xLjQxTDEwIDE0LjE3bDcuNTktNy41OUwxOSA4bC05IDl6IiBmaWxsPSIjNDI4NUY0Ii8+Cjwvc3ZnPg==";
|