@stackshift-ui/signin-signup 6.0.2 → 6.0.4

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.
@@ -0,0 +1,287 @@
1
+ import { Button } from "@stackshift-ui/button";
2
+ import { Container } from "@stackshift-ui/container";
3
+ import { Flex } from "@stackshift-ui/flex";
4
+ import { Form } from "@stackshift-ui/form";
5
+ import { FormField } from "@stackshift-ui/form-field";
6
+ import { Heading } from "@stackshift-ui/heading";
7
+ import { Image } from "@stackshift-ui/image";
8
+ import { Input } from "@stackshift-ui/input";
9
+ import { Link } from "@stackshift-ui/link";
10
+ import { Section } from "@stackshift-ui/section";
11
+ import { Text } from "@stackshift-ui/text";
12
+ import React from "react";
13
+
14
+ import { SignUpFormProps } from ".";
15
+ import { logoLink, thankYouPageLink } from "./helper";
16
+ import { LabeledRoute, LabeledRouteWithKey, Logo, Form as iForm } from "./types";
17
+
18
+ export default function SigninSignup_A({ logo, form, formLinks, signInLink }: SignUpFormProps) {
19
+ return (
20
+ <Section className="py-10 bg-gray-50 lg:py-20">
21
+ <Container maxWidth={576}>
22
+ <LogoSection logo={logo} />
23
+ <Container className="mb-6 text-center lg:mb-10">
24
+ <SubtitleAndHeadingText form={form} />
25
+ <SignupForm
26
+ form={form}
27
+ signInLink={signInLink}
28
+ thankYouPage={thankYouPageLink(form?.thankYouPage)}
29
+ />
30
+ </Container>
31
+ <FormLinks formLinks={formLinks} />
32
+ </Container>
33
+ </Section>
34
+ );
35
+ }
36
+
37
+ function LogoSection({ logo }: { logo?: Logo }) {
38
+ if (!logo) return null;
39
+
40
+ return (
41
+ <div className="mb-10">
42
+ <Link
43
+ aria-label={`Go to ${logoLink(logo) === "/" ? "home page" : logoLink(logo)}`}
44
+ className="flex justify-center mx-auto text-3xl font-bold leading-none"
45
+ href={logoLink(logo)}
46
+ target={logo?.linkTarget}
47
+ rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
48
+ <Image
49
+ src={logo?.image}
50
+ alt={logo?.alt ?? "signup-logo"}
51
+ width={100}
52
+ height={100}
53
+ className="flex justify-center mx-auto text-3xl font-bold leading-none"
54
+ />
55
+ </Link>
56
+ </div>
57
+ );
58
+ }
59
+
60
+ function SubtitleAndHeadingText({ form }: { form?: iForm }) {
61
+ return (
62
+ <div className="mb-6">
63
+ {form?.subtitle ? <Text muted>{form?.subtitle}</Text> : null}
64
+ {form?.name ? <Heading className="text-2xl lg:text-2xl">{form?.name}</Heading> : null}
65
+ </div>
66
+ );
67
+ }
68
+
69
+ function SignupForm({
70
+ form,
71
+ signInLink,
72
+ thankYouPage,
73
+ }: {
74
+ form?: iForm;
75
+ signInLink?: LabeledRoute;
76
+ thankYouPage?: LabeledRoute;
77
+ }) {
78
+ if (!form?.fields) return null;
79
+ const [showPassword, setShowPassword] = React.useState(false);
80
+
81
+ return (
82
+ <Form
83
+ id={form?.id ?? undefined}
84
+ name="SignUp-VariantA-Form"
85
+ className="form-signup"
86
+ thankyouPage={thankYouPage}>
87
+ <FormFields form={form} showPassword={showPassword} setShowPassword={setShowPassword} />
88
+ <div>
89
+ <div className="webriq-recaptcha" />
90
+ </div>
91
+ <div className="text-center">
92
+ {form?.buttonLabel && (
93
+ <Button
94
+ as="button"
95
+ variant="custom"
96
+ ariaLabel={form?.buttonLabel ?? "Sign Up form submit button"}
97
+ className="w-full py-4 text-sm font-bold tex-gray-50"
98
+ type="submit">
99
+ {form?.buttonLabel}
100
+ </Button>
101
+ )}
102
+ </div>
103
+ {signInLink && <SignInLink signInLink={signInLink} />}
104
+ </Form>
105
+ );
106
+ }
107
+
108
+ function FormFields({
109
+ form,
110
+ showPassword,
111
+ setShowPassword,
112
+ }: {
113
+ form?: iForm;
114
+ showPassword: boolean;
115
+ setShowPassword: React.Dispatch<React.SetStateAction<boolean>>;
116
+ }) {
117
+ return (
118
+ <>
119
+ <Flex wrap className="-mx-2">
120
+ {form?.fields?.slice(0, 2).map((formFields, index) => (
121
+ <div className="w-full px-2 mb-3 lg:w-1/2" key={index}>
122
+ {formFields.type === "inputText" ? (
123
+ <Input
124
+ textSize="sm"
125
+ variant="primary"
126
+ noLabel
127
+ placeholder={formFields?.placeholder}
128
+ required={formFields?.isRequired}
129
+ className="w-full py-4 text-xs bg-white"
130
+ name={formFields?.name}
131
+ ariaLabel={formFields?.label}
132
+ {...formFields}
133
+ type="text"
134
+ />
135
+ ) : (
136
+ <FormField textSize="sm" noLabel name={formFields?.name ?? ""} {...formFields} />
137
+ )}
138
+ </div>
139
+ ))}
140
+ </Flex>
141
+ {form?.fields?.slice(2).map((formFields, index) => (
142
+ <div key={index} className="my-3">
143
+ {formFields.type === "inputPassword" ? (
144
+ <PasswordField
145
+ formFields={formFields}
146
+ showPassword={showPassword}
147
+ setShowPassword={setShowPassword}
148
+ />
149
+ ) : (
150
+ <FormField
151
+ className="py-4"
152
+ textSize="sm"
153
+ noLabel
154
+ variant="primary"
155
+ placeholder={formFields?.placeholder}
156
+ required={formFields?.isRequired}
157
+ name={formFields?.name ?? ""}
158
+ items={formFields?.items}
159
+ type={formFields?.type}
160
+ {...formFields}
161
+ />
162
+ )}
163
+ </div>
164
+ ))}
165
+ </>
166
+ );
167
+ }
168
+
169
+ function SignInLink({ signInLink }: { signInLink?: LabeledRoute }) {
170
+ if (!signInLink?.label) return null;
171
+
172
+ return (
173
+ <div className="w-full text-center mt-3">
174
+ <span className="text-xs text-gray-500">Already have an account? </span>
175
+ <Button
176
+ as="link"
177
+ variant="link"
178
+ link={signInLink}
179
+ className="text-xs text-primary cursor-pointer hover:underline"
180
+ ariaLabel={signInLink?.label}>
181
+ {signInLink?.label}
182
+ </Button>
183
+ </div>
184
+ );
185
+ }
186
+
187
+ function PasswordField({
188
+ formFields,
189
+ showPassword,
190
+ setShowPassword,
191
+ }: {
192
+ formFields?: any;
193
+ showPassword: boolean;
194
+ setShowPassword: React.Dispatch<React.SetStateAction<boolean>>;
195
+ }) {
196
+ return (
197
+ <Flex className="relative">
198
+ <Input
199
+ className="py-4"
200
+ textSize="sm"
201
+ noLabel
202
+ aria-label={formFields?.placeholder ?? formFields?.name}
203
+ variant="primary"
204
+ type={showPassword ? "text" : "password"}
205
+ placeholder={formFields?.placeholder}
206
+ name={formFields?.name}
207
+ required={formFields?.isRequired}
208
+ />
209
+ <Button
210
+ as="button"
211
+ variant="unstyled"
212
+ ariaLabel={showPassword ? "Show password" : "Hide password"}
213
+ className="absolute top-0 right-0 h-full p-2"
214
+ type="button"
215
+ onClick={() => setShowPassword(!showPassword)}>
216
+ <PasswordIcon showPassword={showPassword} />
217
+ </Button>
218
+ </Flex>
219
+ );
220
+ }
221
+
222
+ function FormLinks({ formLinks }: { formLinks?: LabeledRouteWithKey[] }) {
223
+ if (!formLinks) return null;
224
+
225
+ return (
226
+ <p className="mt-10 text-xs text-center text-gray-700">
227
+ {formLinks?.map((link: any, index: number, { length }: any) => (
228
+ <span key={index}>
229
+ <Button
230
+ as="link"
231
+ variant="link"
232
+ link={link}
233
+ className="text-xs text-primary cursor-pointer hover:underline"
234
+ ariaLabel={link?.label}>
235
+ {link?.label}
236
+ </Button>
237
+ {index === length - 1 ? null : index === length - 2 ? (
238
+ <span>&nbsp;and&nbsp;</span>
239
+ ) : (
240
+ <span>&nbsp;,&nbsp;</span>
241
+ )}
242
+ </span>
243
+ ))}
244
+ </p>
245
+ );
246
+ }
247
+
248
+ function PasswordIcon({ showPassword }: { showPassword: boolean }) {
249
+ return (
250
+ <>
251
+ {showPassword ? (
252
+ <svg
253
+ className="w-5 h-5 my-auto ml-4 text-gray-500"
254
+ xmlns="http://www.w3.org/2000/svg"
255
+ aria-hidden="true"
256
+ role="img"
257
+ width="1em"
258
+ height="1em"
259
+ preserveAspectRatio="xMidYMid meet"
260
+ viewBox="0 0 16 16">
261
+ <g fill="currentColor">
262
+ <path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288c-.335.48-.83 1.12-1.465 1.755c-.165.165-.337.328-.517.486l.708.709z" />
263
+ <path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299l.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z" />
264
+ <path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884l-12-12l.708-.708l12 12l-.708.708z" />
265
+ </g>
266
+ </svg>
267
+ ) : (
268
+ <svg
269
+ className="w-5 h-5 my-auto ml-4 text-gray-500"
270
+ xmlns="http://www.w3.org/2000/svg"
271
+ aria-hidden="true"
272
+ role="img"
273
+ width="1em"
274
+ height="1em"
275
+ preserveAspectRatio="xMidYMid meet"
276
+ viewBox="0 0 16 16">
277
+ <g fill="currentColor">
278
+ <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288c-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z" />
279
+ <path d="M8 5.5a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0a3.5 3.5 0 0 1-7 0z" />
280
+ </g>
281
+ </svg>
282
+ )}
283
+ </>
284
+ );
285
+ }
286
+
287
+ export { SigninSignup_A };
@@ -0,0 +1,257 @@
1
+ import { Button } from "@stackshift-ui/button";
2
+ import { Card } from "@stackshift-ui/card";
3
+ import { Container } from "@stackshift-ui/container";
4
+ import { Flex } from "@stackshift-ui/flex";
5
+ import { Form } from "@stackshift-ui/form";
6
+ import { FormField } from "@stackshift-ui/form-field";
7
+ import { Heading } from "@stackshift-ui/heading";
8
+ import { Image } from "@stackshift-ui/image";
9
+ import { Input } from "@stackshift-ui/input";
10
+ import { Link } from "@stackshift-ui/link";
11
+ import { Section } from "@stackshift-ui/section";
12
+ import { Text } from "@stackshift-ui/text";
13
+ import React, { useState } from "react";
14
+ import { SignUpFormProps } from ".";
15
+ import { logoLink, thankYouPageLink } from "./helper";
16
+ import { LabeledRoute, Logo, Form as iForm } from "./types";
17
+
18
+ export default function SigninSignup_B({ logo, form, formLinks, signInLink }: SignUpFormProps) {
19
+ return (
20
+ <Section className="py-10 bg-primary lg:py-20">
21
+ <Container maxWidth={1280}>
22
+ <Container maxWidth={576}>
23
+ <LogoSection logo={logo} />
24
+ <Card className="p-6 mb-6 bg-white lg:mb-10 lg:p-12">
25
+ <SubtitleAndHeadingText form={form} />
26
+ <SignupForm form={form} signInLink={signInLink} />
27
+ </Card>
28
+ <FormLinks formLinks={formLinks} />
29
+ </Container>
30
+ </Container>
31
+ </Section>
32
+ );
33
+ }
34
+
35
+ function LogoSection({ logo }: { logo?: Logo }) {
36
+ if (!logo) return null;
37
+
38
+ return (
39
+ <div className="mb-10">
40
+ <Link
41
+ aria-label={`Go to ${logoLink(logo) === "/" ? "home page" : logoLink(logo)}`}
42
+ className="flex justify-center mx-auto text-3xl font-bold leading-none"
43
+ href={logoLink(logo)}
44
+ target={logo?.linkTarget}
45
+ rel={logo?.linkTarget === "_blank" ? "noopener noreferrer" : ""}>
46
+ <Image
47
+ src={logo?.image}
48
+ alt={logo?.alt ?? "signup-logo"}
49
+ width={100}
50
+ height={100}
51
+ className="flex justify-center text-3xl font-bold leading-none text-white"
52
+ />
53
+ </Link>
54
+ </div>
55
+ );
56
+ }
57
+
58
+ function SubtitleAndHeadingText({ form }: { form?: iForm }) {
59
+ return (
60
+ <div className="mb-6">
61
+ <Text muted>{form?.subtitle}</Text>
62
+ <Heading className="text-2xl lg:text-2xl">{form?.name}</Heading>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ function SignupForm({ form, signInLink }: { form?: iForm; signInLink?: LabeledRoute }) {
68
+ if (!form?.fields) return null;
69
+ const [showPassword, setShowPassword] = useState(false);
70
+
71
+ return (
72
+ <Form
73
+ id={form?.id ?? undefined}
74
+ name="SignUp-VariantB-Form"
75
+ className="form-signup"
76
+ thankyouPage={thankYouPageLink(form?.thankYouPage)}>
77
+ <FormFields form={form} showPassword={showPassword} setShowPassword={setShowPassword} />
78
+ <div>
79
+ <div className="webriq-recaptcha" />
80
+ </div>
81
+ <div className="text-center">
82
+ <FormButtonLabel form={form} />
83
+ <SigninLink signInLink={signInLink} />
84
+ </div>
85
+ </Form>
86
+ );
87
+ }
88
+
89
+ function FormFields({
90
+ form,
91
+ showPassword,
92
+ setShowPassword,
93
+ }: {
94
+ form?: iForm;
95
+ showPassword: boolean;
96
+ setShowPassword: React.Dispatch<React.SetStateAction<boolean>>;
97
+ }) {
98
+ return (
99
+ <React.Fragment>
100
+ <Flex className="flex-col lg:flex-row gap-3">
101
+ {form?.fields?.slice(0, 2)?.map((formFields, index) => (
102
+ <div className="w-full" key={index}>
103
+ <FormField
104
+ noLabel
105
+ variant="secondary"
106
+ placeholder={formFields?.placeholder}
107
+ required={formFields?.isRequired}
108
+ name={formFields?.name ?? ""}
109
+ items={formFields?.items}
110
+ type={formFields?.type}
111
+ {...formFields}
112
+ />
113
+ </div>
114
+ ))}
115
+ </Flex>
116
+
117
+ {form?.fields?.slice(2)?.map((formFields, index) => (
118
+ <div key={index} className="my-3">
119
+ {formFields?.type === "inputPassword" ? (
120
+ <div className="flex">
121
+ <Input
122
+ noLabel
123
+ ariaLabel={formFields?.placeholder ?? formFields?.name}
124
+ variant="secondary"
125
+ type={showPassword ? "text" : "password"}
126
+ placeholder={formFields?.placeholder}
127
+ name={formFields?.name}
128
+ required={formFields?.isRequired}
129
+ />
130
+ {/* SVG icon on the right of the password input field */}
131
+ <Button
132
+ variant="unstyled"
133
+ as="button"
134
+ ariaLabel={showPassword ? "Show password" : "Hide password"}
135
+ className="focus:outline-none mr-4"
136
+ type="button"
137
+ onClick={() => setShowPassword(!showPassword)}>
138
+ <PasswordIcon showPassword={showPassword} />
139
+ </Button>
140
+ </div>
141
+ ) : (
142
+ <FormField
143
+ noLabel
144
+ variant="secondary"
145
+ name={formFields?.name ?? ""}
146
+ placeholder={formFields?.placeholder}
147
+ required={formFields?.isRequired}
148
+ items={formFields?.items}
149
+ type={formFields?.type}
150
+ {...formFields}
151
+ />
152
+ )}
153
+ </div>
154
+ ))}
155
+ </React.Fragment>
156
+ );
157
+ }
158
+
159
+ function FormButtonLabel({ form }: { form?: iForm }) {
160
+ if (!form?.buttonLabel) return null;
161
+
162
+ return (
163
+ <Button
164
+ as="button"
165
+ className="w-full py-4 mb-3"
166
+ ariaLabel={form?.buttonLabel ?? "Sign Up form submit button"}
167
+ variant="custom"
168
+ type="submit">
169
+ {form?.buttonLabel}
170
+ </Button>
171
+ );
172
+ }
173
+
174
+ function SigninLink({ signInLink }: { signInLink?: LabeledRoute }) {
175
+ if (!signInLink?.label) return null;
176
+
177
+ return (
178
+ <span className="text-xs text-gray-900">
179
+ <span>Already have an account?</span>{" "}
180
+ <Button
181
+ as="link"
182
+ variant="link"
183
+ link={signInLink}
184
+ className="text-xs text-primary hover:underline"
185
+ ariaLabel={signInLink?.label}>
186
+ {signInLink?.label}
187
+ </Button>
188
+ </span>
189
+ );
190
+ }
191
+
192
+ function FormLinks({ formLinks }: { formLinks?: LabeledRoute[] }) {
193
+ if (!formLinks) return null;
194
+
195
+ return (
196
+ <p className="text-xs text-center text-secondary-foreground">
197
+ {formLinks?.map((link, index, { length }) => (
198
+ <span key={index}>
199
+ <Button
200
+ as="link"
201
+ variant="link"
202
+ link={link}
203
+ className="text-xs underline text-secondary-foreground hover:text-gray-50"
204
+ ariaLabel={link?.label}>
205
+ {link?.label}
206
+ </Button>
207
+ {index === length - 1 ? null : index === length - 2 ? (
208
+ <span>&nbsp;and&nbsp;</span>
209
+ ) : (
210
+ <span>&nbsp;,&nbsp;</span>
211
+ )}
212
+ </span>
213
+ ))}
214
+ </p>
215
+ );
216
+ }
217
+
218
+ function PasswordIcon({ showPassword }: { showPassword: boolean }) {
219
+ return (
220
+ <React.Fragment>
221
+ {showPassword ? (
222
+ <svg
223
+ className="w-5 h-5 my-auto text-gray-500"
224
+ xmlns="http://www.w3.org/2000/svg"
225
+ aria-hidden="true"
226
+ role="img"
227
+ width="1em"
228
+ height="1em"
229
+ preserveAspectRatio="xMidYMid meet"
230
+ viewBox="0 0 16 16">
231
+ <g fill="currentColor">
232
+ <path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288c-.335.48-.83 1.12-1.465 1.755c-.165.165-.337.328-.517.486l.708.709z" />
233
+ <path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299l.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z" />
234
+ <path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884l-12-12l.708-.708l12 12l-.708.708z" />
235
+ </g>
236
+ </svg>
237
+ ) : (
238
+ <svg
239
+ className="w-5 h-5 my-auto ml-4 text-gray-500"
240
+ xmlns="http://www.w3.org/2000/svg"
241
+ aria-hidden="true"
242
+ role="img"
243
+ width="1em"
244
+ height="1em"
245
+ preserveAspectRatio="xMidYMid meet"
246
+ viewBox="0 0 16 16">
247
+ <g fill="currentColor">
248
+ <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288c-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z" />
249
+ <path d="M8 5.5a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0a3.5 3.5 0 0 1-7 0z" />
250
+ </g>
251
+ </svg>
252
+ )}
253
+ </React.Fragment>
254
+ );
255
+ }
256
+
257
+ export { SigninSignup_B };