@stackshift-ui/signin-signup 6.0.3 → 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.
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@stackshift-ui/signin-signup",
3
3
  "description": "",
4
- "version": "6.0.3",
4
+ "version": "6.0.4",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
8
8
  "module": "./dist/index.mjs",
9
9
  "types": "./dist/index.d.ts",
10
10
  "files": [
11
- "dist/**"
11
+ "dist/**",
12
+ "src"
12
13
  ],
13
14
  "author": "WebriQ <info@webriq.com>",
14
15
  "devDependencies": {
@@ -33,20 +34,20 @@
33
34
  },
34
35
  "dependencies": {
35
36
  "classnames": "^2.5.1",
37
+ "@stackshift-ui/card": "6.0.3",
36
38
  "@stackshift-ui/scripts": "6.0.2",
37
- "@stackshift-ui/button": "6.0.2",
38
- "@stackshift-ui/card": "6.0.2",
39
- "@stackshift-ui/heading": "6.0.2",
40
- "@stackshift-ui/system": "6.0.2",
41
- "@stackshift-ui/input": "6.0.3",
42
- "@stackshift-ui/text": "6.0.2",
43
- "@stackshift-ui/link": "6.0.2",
44
- "@stackshift-ui/image": "6.0.2",
45
- "@stackshift-ui/form": "6.0.2",
46
- "@stackshift-ui/form-field": "6.0.3",
47
- "@stackshift-ui/container": "6.0.2",
48
- "@stackshift-ui/section": "6.0.2",
49
- "@stackshift-ui/flex": "6.0.2"
39
+ "@stackshift-ui/heading": "6.0.3",
40
+ "@stackshift-ui/system": "6.0.3",
41
+ "@stackshift-ui/button": "6.0.3",
42
+ "@stackshift-ui/input": "6.0.4",
43
+ "@stackshift-ui/text": "6.0.3",
44
+ "@stackshift-ui/link": "6.0.3",
45
+ "@stackshift-ui/image": "6.0.3",
46
+ "@stackshift-ui/form": "6.0.3",
47
+ "@stackshift-ui/form-field": "6.0.4",
48
+ "@stackshift-ui/section": "6.0.3",
49
+ "@stackshift-ui/flex": "6.0.3",
50
+ "@stackshift-ui/container": "6.0.3"
50
51
  },
