@sikka/hawa 0.1.0 → 0.1.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.
@@ -8,7 +8,10 @@ import {
8
8
  HawaSelect,
9
9
  } from "../../elements"
10
10
  import { Controller, FormProvider, useForm } from "react-hook-form"
11
- import { HawaContainer } from "../../layout/HawaContainer"
11
+ import { Card, CardContent, CardFooter } from "../../elements/Card"
12
+ import { Button } from "../../elements/Button"
13
+ import { Icons } from "../../elements/Icons"
14
+ import clsx from "clsx"
12
15
 
13
16
  type SignUpFormTypes = {
14
17
  direction?: "rtl" | "ltr"
@@ -72,259 +75,268 @@ export const SignUpForm: FC<SignUpFormTypes> = (props) => {
72
75
  } = methods
73
76
 
74
77
  return (
75
- <HawaContainer direction={props.direction}>
76
- <div>
77
- {props.showError && (
78
- <HawaAlert
79
- title={props.errorTitle}
80
- text={props.errorText}
81
- severity="error"
82
- />
83
- )}
84
- <FormProvider {...methods}>
85
- <form onSubmit={handleSubmit((e) => props.handleSignUp(e))}>
86
- <div>
87
- {props.signUpFields.map((fld, i) => {
88
- if (fld === "fullname") {
89
- return (
90
- <Controller
91
- key={i}
92
- control={control}
93
- name="fullName"
94
- render={({ field }) => (
95
- <HawaTextField
96
- width="full"
97
- type="text"
98
- label={props.texts.fullNameLabel}
99
- placeholder={props.texts.fullNamePlaceholder}
100
- helpertext={errors.fullName?.message}
101
- onChange={field.onChange}
102
- value={field.value ?? ""}
103
- />
104
- )}
105
- rules={{
106
- required: props.texts.fullNameRequiredText,
107
- }}
108
- />
109
- )
110
- }
111
- if (fld === "email") {
112
- return (
113
- <Controller
114
- control={control}
115
- name="email"
116
- render={({ field }) => (
117
- <HawaTextField
118
- width="full"
119
- type="text"
120
- autoComplete="email"
121
- label={props.texts.emailLabel}
122
- helpertext={errors.email?.message}
123
- placeholder={props.texts.emailPlaceholder}
124
- onChange={field.onChange}
125
- value={field.value ?? ""}
126
- />
127
- )}
128
- rules={{
129
- required: props.texts.emailRequiredText,
130
- pattern: {
131
- value:
132
- /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
133
- message: props.texts.emailInvalidText,
134
- },
135
- }}
136
- />
137
- )
138
- }
139
- if (fld === "username") {
140
- return (
141
- <Controller
142
- control={control}
143
- name="username"
144
- render={({ field }) => (
145
- <HawaTextField
146
- width="full"
147
- type="text"
148
- autoComplete="username"
149
- label={props.texts.usernameLabel}
150
- helpertext={errors.username?.message}
151
- placeholder={props.texts.usernamePlaceholder}
152
- onChange={field.onChange}
153
- value={field.value ?? ""}
154
- />
155
- )}
156
- rules={{
157
- required: props.texts.usernameRequired,
158
- }}
159
- />
160
- )
161
- }
162
- })}
163
- </div>
164
- <Controller
165
- control={control}
166
- name="password"
167
- render={({ field }) => (
168
- <HawaTextField
169
- width="full"
170
- type="password"
171
- autoComplete="new-password"
172
- // defaultValue={field.value ?? ""}
173
- label={props.texts.passwordLabel}
174
- placeholder={props.texts.passwordPlaceholder}
175
- helpertext={errors.password?.message}
176
- onChange={field.onChange}
177
- value={field.value ?? ""}
178
- />
179
- )}
180
- rules={{ required: props.texts.passwordRequiredText }}
181
- />
182
- <Controller
183
- control={control}
184
- name="confirm_password"
185
- render={({ field }) => (
186
- <HawaTextField
187
- width="full"
188
- type="password"
189
- autoComplete="new-password"
190
- // defaultValue={field.value ?? ""}
191
- label={props.texts.confirmPasswordLabel}
192
- placeholder={props.texts.confirmPasswordPlaceholder}
193
- helpertext={errors.confirm_password?.message}
194
- onChange={field.onChange}
195
- value={field.value ?? ""}
196
- />
197
- )}
198
- rules={{ required: props.texts.passwordRequiredText }}
78
+ <Card dir={props.direction}>
79
+ <CardContent headless>
80
+ <div>
81
+ {props.showError && (
82
+ <HawaAlert
83
+ title={props.errorTitle}
84
+ text={props.errorText}
85
+ severity="error"
199
86
  />
200
- {props.showRefCode && (
87
+ )}
88
+ <FormProvider {...methods}>
89
+ <form onSubmit={handleSubmit((e) => props.handleSignUp(e))}>
90
+ <div>
91
+ {props.signUpFields.map((fld, i) => {
92
+ if (fld === "fullname") {
93
+ return (
94
+ <Controller
95
+ key={i}
96
+ control={control}
97
+ name="fullName"
98
+ render={({ field }) => (
99
+ <HawaTextField
100
+ width="full"
101
+ type="text"
102
+ label={props.texts.fullNameLabel}
103
+ placeholder={props.texts.fullNamePlaceholder}
104
+ helpertext={errors.fullName?.message}
105
+ onChange={field.onChange}
106
+ value={field.value ?? ""}
107
+ />
108
+ )}
109
+ rules={{
110
+ required: props.texts.fullNameRequiredText,
111
+ }}
112
+ />
113
+ )
114
+ }
115
+ if (fld === "email") {
116
+ return (
117
+ <Controller
118
+ control={control}
119
+ name="email"
120
+ render={({ field }) => (
121
+ <HawaTextField
122
+ width="full"
123
+ type="text"
124
+ autoComplete="email"
125
+ label={props.texts.emailLabel}
126
+ helpertext={errors.email?.message}
127
+ placeholder={props.texts.emailPlaceholder}
128
+ onChange={field.onChange}
129
+ value={field.value ?? ""}
130
+ />
131
+ )}
132
+ rules={{
133
+ required: props.texts.emailRequiredText,
134
+ pattern: {
135
+ value:
136
+ /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
137
+ message: props.texts.emailInvalidText,
138
+ },
139
+ }}
140
+ />
141
+ )
142
+ }
143
+ if (fld === "username") {
144
+ return (
145
+ <Controller
146
+ control={control}
147
+ name="username"
148
+ render={({ field }) => (
149
+ <HawaTextField
150
+ width="full"
151
+ type="text"
152
+ autoComplete="username"
153
+ label={props.texts.usernameLabel}
154
+ helpertext={errors.username?.message}
155
+ placeholder={props.texts.usernamePlaceholder}
156
+ onChange={field.onChange}
157
+ value={field.value ?? ""}
158
+ />
159
+ )}
160
+ rules={{
161
+ required: props.texts.usernameRequired,
162
+ }}
163
+ />
164
+ )
165
+ }
166
+ })}
167
+ </div>
201
168
  <Controller
202
169
  control={control}
203
- name="refCode"
170
+ name="password"
204
171
  render={({ field }) => (
205
172
  <HawaTextField
206
173
  width="full"
207
- type="text"
208
- defaultValue={field.value ?? ""}
209
- label={props.texts.refCode}
174
+ type="password"
175
+ autoComplete="new-password"
176
+ // defaultValue={field.value ?? ""}
177
+ label={props.texts.passwordLabel}
210
178
  placeholder={props.texts.passwordPlaceholder}
211
179
  helpertext={errors.password?.message}
180
+ onChange={field.onChange}
212
181
  value={field.value ?? ""}
182
+ />
183
+ )}
184
+ rules={{ required: props.texts.passwordRequiredText }}
185
+ />
186
+ <Controller
187
+ control={control}
188
+ name="confirm_password"
189
+ render={({ field }) => (
190
+ <HawaTextField
191
+ width="full"
192
+ type="password"
193
+ autoComplete="new-password"
194
+ // defaultValue={field.value ?? ""}
195
+ label={props.texts.confirmPasswordLabel}
196
+ placeholder={props.texts.confirmPasswordPlaceholder}
197
+ helpertext={errors.confirm_password?.message}
213
198
  onChange={field.onChange}
199
+ value={field.value ?? ""}
214
200
  />
215
201
  )}
202
+ rules={{ required: props.texts.passwordRequiredText }}
216
203
  />
217
- )}
218
- {props.showUserSource && (
219
- <div>
204
+ {props.showRefCode && (
220
205
  <Controller
221
206
  control={control}
222
- name="reference"
207
+ name="refCode"
223
208
  render={({ field }) => (
224
- <HawaSelect
225
- label="How did you learn about us?"
226
- isCreatable={false}
227
- isMulti={false ?? false}
228
- isSearchable={false}
229
- isClearable={false ?? false}
230
- options={[
231
- { value: "friend", label: "From a friend" },
232
- { value: "ad", label: "Advertisement" },
233
- { value: "other", label: "Other" },
234
- ]}
235
- onChange={(e: any) => {
236
- field.onChange(e.value)
237
- }}
209
+ <HawaTextField
210
+ width="full"
211
+ type="text"
212
+ defaultValue={field.value ?? ""}
213
+ label={props.texts.refCode}
214
+ placeholder={props.texts.passwordPlaceholder}
215
+ helpertext={errors.password?.message}
216
+ value={field.value ?? ""}
217
+ onChange={field.onChange}
238
218
  />
239
219
  )}
240
220
  />
241
- </div>
242
- )}
243
- {props.showTermsOption && (
244
- <Controller
245
- control={control}
246
- name="terms_accepted"
247
- render={({ field }) => (
248
- <HawaCheckbox
249
- id="terms_accepted"
250
- helperText={errors.terms_accepted?.message}
251
- onChange={(e) => field.onChange(e)}
252
- label={
253
- <span>
254
- {props.texts.iAcceptText}{" "}
255
- <a
256
- onClick={props.handleRouteToTOS}
257
- className="cursor-pointer text-blue-800"
258
- >
259
- {props.texts.termsText}
260
- </a>
261
- </span>
262
- }
263
- />
264
- )}
265
- rules={{ required: props.texts.termsRequiredText }}
266
- />
267
- )}
268
- {props.showNewsletterOption && (
269
- <Controller
270
- control={control}
271
- name="newsletter_accepted"
272
- render={({ field }) => (
273
- <HawaCheckbox
274
- id="newsletter_accepted"
275
- label={props.texts.subscribeToNewsletter}
276
- onChange={field.onChange}
221
+ )}
222
+ {props.showUserSource && (
223
+ <div>
224
+ <Controller
225
+ control={control}
226
+ name="reference"
227
+ render={({ field }) => (
228
+ <HawaSelect
229
+ label="How did you learn about us?"
230
+ isCreatable={false}
231
+ isMulti={false ?? false}
232
+ isSearchable={false}
233
+ isClearable={false ?? false}
234
+ options={[
235
+ { value: "friend", label: "From a friend" },
236
+ { value: "ad", label: "Advertisement" },
237
+ { value: "other", label: "Other" },
238
+ ]}
239
+ onChange={(e: any) => {
240
+ field.onChange(e.value)
241
+ }}
242
+ />
243
+ )}
277
244
  />
278
- )}
279
- />
280
- )}
281
- <HawaButton
282
- isLoading={props.isLoading}
283
- color="primary"
284
- width="full"
245
+ </div>
246
+ )}
247
+ {props.showTermsOption && (
248
+ <Controller
249
+ control={control}
250
+ name="terms_accepted"
251
+ render={({ field }) => (
252
+ <HawaCheckbox
253
+ id="terms_accepted"
254
+ helperText={errors.terms_accepted?.message}
255
+ onChange={(e) => field.onChange(e)}
256
+ label={
257
+ <span>
258
+ {props.texts.iAcceptText}{" "}
259
+ <a
260
+ onClick={props.handleRouteToTOS}
261
+ className="cursor-pointer text-blue-800"
262
+ >
263
+ {props.texts.termsText}
264
+ </a>
265
+ </span>
266
+ }
267
+ />
268
+ )}
269
+ rules={{ required: props.texts.termsRequiredText }}
270
+ />
271
+ )}
272
+ {props.showNewsletterOption && (
273
+ <Controller
274
+ control={control}
275
+ name="newsletter_accepted"
276
+ render={({ field }) => (
277
+ <HawaCheckbox
278
+ id="newsletter_accepted"
279
+ label={props.texts.subscribeToNewsletter}
280
+ onChange={field.onChange}
281
+ />
282
+ )}
283
+ />
284
+ )}
285
+
286
+ <Button className="w-full" isLoading={props.isLoading}>
287
+ {props.texts.signUpText}
288
+ </Button>
289
+ </form>
290
+ </FormProvider>
291
+ <div className="flex flex-row items-center justify-center gap-1 p-3 text-center text-sm font-semibold dark:text-white">
292
+ <span>{props.texts.existingUserText}</span>
293
+ <span
294
+ onClick={props.handleRouteToSignIn}
295
+ className="cursor-pointer text-blue-600"
285
296
  >
286
- {props.texts.signUpText}
287
- </HawaButton>
288
- </form>
289
- </FormProvider>
290
- <div className="flex flex-row items-center justify-center gap-1 p-3 text-center text-sm font-semibold dark:text-white">
291
- <span>{props.texts.existingUserText}</span>
292
- <span
293
- onClick={props.handleRouteToSignIn}
294
- className="cursor-pointer text-blue-600"
295
- >
296
- {props.texts.signInText}
297
- </span>
297
+ {props.texts.signInText}
298
+ </span>
299
+ </div>
298
300
  </div>
299
- </div>
301
+ </CardContent>
302
+
300
303
  {props.viaGithub || props.viaGoogle || props.viaTwitter ? (
301
- <div style={{ display: "flex", flexDirection: "column" }}>
304
+ <CardFooter className="grid grid-cols-1 gap-2 ">
302
305
  {props.viaGoogle && (
303
- <HawaLogoButton
304
- logo="google"
305
- direction={props.direction}
306
- buttonText={props.texts.signUpViaGoogleLabel}
307
- onClick={props.handleGoogleSignUp}
308
- />
306
+ <Button variant="outline" onClick={props.handleGoogleSignUp}>
307
+ <Icons.google
308
+ className={clsx(
309
+ "h-4 w-4",
310
+ props.direction === "rtl" ? "ml-2" : "mr-2"
311
+ )}
312
+ />
313
+ {props.texts.signUpViaGoogleLabel}
314
+ </Button>
309
315
  )}
310
316
  {props.viaGithub && (
311
- <HawaLogoButton
312
- logo="github"
313
- direction={props.direction}
314
- buttonText={props.texts.signUpViaGithubLabel}
315
- onClick={props.handleGithubSignUp}
316
- />
317
+ <Button variant="outline" onClick={props.handleGithubSignUp}>
318
+ <Icons.gitHub
319
+ className={clsx(
320
+ "h-4 w-4",
321
+ props.direction === "rtl" ? "ml-2" : "mr-2"
322
+ )}
323
+ />
324
+ {props.texts.signUpViaGithubLabel}
325
+ </Button>
317
326
  )}
318
327
  {props.viaTwitter && (
319
- <HawaLogoButton
320
- logo="twitter"
321
- direction={props.direction}
322
- buttonText={props.texts.signUpViaTwitterLabel}
323
- onClick={props.handleTwitterSignUp}
324
- />
328
+ <Button variant="outline" onClick={props.handleTwitterSignUp}>
329
+ <Icons.twitter
330
+ className={clsx(
331
+ "h-4 w-4",
332
+ props.direction === "rtl" ? "ml-2" : "mr-2"
333
+ )}
334
+ />
335
+ {props.texts.signUpViaTwitterLabel}
336
+ </Button>
325
337
  )}
326
- </div>
338
+ </CardFooter>
327
339
  ) : null}
328
- </HawaContainer>
340
+ </Card>
329
341
  )
330
342
  }
@@ -3,6 +3,7 @@ import * as React from "react"
3
3
  import { cva, type VariantProps } from "class-variance-authority"
4
4
 
5
5
  import { cn } from "../util"
6
+ import { HawaLoading } from "./HawaLoading"
6
7
 
7
8
  const buttonVariants = cva(
8
9
  "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
@@ -37,10 +38,22 @@ export interface ButtonProps
37
38
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
38
39
  VariantProps<typeof buttonVariants> {
39
40
  asChild?: boolean
41
+ isLoading?: boolean
40
42
  }
41
43
 
42
44
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
43
- ({ className, variant, size, asChild = false, ...props }, ref) => {
45
+ (
46
+ {
47
+ className,
48
+ variant,
49
+ size,
50
+ asChild = false,
51
+ isLoading,
52
+ children,
53
+ ...props
54
+ },
55
+ ref
56
+ ) => {
44
57
  // const Comp = asChild ? Slot : "button"
45
58
  const Comp = "button"
46
59
  return (
@@ -48,7 +61,14 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
48
61
  className={cn(buttonVariants({ variant, size, className }))}
49
62
  ref={ref}
50
63
  {...props}
51
- />
64
+ >
65
+ {isLoading ? (
66
+ // Replace with your loading icon component
67
+ <HawaLoading design="dots-pulse" size="button" />
68
+ ) : (
69
+ children
70
+ )}
71
+ </Comp>
52
72
  )
53
73
  }
54
74
  )
@@ -55,11 +55,20 @@ const CardDescription = React.forwardRef<
55
55
  ))