51
52
  "peerDependencies": {
52
53
  "@types/react": "16.8 - 19",
@@ -0,0 +1,28 @@
1
+ import { Logo } from "../types";
2
+
3
+ // Logo link
4
+ export const logoLink = (logo: Logo) => {
5
+ if (logo?.internalLink && logo?.type === "linkInternal") {
6
+ if (logo?.internalLink?.toLowerCase()?.includes("home")) {
7
+ return "/";
8
+ }
9
+ return `/${logo.internalLink}`;
10
+ } else if (logo?.externalLink && logo?.type === "linkExternal") {
11
+ return logo?.externalLink ?? "/";
12
+ } else {
13
+ return "/";
14
+ }
15
+ };
16
+
17
+ // WebriQ form redirect thank you page on successful submission
18
+ export const thankYouPageLink = (link: any) => {
19
+ if (!link) {
20
+ return "/thank-you";
21
+ } else {
22
+ if (link?.linkType === "linkInternal") {
23
+ return `/${link?.internalLink}`;
24
+ } else {
25
+ return link?.externalLink;
26
+ }
27
+ }
28
+ };
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ "use client";
2
+
3
+ // component exports
4
+ export * from "./signin-signup";
5
+ export * from "./signin_signup_a";
6
+ export * from "./signin_signup_b";
@@ -0,0 +1,13 @@
1
+ import { cleanup, render, screen } from "@testing-library/react";
2
+ import { afterEach, describe, test } from "vitest";
3
+ import { SigninSignup } from "./signin-signup";
4
+
5
+ describe.concurrent("signin-signup", () => {
6
+ afterEach(cleanup);
7
+
8
+ test.skip("Dummy test - test if renders without errors", ({ expect }) => {
9
+ const clx = "my-class";
10
+ render(<SigninSignup />);
11
+ expect(screen.getByTestId("{ kebabCase name }}").classList).toContain(clx);
12
+ });
13
+ });
@@ -0,0 +1,42 @@
1
+ import React, { lazy } from "react";
2
+ import { SectionsProps, Logo, LabeledRoute, LabeledRouteWithKey, Form } from "./types";
3
+
4
+ const Variants = {
5
+ variant_a: lazy(() => import("./signin_signup_a")),
6
+ variant_b: lazy(() => import("./signin_signup_b")),
7
+ };
8
+
9
+ export interface SignUpFormProps {
10
+ logo?: Logo;
11
+ title?: string;
12
+ subtitle?: string;
13
+ text?: string;
14
+ firstButton?: LabeledRoute;
15
+ secondButton?: LabeledRoute;
16
+ formLinks?: LabeledRouteWithKey[];
17
+ signInLink?: LabeledRoute;
18
+ form?: Form;
19
+ }
20
+
21
+ const displayName = "SigninSignup";
22
+
23
+ export const SigninSignup: React.FC<SectionsProps> = ({ data }) => {
24
+ const variant = data?.variant;
25
+ const Variant = variant && Variants?.[variant as keyof typeof Variants];
26
+
27
+ const props = {
28
+ logo: data?.variants?.logo ?? undefined,
29
+ title: data?.variants?.title ?? undefined,
30
+ subtitle: data?.variants?.subtitle ?? undefined,
31
+ text: data?.variants?.plainText ?? undefined,
32
+ firstButton: data?.variants?.primaryButton ?? undefined,
33
+ secondButton: data?.variants?.secondaryButton ?? undefined,
34
+ formLinks: data?.variants?.formLinks ?? undefined,
35
+ signInLink: data?.variants?.signinLink ?? undefined,
36
+ form: data?.variants?.form ?? undefined,
37
+ };
38
+
39
+ return Variant ? <Variant {...props} /> : null;
40
+ };
41
+
42
+ SigninSignup.displayName = displayName;
@@ -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 };
package/src/types.ts ADDED
@@ -0,0 +1,412 @@
1
+ export type StyleVariants<T extends string> = Record<T, string>;
2
+
3
+ export type Socials = "facebook" | "instagram" | "youtube" | "linkedin" | "twitter";
4
+ export interface MainImage {
5
+ image: string;
6
+ alt?: string;
7
+ }
8
+
9
+ export interface LabeledRoute extends ConditionalLink {
10
+ ariaLabel?: string;
11
+ label?: string;
12
+ linkTarget?: string;
13
+ linkType?: string;
14
+ _type?: string;
15
+ linkInternal?: any;
16
+ }
17
+ export interface ConditionalLink {
18
+ type?: string;
19
+ internalLink?: string | null;
20
+ externalLink?: string | null;
21
+ }
22
+
23
+ export interface StatItems {
24
+ label?: string;
25
+ mainImage?: MainImage;
26
+ value?: string;
27
+ _key?: string;
28
+ _type?: string;
29
+ }
30
+
31
+ export interface Logo extends ConditionalLink {
32
+ alt?: string;
33
+ linkTarget?: string;
34
+ image?: string;
35
+ }
36
+
37
+ export interface Images {
38
+ image?: string;
39
+ _key?: string;
40
+ _type?: string;
41
+ alt?: string;
42
+ }
43
+
44
+ export interface ContactDetails {
45
+ addressInfo?: string;
46
+ contactInfo?: string;
47
+ emailInfo?: string;
48
+ _key?: string;
49
+ }
50
+
51
+ export interface SocialLink {
52
+ socialMedia?: string | null;
53
+ socialMediaLink?: string | null;
54
+ _key?: string | null;
55
+ _type?: string | null;
56
+ socialMediaIcon?: {
57
+ alt?: string;
58
+ image?: string;
59
+ } | null;
60
+ socialMediaPlatform?: string | null;
61
+ }
62
+
63
+ export interface LabeledRouteWithKey extends LabeledRoute {
64
+ _key?: string;
65
+ }
66
+
67
+ export interface ArrayOfImageTitleAndText {
68
+ mainImage?: {
69
+ alt?: string;
70
+ image?: string;
71
+ };
72
+ plainText?: string;
73
+ title?: string;
74
+ _key?: string;
75
+ _type?: string;
76
+ }
77
+
78
+ export interface FeaturedItem {
79
+ description?: string;
80
+ mainImage?: MainImage;
81
+ title?: string;
82
+ subtitle?: string;
83
+ _key?: string;
84
+ _type?: string;
85
+ }
86
+
87
+ export interface ArrayOfTitleAndText {
88
+ _key?: string;
89
+ plainText?: string;
90
+ title?: string;
91
+ }
92
+
93
+ export interface BlogPost extends SanityBody {
94
+ authors?: Author[] | null;
95
+ body?: any;
96
+ categories?: Category[] | null;
97
+ excerpt?: string | null;
98
+ link?: string | null;
99
+ mainImage?: string | null;
100
+ publishedAt?: string;
101
+ seo?: Seo | null;
102
+ slug?: SanitySlug | null;
103
+ title?: string;
104
+ }
105
+
106
+ export interface Seo {
107
+ _type?: string;
108
+ seoTitle?: string;
109
+ seoDescription?: string;
110
+ seoImage?: string;
111
+ seoKeywords?: string;
112
+ seoSynonyms?: string;
113
+ }
114
+
115
+ export interface SanitySlug {
116
+ current?: string;
117
+ _type?: "slug";
118
+ }
119
+
120
+ export interface SanityBody {
121
+ _createdAt?: string;
122
+ _id?: string;
123
+ _rev?: string;
124
+ _type?: string;
125
+ _updatedAt?: string;
126
+ }
127
+
128
+ export interface Author extends SanityBody {
129
+ link?: string | null;
130
+ bio?: string | null;
131
+ name?: string | null;
132
+ slug?: SanitySlug | null;
133
+ image?: string | null;
134
+ profile?: {
135
+ alt: string;
136
+ image: string;
137
+ } | null;
138
+ }
139
+
140
+ export interface Category extends SanityBody {
141
+ title?: string;
142
+ }
143
+
144
+ export interface Form {
145
+ id?: string | null;
146
+ buttonLabel?: string | null;
147
+ name?: string | null;
148
+ subtitle?: string | null;
149
+ fields?: FormFields[] | null;
150
+ thankYouPage?: ThankYouPage | null;
151
+ }
152
+
153
+ export interface FormFields {
154
+ name?: string;
155
+ placeholder?: string;
156
+ pricingType?: string;
157
+ type?: FormTypes;
158
+ _key?: string;
159
+ _type?: string;
160
+ isRequired?: boolean;
161
+ label?: string;
162
+ items?: string[];
163
+ }
164
+
165
+ export type FormTypes =
166
+ | "inputText"
167
+ | "inputEmail"
168
+ | "inputPassword"
169
+ | "inputNumber"
170
+ | "textarea"
171
+ | "inputFile"
172
+ | "inputRadio"
173
+ | "inputCheckbox"
174
+ | "inputSelect";
175
+
176
+ export interface ThankYouPage {
177
+ externalLink?: string | null;
178
+ internalLink?: string | null;
179
+ linkInternal?: any;
180
+ linkTarget?: string;
181
+ linkType?: string;
182
+ type?: string;
183
+ }
184
+
185
+ //Used on different sections
186
+ export interface SectionsProps {
187
+ template?: Template;
188
+ data?: Sections;
189
+ variant?: string | null | undefined;
190
+ schema?: Variants;
191
+ }
192
+
193
+ export interface Sections extends SanityBody {
194
+ label?: string;
195
+ variant?: string;
196
+ variants?: Variants;
197
+ _key?: string;
198
+ }
199
+
200
+ //*EDIT THIS SECTION WHEN CREATING/UPDATING SCHEMAS ON STUDIO */
201
+ export interface Variants {
202
+ template?: Template;
203
+ multipleMenus?: any;
204
+ arrayOfTitleAndText?: ArrayOfTitleAndText[] | null;
205
+ logo?: Logo | null;
206
+ primaryButton?: LabeledRoute | null;
207
+ secondaryButton?: LabeledRoute | null;
208
+ routes?: LabeledRouteWithKey[] | null;
209
+ menu?: LabeledRouteWithKey[] | null;
210
+ plans?: Plans[] | null;
211
+ formLinks?: LabeledRouteWithKey[] | null;
212
+ portfolios?: Portfolios[] | null;
213
+ portfoliosWithCategories?: PortfoliosWithCategories[] | null;
214
+ length?: number;
215
+ signInLink?: LabeledRoute | null;
216
+ signinLink?: LabeledRoute | null;
217
+ tags?: string[] | null;
218
+ posts?: BlogPost[] | null;
219
+ blogsPerPage?: number | null;
220
+ form?: Form | null;
221
+ collections?: Collection | null;
222
+ products?: CollectionProduct | null;
223
+ allProducts?: Collection[];
224
+ subtitle?: string | null;
225
+ caption?: string | null;
226
+ title?: string | null;
227
+ plainText?: string | null;
228
+ contactDescription?: string | null;
229
+ officeInformation?: string | null;
230
+ contactEmail?: string | null;
231
+ contactNumber?: string | null;
232
+ socialLinks?: SocialLink[] | null;
233
+ block?: any;
234
+ heading?: string | null;
235
+ acceptButtonLabel?: string | null;
236
+ declineButtonLabel?: string | null;
237
+ faqsWithCategories?: FaqsWithCategory[] | null;
238
+ faqs?: AskedQuestion[] | null;
239
+ arrayOfImageTitleAndText?: ArrayOfImageTitleAndText[] | null;
240
+ description?: string | null;
241
+ featuredItems?: FeaturedItem[] | null;
242
+ images?: Images[] | null;
243
+ contactDetails?: ContactDetails[] | null;
244
+ copyright?: string | null;
245
+ mainImage?: MainImage | null;
246
+ youtubeLink?: string | null;
247
+ banner?: any;
248
+ stats?: StatItems[] | null;
249
+ teams?: Team[] | null;
250
+ testimonials?: Testimonial[] | null;
251
+ selectStripeAccount?: string;
252
+ annualBilling?: string;
253
+ monthlyBilling?: string;
254
+ productDetails?: ProductDetail[];
255
+ btnLabel?: string;
256
+ selectAccount?: string;
257
+ hashtags?: string[];
258
+ numberOfPosts?: number;
259
+ text?: string;
260
+ button?: LabeledRoute;
261
+ features?: string[];
262
+ config?: {
263
+ enableAnalytics: boolean;
264
+ cookiePolicy?: {
265
+ siteName: string;
266
+ cookiePolicyPage: Reference;
267
+ };
268
+ consentModalPosition?: string;
269
+ };
270
+ contactLink?: LabeledRoute;
271
+ }
272
+
273
+ export interface Template {
274
+ bg?: string;
275
+ color?: string;
276
+ }
277
+
278
+ export type Plans = {
279
+ _key?: string | null;
280
+ _type?: "planItems" | null;
281
+ checkoutButtonName?: string | null;
282
+ description?: string | null;
283
+ monthlyPrice?: string | null;
284
+ planType?: string | null;
285
+ yearlyPrice?: string | null;
286
+ planIncludes?: string[] | null;
287
+ primaryButton?: LabeledRoute | null;
288
+ } & Record<string, string>;
289
+
290
+ export interface Portfolios {
291
+ dateAdded?: string | null;
292
+ mainImage?: {
293
+ image?: string | null;
294
+ alt?: string | null;
295
+ } | null;
296
+ primaryButton?: LabeledRoute | null;
297
+ title?: string | null;
298
+ _key?: string | null;
299
+ _type?: string | null;
300
+ }
301
+
302
+ export interface PortfoliosWithCategories {
303
+ category?: string | null;
304
+ content?: Content[] | null;
305
+ primaryButton?: LabeledRoute | null;
306
+ _key?: string | null;
307
+ _type?: string | null;
308
+ }
309
+
310
+ export interface Content extends Portfolios {
311
+ description?: string | null;
312
+ subtitle?: string | null;
313
+ }
314
+
315
+ export interface Collection extends SanityBody {
316
+ collectionInfoVariant?: {
317
+ variant?: string;
318
+ } | null;
319
+ name?: string | null;
320
+ products?: CollectionProduct[] | null;
321
+ sections?: any; //todo
322
+ seo?: Seo | null;
323
+ slug?: SanitySlug | null;
324
+ }
325
+
326
+ export interface CollectionProduct extends SanityBody {
327
+ compareToPrice?: number | null;
328
+ description?: string | null;
329
+ ecwidProductId?: number | null;
330
+ name?: string | null;
331
+ price?: number | null;
332
+ productInfo?: ProductInfo | null;
333
+ productInfoVariant?: {
334
+ variant?: string;
335
+ } | null;
336
+ sections?: any; //todo
337
+ seo?: Seo | null;
338
+ slug?: SanitySlug | null;
339
+ }
340
+
341
+ //TODO, RECHECK PRODUCT INFO DATA FROM SANITY
342
+ interface ProductInfo {
343
+ btnLabel?: string | null;
344
+ images?: ProductInfoImage[] | null;
345
+ productDetails?: ProductDetail[] | null;
346
+ socialLinks?: SocialLink[] | null;
347
+ subtitle?: string | null;
348
+ }
349
+
350
+ //TODO, RECHECK PRODUCT INFO DATA FROM SANITY
351
+ export interface ProductDetail {
352
+ blockContent?: any;
353
+ contentType?: string;
354
+ tabName?: string;
355
+ _key?: string;
356
+ [key: string]: any;
357
+ }
358
+ interface ProductInfoImage {
359
+ alt?: string | null;
360
+ _key: string;
361
+ _type: string;
362
+ image?: string | null;
363
+ }
364
+
365
+ export interface FaqsWithCategory {
366
+ askedQuestions?: AskedQuestion[] | null;
367
+ category?: string | null;
368
+ _key?: string;
369
+ _type?: string;
370
+ }
371
+
372
+ export interface AskedQuestion {
373
+ answer?: string | null;
374
+ question?: string | null;
375
+ hidden?: boolean;
376
+ _key?: string;
377
+ _type?: string;
378
+ }
379
+
380
+ export interface Team {
381
+ jobTitle?: string;
382
+ mainImage?: MainImage;
383
+ name?: string;
384
+ plainText?: string;
385
+ _key?: string;
386
+ _type?: string;
387
+ }
388
+
389
+ export interface Testimonial {
390
+ jobTitle?: string;
391
+ mainImage?: MainImage;
392
+ name?: string;
393
+ rating?: string;
394
+ testimony?: string;
395
+ _key?: string;
396
+ _type?: string;
397
+ }
398
+
399
+ export declare interface Reference {
400
+ _type: string;
401
+ _ref: string;
402
+ _key?: string;
403
+ _weak?: boolean;
404
+ _strengthenOnPublish?: {
405
+ type: string;
406
+ weak?: boolean;
407
+ template?: {
408
+ id: string;
409
+ params: Record<string, string | number | boolean>;
410
+ };
411
+ };
412
+ }