56
56
  CardDescription.displayName = "CardDescription"
57
57
 
58
+ type CardContentProps = {
59
+ headless?: boolean
60
+ } & React.HTMLAttributes<HTMLDivElement>
61
+
58
62
  const CardContent = React.forwardRef<
59
63
  HTMLDivElement,
60
- React.HTMLAttributes<HTMLDivElement>
64
+ CardContentProps
65
+ // React.HTMLAttributes<HTMLDivElement>
61
66
  >(({ className, ...props }, ref) => (
62
- <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
67
+ <div
68
+ ref={ref}
69
+ className={cn("p-6", props.headless ? "pt-6" : "pt-0", className)}
70
+ {...props}
71
+ />
63
72
  ))
64
73
  CardContent.displayName = "CardContent"
65
74
 
@@ -28,13 +28,13 @@ export const HawaLoading: FC<LoadingTypes> = ({
28
28
  switch (design.split("-")[0]) {
29
29
  case "dots":
30
30
  return (
31
- <div className="flex flex-row space-x-2 ">
31
+ <div className="flex flex-row gap-2">
32
32
  <div
33
33
  className={clsx(
34
34
  "animate-bounce rounded-full delay-100 repeat-infinite",
35
35
  size === "button" ? "h-2 w-2" : sizeStyles[size],
36
36
  animationStyles[design.split("-")[1]],
37
- color ? color : "bg-buttonPrimary-500"
37
+ color ? color : "bg-primary-foreground"
38
38
  )}
39
39
  ></div>
40
40
  <div
@@ -43,7 +43,7 @@ export const HawaLoading: FC<LoadingTypes> = ({
43
43
  size === "button" ? "h-2 w-2" : sizeStyles[size],
44
44
  animationStyles[design.split("-")[1]],
45
45
 
46
- color ? color : "bg-buttonPrimary-500"
46
+ color ? color : "bg-primary-foreground"
47
47
  )}
48
48
  ></div>
49
49
  <div
@@ -52,7 +52,7 @@ export const HawaLoading: FC<LoadingTypes> = ({
52
52
  size === "button" ? "h-2 w-2" : sizeStyles[size],
53
53
  animationStyles[design.split("-")[1]],
54
54
 
55
- color ? color : "bg-buttonPrimary-500"
55
+ color ? color : "bg-primary-foreground"
56
56
  )}
57
57
  ></div>
58
58
  </div>
@@ -1,5 +1,6 @@
1
1
  import React, { FC, PropsWithRef } from "react"
2
2
  import clsx from "clsx"
3
+ import { Label } from "./Label"
3
4
 
4
5
  // TODO: make icon based on direction
5
6
  // TODO: Preferebly use context to pass direction rtl | ltr
@@ -50,7 +51,7 @@ export const HawaTextField: FC<TextFieldTypes> = ({
50
51
  full: "w-full",
51
52
  }
52
53
 
53
- let defaultStyle = "flex max-h-fit flex-col justify-center"
54
+ let defaultStyle = "flex max-h-fit flex-col justify-center gap-2"
54
55
  let defaultInputStyle =
55
56
  "block w-full rounded border bg-background p-2 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500"
56
57
  let previewInputStyle =
@@ -62,12 +63,13 @@ export const HawaTextField: FC<TextFieldTypes> = ({
62
63
  className={clsx(defaultStyle, marginStyles[margin], widthStyles[width])}
63
64
  >
64
65
  {props.label && (
65
- <label
66
- // htmlFor="first_name"
67
- className="mb-2 block text-sm font-medium "
68
- >
69
- {props.label}
70
- </label>
66
+ // <label
67
+ // // htmlFor="first_name"
68
+ // className="mb-2 block text-sm font-medium "
69
+ // >
70
+ // {props.label}
71
+ // </label>
72
+ <Label>{props.label}</Label>
71
73
  )}
72
74
  {preview ? (
73
75
  <>
@@ -30,7 +30,10 @@ export const Icons = {
30
30
  ),
31
31
  twitter: (props: IconProps) => (
32
32
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
33
- <path d="M21.543 7.104c.015.211.015.423.015.636 0 6.507-4.954 14.01-14.01 14.01v-.003A13.94 13.94 0 0 1 0 19.539a9.88 9.88 0 0 0 7.287-2.041 4.93 4.93 0 0 1-4.6-3.42 4.916 4.916 0 0 0 2.223-.084A4.926 4.926 0 0 1 .96 9.167v-.062a4.887 4.887 0 0 0 2.235.616A4.928 4.928 0 0 1 1.67 3.148a13.98 13.98 0 0 0 10.15 5.144 4.929 4.929 0 0 1 8.39-4.49 9.868 9.868 0 0 0 3.128-1.196 4.941 4.941 0 0 1-2.165 2.724A9.828 9.828 0 0 0 24 4.555a10.019 10.019 0 0 1-2.457 2.549z" />
33
+ <path
34
+ fill="currentColor"
35
+ d="M21.543 7.104c.015.211.015.423.015.636 0 6.507-4.954 14.01-14.01 14.01v-.003A13.94 13.94 0 0 1 0 19.539a9.88 9.88 0 0 0 7.287-2.041 4.93 4.93 0 0 1-4.6-3.42 4.916 4.916 0 0 0 2.223-.084A4.926 4.926 0 0 1 .96 9.167v-.062a4.887 4.887 0 0 0 2.235.616A4.928 4.928 0 0 1 1.67 3.148a13.98 13.98 0 0 0 10.15 5.144 4.929 4.929 0 0 1 8.39-4.49 9.868 9.868 0 0 0 3.128-1.196 4.941 4.941 0 0 1-2.165 2.724A9.828 9.828 0 0 0 24 4.555a10.019 10.019 0 0 1-2.457 2.549z"
36
+ />
34
37
  </svg>
35
38
  ),
36
39
  gitHub: (props: IconProps) => (