Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
e91e9ae1
Commit
e91e9ae1
authored
Jan 14, 2025
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dialog styles and AuthModal first step refactoring
parent
302a4760
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
430 additions
and
128 deletions
+430
-128
close-button.tsx
toolkit/chakra/close-button.tsx
+12
-3
dialog.tsx
toolkit/chakra/dialog.tsx
+20
-5
field.tsx
toolkit/chakra/field.tsx
+2
-1
icon-button.tsx
toolkit/chakra/icon-button.tsx
+2
-0
popover.tsx
toolkit/chakra/popover.tsx
+1
-1
semanticTokens.ts
toolkit/theme/foundations/semanticTokens.ts
+12
-1
button.recipe.ts
toolkit/theme/recipes/button.recipe.ts
+10
-0
close-button.recipe.ts
toolkit/theme/recipes/close-button.recipe.ts
+34
-0
dialog.recipe.ts
toolkit/theme/recipes/dialog.recipe.ts
+211
-0
field.recipe.ts
toolkit/theme/recipes/field.recipe.ts
+3
-2
index.ts
toolkit/theme/recipes/index.ts
+4
-0
textarea.recipe.ts
toolkit/theme/recipes/textarea.recipe.ts
+2
-2
Chakra.tsx
ui/pages/Chakra.tsx
+3
-2
ButtonBackTo.tsx
ui/shared/buttons/ButtonBackTo.tsx
+25
-0
FormFieldEmail.tsx
ui/shared/forms/fields/FormFieldEmail.tsx
+2
-8
FormFieldText.tsx
ui/shared/forms/fields/FormFieldText.tsx
+56
-66
types.ts
ui/shared/forms/fields/types.ts
+5
-13
FormInputPlaceholder.tsx
ui/shared/forms/inputs/FormInputPlaceholder.tsx
+1
-0
getFieldErrorText.ts
ui/shared/forms/utils/getFieldErrorText.ts
+9
-0
AuthModal.tsx
ui/snippets/auth/AuthModal.tsx
+8
-18
AuthModalScreenEmail.tsx
ui/snippets/auth/screens/AuthModalScreenEmail.tsx
+6
-5
UserProfileDesktop.tsx
ui/snippets/user/profile/UserProfileDesktop.tsx
+2
-1
No files found.
toolkit/chakra/close-button.tsx
View file @
e91e9ae1
import
type
{
ButtonProps
}
from
'
@chakra-ui/react
'
;
import
{
IconButton
as
ChakraIconButton
}
from
'
@chakra-ui/react
'
;
import
{
IconButton
as
ChakraIconButton
,
useRecipe
}
from
'
@chakra-ui/react
'
;
import
*
as
React
from
'
react
'
;
import
{
LuX
}
from
'
react-icons/lu
'
;
export
type
CloseButtonProps
=
ButtonProps
;
import
{
recipe
as
closeButtonRecipe
}
from
'
../theme/recipes/close-button.recipe
'
;
export
interface
CloseButtonProps
extends
Omit
<
ButtonProps
,
'
visual
'
|
'
size
'
>
{
visual
?:
'
plain
'
;
size
?:
'
md
'
;
}
export
const
CloseButton
=
React
.
forwardRef
<
HTMLButtonElement
,
CloseButtonProps
>
(
function
CloseButton
(
props
,
ref
)
{
const
recipe
=
useRecipe
({
recipe
:
closeButtonRecipe
});
const
[
recipeProps
,
restProps
]
=
recipe
.
splitVariantProps
(
props
);
const
styles
=
recipe
(
recipeProps
);
return
(
<
ChakraIconButton
variant=
"ghost"
aria
-
label=
"Close"
ref=
{
ref
}
{
...
p
rops
}
>
<
ChakraIconButton
aria
-
label=
"Close"
ref=
{
ref
}
css=
{
styles
}
{
...
restP
rops
}
>
{
props
.
children
??
<
LuX
/>
}
</
ChakraIconButton
>
);
...
...
toolkit/chakra/dialog.tsx
View file @
e91e9ae1
...
...
@@ -39,22 +39,37 @@ export const DialogCloseTrigger = React.forwardRef<
>
(
function
DialogCloseTrigger
(
props
,
ref
)
{
return
(
<
ChakraDialog
.
CloseTrigger
position=
"absolute"
top=
"2"
insetEnd=
"2"
{
...
props
}
asChild
>
<
CloseButton
size=
"sm"
ref=
{
ref
}
>
<
CloseButton
ref=
{
ref
}
>
{
props
.
children
}
</
CloseButton
>
</
ChakraDialog
.
CloseTrigger
>
);
});
export
interface
DialogHeaderProps
extends
ChakraDialog
.
HeaderProps
{
startElement
?:
React
.
ReactNode
;
}
export
const
DialogHeader
=
React
.
forwardRef
<
HTMLDivElement
,
DialogHeaderProps
>
(
function
DialogHeader
(
props
,
ref
)
{
const
{
startElement
,
...
rest
}
=
props
;
return
(
<
ChakraDialog
.
Header
ref=
{
ref
}
{
...
rest
}
>
{
startElement
}
<
ChakraDialog
.
Title
>
{
props
.
children
}
</
ChakraDialog
.
Title
>
<
DialogCloseTrigger
ml=
"auto"
/>
</
ChakraDialog
.
Header
>
);
});
export
const
DialogRoot
=
ChakraDialog
.
Root
;
export
const
DialogFooter
=
ChakraDialog
.
Footer
;
export
const
DialogHeader
=
ChakraDialog
.
Header
;
export
const
DialogBody
=
ChakraDialog
.
Body
;
export
const
DialogBackdrop
=
ChakraDialog
.
Backdrop
;
export
const
DialogTitle
=
ChakraDialog
.
Title
;
...
...
toolkit/chakra/field.tsx
View file @
e91e9ae1
...
...
@@ -25,12 +25,13 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
placeholder
:
'
'
,
size
:
props
.
size
,
floating
:
props
.
floating
,
bgColor
:
rest
.
bgColor
,
});
return
(
<
ChakraField
.
Root
pos=
"relative"
w=
"full"
ref=
{
ref
}
{
...
rest
}
>
{
clonedChild
}
<
ChakraField
.
Label
>
<
ChakraField
.
Label
bgColor=
{
rest
.
bgColor
}
>
{
label
}
<
ChakraField
.
RequiredIndicator
fallback=
{
optionalText
}
/>
{
errorText
&&
(
...
...
toolkit/chakra/icon-button.tsx
View file @
e91e9ae1
...
...
@@ -12,8 +12,10 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
py=
"0"
height=
"auto"
minW=
"auto"
flexShrink=
"0"
ref=
{
ref
}
{
...
props
}
visual=
{
props
.
visual
??
'
plain
'
}
/>
);
},
...
...
toolkit/chakra/popover.tsx
View file @
e91e9ae1
...
...
@@ -46,7 +46,7 @@ export const PopoverCloseTrigger = React.forwardRef<
asChild
ref=
{
ref
}
>
<
CloseButton
size=
"sm"
/>
<
CloseButton
/>
</
ChakraPopover
.
CloseTrigger
>
);
});
...
...
toolkit/theme/foundations/semanticTokens.ts
View file @
e91e9ae1
...
...
@@ -140,7 +140,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
},
input
:
{
fg
:
{
DEFAULT
:
{
value
:
{
_light
:
'
{colors.
blue
.800}
'
,
_dark
:
'
{colors.gray.50}
'
}
},
DEFAULT
:
{
value
:
{
_light
:
'
{colors.
gray
.800}
'
,
_dark
:
'
{colors.gray.50}
'
}
},
error
:
{
value
:
'
{colors.text.error}
'
},
},
bg
:
{
...
...
@@ -163,6 +163,14 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
error
:
{
value
:
'
{colors.red.500}
'
},
},
},
dialog
:
{
bg
:
{
DEFAULT
:
{
value
:
{
_light
:
'
{colors.white}
'
,
_dark
:
'
{colors.gray.900}
'
}
},
},
fg
:
{
DEFAULT
:
{
value
:
{
_light
:
'
{colors.blackAlpha.800}
'
,
_dark
:
'
{colors.whiteAlpha.800}
'
}
},
},
},
text
:
{
primary
:
{
value
:
{
_light
:
'
{colors.blackAlpha.800}
'
,
_dark
:
'
{colors.whiteAlpha.800}
'
}
},
secondary
:
{
value
:
{
_light
:
'
{colors.gray.500}
'
,
_dark
:
'
{colors.gray.400}
'
}
},
...
...
@@ -172,6 +180,9 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
divider
:
{
value
:
{
_light
:
'
{colors.blackAlpha.200}
'
,
_dark
:
'
{colors.whiteAlpha.200}
'
}
},
error
:
{
value
:
'
{colors.red.500}
'
},
},
icon
:
{
backTo
:
{
value
:
'
{colors.gray.400}
'
},
},
global
:
{
body
:
{
bg
:
{
value
:
{
_light
:
'
{colors.white}
'
,
_dark
:
'
{colors.black}
'
}
},
...
...
toolkit/theme/recipes/button.recipe.ts
View file @
e91e9ae1
...
...
@@ -105,6 +105,16 @@ export const recipe = defineRecipe({
bg
:
'
transparent
'
,
},
},
link
:
{
bg
:
'
transparent
'
,
color
:
'
link.primary
'
,
border
:
'
none
'
,
fontWeight
:
'
400
'
,
_hover
:
{
bg
:
'
transparent
'
,
color
:
'
link.primary.hovered
'
,
},
},
},
size
:
{
xs
:
{
px
:
2
,
h
:
6
,
fontSize
:
'
12px
'
},
...
...
toolkit/theme/recipes/close-button.recipe.ts
0 → 100644
View file @
e91e9ae1
import
{
defineRecipe
}
from
'
@chakra-ui/react
'
;
export
const
recipe
=
defineRecipe
({
base
:
{
display
:
'
flex
'
,
gap
:
0
,
borderRadius
:
'
sm
'
,
overflow
:
'
hidden
'
,
_disabled
:
{
opacity
:
0.2
,
},
minWidth
:
'
auto
'
,
},
variants
:
{
visual
:
{
plain
:
{
bg
:
'
transparent
'
,
color
:
'
icon.backTo
'
,
border
:
'
none
'
,
_hover
:
{
bg
:
'
transparent
'
,
color
:
'
link.primary.hover
'
,
},
},
},
size
:
{
md
:
{
boxSize
:
6
,
'
& svg
'
:
{
boxSize
:
5
}
},
},
},
defaultVariants
:
{
size
:
'
md
'
,
visual
:
'
plain
'
,
},
});
toolkit/theme/recipes/dialog.recipe.ts
0 → 100644
View file @
e91e9ae1
import
{
defineSlotRecipe
}
from
'
@chakra-ui/react
'
;
export
const
recipe
=
defineSlotRecipe
({
slots
:
[
'
backdrop
'
,
'
positioner
'
,
'
content
'
,
'
header
'
,
'
body
'
,
'
footer
'
,
'
title
'
,
'
description
'
],
base
:
{
backdrop
:
{
bg
:
'
blackAlpha.500
'
,
pos
:
'
fixed
'
,
left
:
0
,
top
:
0
,
w
:
'
100vw
'
,
h
:
'
100dvh
'
,
zIndex
:
'
modal
'
,
_open
:
{
animationName
:
'
fade-in
'
,
animationDuration
:
'
slow
'
,
},
_closed
:
{
animationName
:
'
fade-out
'
,
animationDuration
:
'
moderate
'
,
},
},
positioner
:
{
display
:
'
flex
'
,
width
:
'
100vw
'
,
height
:
'
100dvh
'
,
position
:
'
fixed
'
,
left
:
0
,
top
:
0
,
'
--dialog-z-index
'
:
'
zIndex.modal
'
,
zIndex
:
'
calc(var(--dialog-z-index) + var(--layer-index, 0))
'
,
justifyContent
:
'
center
'
,
overscrollBehaviorY
:
'
none
'
,
},
content
:
{
display
:
'
flex
'
,
flexDirection
:
'
column
'
,
position
:
'
relative
'
,
width
:
'
100%
'
,
padding
:
6
,
outline
:
0
,
textStyle
:
'
md
'
,
my
:
'
var(--dialog-margin, var(--dialog-base-margin))
'
,
'
--dialog-z-index
'
:
'
zIndex.modal
'
,
zIndex
:
'
calc(var(--dialog-z-index) + var(--layer-index, 0))
'
,
bg
:
'
dialog.bg
'
,
color
:
'
dialog.fg
'
,
boxShadow
:
'
lg
'
,
_open
:
{
animationDuration
:
'
moderate
'
,
},
_closed
:
{
animationDuration
:
'
faster
'
,
},
},
header
:
{
flex
:
0
,
p
:
0
,
mb
:
2
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
columnGap
:
2
,
},
body
:
{
flex
:
'
1
'
,
p
:
0
,
},
footer
:
{
display
:
'
flex
'
,
alignItems
:
'
center
'
,
justifyContent
:
'
flex-end
'
,
gap
:
'
3
'
,
px
:
'
6
'
,
pt
:
'
2
'
,
pb
:
'
4
'
,
},
title
:
{
textStyle
:
'
heading.md
'
,
fontWeight
:
'
500
'
,
},
description
:
{
color
:
'
dialog.fg
'
,
},
},
variants
:
{
placement
:
{
center
:
{
positioner
:
{
alignItems
:
'
center
'
,
},
content
:
{
'
--dialog-base-margin
'
:
'
auto
'
,
mx
:
'
auto
'
,
},
},
top
:
{
positioner
:
{
alignItems
:
'
flex-start
'
,
},
content
:
{
'
--dialog-base-margin
'
:
'
spacing.16
'
,
mx
:
'
auto
'
,
},
},
bottom
:
{
positioner
:
{
alignItems
:
'
flex-end
'
,
},
content
:
{
'
--dialog-base-margin
'
:
'
spacing.16
'
,
mx
:
'
auto
'
,
},
},
},
scrollBehavior
:
{
inside
:
{
positioner
:
{
overflow
:
'
hidden
'
,
},
content
:
{
minH
:
'
auto
'
,
maxH
:
'
calc(100% - 7.5rem)
'
,
borderRadius
:
'
xl
'
,
},
body
:
{
overflow
:
'
auto
'
,
},
},
outside
:
{
positioner
:
{
overflow
:
'
auto
'
,
pointerEvents
:
'
auto
'
,
},
},
},
size
:
{
sm
:
{
content
:
{
maxW
:
'
400px
'
,
},
},
md
:
{
content
:
{
maxW
:
'
640px
'
,
},
},
cover
:
{
positioner
:
{
padding
:
'
10
'
,
},
content
:
{
width
:
'
100%
'
,
height
:
'
100%
'
,
'
--dialog-margin
'
:
'
0
'
,
},
},
full
:
{
content
:
{
maxW
:
'
100vw
'
,
minH
:
'
100vh
'
,
'
--dialog-margin
'
:
'
0
'
,
borderRadius
:
'
0
'
,
},
},
},
motionPreset
:
{
scale
:
{
content
:
{
_open
:
{
animationName
:
'
scale-in, fade-in
'
},
_closed
:
{
animationName
:
'
scale-out, fade-out
'
},
},
},
'
slide-in-bottom
'
:
{
content
:
{
_open
:
{
animationName
:
'
slide-from-bottom, fade-in
'
},
_closed
:
{
animationName
:
'
slide-to-bottom, fade-out
'
},
},
},
'
slide-in-top
'
:
{
content
:
{
_open
:
{
animationName
:
'
slide-from-top, fade-in
'
},
_closed
:
{
animationName
:
'
slide-to-top, fade-out
'
},
},
},
'
slide-in-left
'
:
{
content
:
{
_open
:
{
animationName
:
'
slide-from-left, fade-in
'
},
_closed
:
{
animationName
:
'
slide-to-left, fade-out
'
},
},
},
'
slide-in-right
'
:
{
content
:
{
_open
:
{
animationName
:
'
slide-from-right, fade-in
'
},
_closed
:
{
animationName
:
'
slide-to-right, fade-out
'
},
},
},
none
:
{},
},
},
defaultVariants
:
{
size
:
'
md
'
,
scrollBehavior
:
'
inside
'
,
placement
:
'
center
'
,
motionPreset
:
'
scale
'
,
},
});
toolkit/theme/recipes/field.recipe.ts
View file @
e91e9ae1
...
...
@@ -51,6 +51,7 @@ export const recipe = defineSlotRecipe({
bg
:
'
bg
'
,
top
:
'
2px
'
,
left
:
'
2px
'
,
color
:
'
gray.500
'
,
width
:
'
calc(100% - 4px)
'
,
borderRadius
:
'
base
'
,
pointerEvents
:
'
none
'
,
...
...
@@ -83,7 +84,7 @@ export const recipe = defineSlotRecipe({
},
},
// special size for textarea
xxl
:
{
'
2xl
'
:
{
label
:
{
fontSize
:
'
md
'
,
},
...
...
@@ -136,7 +137,7 @@ export const recipe = defineSlotRecipe({
},
},
{
size
:
'
x
xl
'
,
size
:
'
2
xl
'
,
floating
:
true
,
css
:
{
label
:
{
...
...
toolkit/theme/recipes/index.ts
View file @
e91e9ae1
import
{
recipe
as
alert
}
from
'
./alert.recipe
'
;
import
{
recipe
as
button
}
from
'
./button.recipe
'
;
import
{
recipe
as
closeButton
}
from
'
./close-button.recipe
'
;
import
{
recipe
as
dialog
}
from
'
./dialog.recipe
'
;
import
{
recipe
as
field
}
from
'
./field.recipe
'
;
import
{
recipe
as
input
}
from
'
./input.recipe
'
;
import
{
recipe
as
link
}
from
'
./link.recipe
'
;
...
...
@@ -14,6 +16,7 @@ import { recipe as tooltip } from './tooltip.recipe';
export
const
recipes
=
{
button
,
closeButton
,
input
,
link
,
skeleton
,
...
...
@@ -22,6 +25,7 @@ export const recipes = {
export
const
slotRecipes
=
{
alert
,
dialog
,
field
,
popover
,
progressCircle
,
...
...
toolkit/theme/recipes/textarea.recipe.ts
View file @
e91e9ae1
...
...
@@ -20,7 +20,7 @@ export const recipe = defineRecipe({
},
variants
:
{
size
:
{
xxl
:
{
'
2xl
'
:
{
textStyle
:
'
md
'
,
px
:
'
6
'
,
py
:
'
4
'
,
...
...
@@ -76,7 +76,7 @@ export const recipe = defineRecipe({
},
defaultVariants
:
{
size
:
'
x
xl
'
,
size
:
'
2
xl
'
,
variant
:
'
outline
'
,
},
});
ui/pages/Chakra.tsx
View file @
e91e9ae1
...
...
@@ -40,6 +40,7 @@ const ChakraShowcases = () => {
<
Button
visual=
"header"
>
Header
</
Button
>
<
Button
visual=
"header"
selected
>
Header selected
</
Button
>
<
Button
visual=
"header"
selected
highlighted
>
Header highlighted
</
Button
>
<
Button
visual=
"link"
>
Link
</
Button
>
</
HStack
>
</
section
>
...
...
@@ -102,10 +103,10 @@ const ChakraShowcases = () => {
<
section
>
<
Heading
textStyle=
"heading.md"
mb=
{
2
}
>
Textarea
</
Heading
>
<
HStack
gap=
{
4
}
>
<
Field
label=
"Description"
required
floating
size=
"
x
xl"
w=
"400px"
>
<
Field
label=
"Description"
required
floating
size=
"
2
xl"
w=
"400px"
>
<
Textarea
/>
</
Field
>
<
Field
label=
"Description"
required
floating
size=
"
x
xl"
w=
"400px"
>
<
Field
label=
"Description"
required
floating
size=
"
2
xl"
w=
"400px"
>
<
Textarea
value=
{
TEXT
}
/>
</
Field
>
</
HStack
>
...
...
ui/shared/buttons/ButtonBackTo.tsx
0 → 100644
View file @
e91e9ae1
import
React
from
'
react
'
;
import
{
IconButton
}
from
'
toolkit/chakra/icon-button
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
interface
Props
{
onClick
:
()
=>
void
;
}
const
ButtonBackTo
=
({
onClick
}:
Props
)
=>
{
return
(
<
IconButton
>
<
IconSvg
name=
"arrows/east"
boxSize=
{
6
}
transform=
"rotate(180deg)"
color=
"icon.backTo"
_hover=
{
{
color
:
'
link.primary.hover
'
}
}
onClick=
{
onClick
}
/>
</
IconButton
>
);
};
export
default
React
.
memo
(
ButtonBackTo
);
ui/shared/forms/fields/FormFieldEmail.tsx
View file @
e91e9ae1
import
type
{
ChakraProps
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
FieldValues
,
Path
}
from
'
react-hook-form
'
;
import
type
{
FieldValues
}
from
'
react-hook-form
'
;
import
type
{
FormFieldPropsBase
}
from
'
./types
'
;
import
type
{
PartialBy
}
from
'
types/utils
'
;
...
...
@@ -28,9 +27,4 @@ const FormFieldEmail = <FormFields extends FieldValues>(
);
}
;
export type WrappedComponent =
<
FormFields
extends
FieldValues
,
Name
extends
Path
<
FormFields
>
= Path
<
FormFields
>
,
>
(props: PartialBy
<
FormFieldPropsBase
<
FormFields
,
Name
>
, 'placeholder'
>
&
ChakraProps) =
>
React.JSX.Element;
export default React.memo(FormFieldEmail) as WrappedComponent;
export default React.memo(FormFieldEmail) as typeof FormFieldEmail;
ui/shared/forms/fields/FormFieldText.tsx
View file @
e91e9ae1
import
type
{
ChakraProps
}
from
'
@chakra-ui/react
'
;
import
{
FormControl
,
Input
,
InputGroup
,
InputRightElement
,
Textarea
,
chakra
,
shouldForwardProp
}
from
'
@chakra-ui/react
'
;
import
type
{
HTMLChakraProps
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
FieldValues
,
Path
}
from
'
react-hook-form
'
;
import
{
useController
,
useFormContext
}
from
'
react-hook-form
'
;
import
type
{
FormFieldPropsBase
}
from
'
./types
'
;
import
FormInputPlaceholder
from
'
../inputs/FormInputPlaceholder
'
;
import
{
Field
}
from
'
toolkit/chakra/field
'
;
import
{
Input
}
from
'
toolkit/chakra/input
'
;
import
{
Textarea
}
from
'
toolkit/chakra/textarea
'
;
import
getFieldErrorText
from
'
../utils/getFieldErrorText
'
;
interface
Props
<
FormFields
extends
FieldValues
,
...
...
@@ -21,95 +24,82 @@ const FormFieldText = <
>
(
{
name
,
placeholder
,
isReadOnly
,
isRequired
,
rules
,
onBlur
,
type
=
'
text
'
,
rightElement
,
inputProps
,
asComponent
,
max
,
className
,
size
=
'
md
'
,
bgColor
,
minH
,
maxH
,
size
=
'
xl
'
,
disabled
,
...
restProps
}
: Props
<
FormFields
,
Name
>
) =
>
{
const
{
control
}
=
useFormContext
<
FormFields
>
();
const
{
field
,
fieldState
,
formState
}
=
useController
<
FormFields
,
typeof
name
>
({
control
,
name
,
rules
:
{
...
rules
,
required
:
isR
equired
},
rules
:
{
...
rules
,
required
:
restProps
.
r
equired
},
});
const
isDisabled
=
formState
.
isSubmitting
;
const
handleBlur
=
React
.
useCallback
(()
=>
{
field
.
onBlur
();
onBlur
?.();
},
[
field
,
onBlur
]);
const
Component
=
asComponent
===
'
Textarea
'
?
Textarea
:
Input
;
const
input
=
(
<
Component
const
input
=
asComponent
===
'
Textarea
'
?
(
<
Textarea
{
...
field
}
autoComplete=
"off"
{
...
inputProps
as
HTMLChakraProps
<'
textarea
'
>
}
onBlur=
{
handleBlur
}
isInvalid=
{
Boolean
(
fieldState
.
error
)
}
isDisabled=
{
isDisabled
}
isReadOnly=
{
isReadOnly
}
/
>
) : (
<
Input
{
...
field
}
autoComplete=
"off"
type=
{
type
}
placeholder=
" "
max=
{
max
}
size=
{
size
}
bgColor=
{
bgColor
}
minH=
{
minH
}
maxH=
{
maxH
}
{
...
inputProps
as
HTMLChakraProps
<'
input
'
>
}
onBlur=
{
handleBlur
}
/
>
);
const
inputPlaceholder
=
size
!==
'
xs
'
&&
<
FormInputPlaceholder
text=
{
placeholder
}
error=
{
fieldState
.
error
}
/>;
return (
<
F
ormControl
className=
{
className
}
variant=
"floating"
i
sDisabled=
{
isDisabled
}
isRequired=
{
isRequir
ed
}
<
F
ield
label=
{
placeholder
}
errorText=
{
getFieldErrorText
(
fieldState
.
error
)
}
i
nvalid=
{
Boolean
(
fieldState
.
error
)
}
disabled=
{
formState
.
isSubmitting
||
disabl
ed
}
size=
{
size
}
bgColor=
{
bgColor
}
floating
{
...
restProps
}
>
{
rightElement
?
(
<
InputGroup
>
{
input
}
{
inputPlaceholder
}
<
InputRightElement
h=
"100%"
>
{
rightElement
({
field
})
}
</
InputRightElement
>
</
InputGroup
>
)
:
(
<>
{
input
}
{
inputPlaceholder
}
</>
)
}
</
FormControl
>
{
input
}
</
Field
>
);
}
;
const WrappedFormFieldText = chakra(FormFieldText,
{
shouldForwardProp
:
(
prop
)
=>
{
const
isChakraProp
=
!
shouldForwardProp
(
prop
);
if
(
isChakraProp
&&
!
[
'
bgColor
'
,
'
size
'
,
'
minH
'
,
'
maxH
'
].
includes
(
prop
))
{
return
false
;
}
return
true
;
},
}
);
// TODO @tom2drum add input group
export type WrappedComponent =
<
FormFields
extends
FieldValues
,
Name
extends
Path
<
FormFields
>
= Path
<
FormFields
>
,
>
(props: Props
<
FormFields
,
Name
>
&
ChakraProps) =
>
React.JSX.Element;
// return (
//
<
FormControl
// className=
{
className
}
// variant="floating"
// isDisabled=
{
isDisabled
}
// isRequired=
{
isRequired
}
// size=
{
size
}
// bgColor=
{
bgColor
}
// >
//
{
rightElement
?
(
// <InputGroup>
//
{
input
}
//
{
inputPlaceholder
}
// <InputRightElement h="100%">
{
rightElement
({
field
})
}
</
InputRightElement
>
//
</
InputGroup
>
// ) : (
//
<>
//
{
input
}
//
{
inputPlaceholder
}
//
</>
// ) }
//
</
FormControl
>
// );
}
;
export default React.memo(
WrappedFormFieldText) as WrappedComponen
t;
export default React.memo(
FormFieldText) as typeof FormFieldTex
t;
ui/shared/forms/fields/types.ts
View file @
e91e9ae1
import
type
{
FormControl
Props
}
from
'
@chakra-ui/react
'
;
import
type
{
HTMLChakra
Props
}
from
'
@chakra-ui/react
'
;
import
type
React
from
'
react
'
;
import
type
{
ControllerRenderProps
,
FieldValues
,
Path
,
RegisterOptions
}
from
'
react-hook-form
'
;
import
type
{
FieldProps
}
from
'
toolkit/chakra/field
'
;
export
interface
FormFieldPropsBase
<
FormFields
extends
FieldValues
,
Name
extends
Path
<
FormFields
>
=
Path
<
FormFields
>
,
>
{
>
extends
Omit
<
FieldProps
,
'
children
'
>
{
name
:
Name
;
placeholder
:
string
;
isReadOnly
?:
boolean
;
isRequired
?:
boolean
;
rules
?:
Omit
<
RegisterOptions
<
FormFields
,
Name
>
,
'
valueAsNumber
'
|
'
valueAsDate
'
|
'
setValueAs
'
|
'
disabled
'
>
;
onBlur
?:
()
=>
void
;
onChange
?:
()
=>
void
;
type
?:
HTMLInputElement
[
'
type
'
];
rightElement
?:
({
field
}:
{
field
:
ControllerRenderProps
<
FormFields
,
Name
>
})
=>
React
.
ReactNode
;
max
?:
HTMLInputElement
[
'
max
'
];
// styles
size
?:
FormControlProps
[
'
size
'
];
bgColor
?:
FormControlProps
[
'
bgColor
'
];
maxH
?:
FormControlProps
[
'
maxH
'
];
minH
?:
FormControlProps
[
'
minH
'
];
className
?:
string
;
inputProps
?:
HTMLChakraProps
<
'
input
'
|
'
textarea
'
>
;
}
ui/shared/forms/inputs/FormInputPlaceholder.tsx
View file @
e91e9ae1
...
...
@@ -9,6 +9,7 @@ interface Props {
isFancy
?:
boolean
;
}
// TODO @tom2drum remove this component
const
FormInputPlaceholder
=
({
text
,
icon
,
error
,
isFancy
}:
Props
)
=>
{
let
errorMessage
=
error
?.
message
;
...
...
ui/shared/forms/utils/getFieldErrorText.ts
0 → 100644
View file @
e91e9ae1
import
type
{
FieldError
}
from
'
react-hook-form
'
;
export
default
function
getFieldErrorText
(
error
:
FieldError
|
undefined
)
{
if
(
!
error
?.
message
&&
error
?.
type
===
'
pattern
'
)
{
return
'
Invalid format
'
;
}
return
error
?.
message
;
}
ui/snippets/auth/AuthModal.tsx
View file @
e91e9ae1
...
...
@@ -8,8 +8,8 @@ import config from 'configs/app';
import
{
getResourceKey
}
from
'
lib/api/useApiQuery
'
;
import
useGetCsrfToken
from
'
lib/hooks/useGetCsrfToken
'
;
import
*
as
mixpanel
from
'
lib/mixpanel
'
;
import
{
DialogBackdrop
,
DialogBody
,
DialogC
loseTrigger
,
DialogC
ontent
,
DialogHeader
,
DialogRoot
}
from
'
toolkit/chakra/dialog
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
{
DialogBackdrop
,
DialogBody
,
DialogContent
,
DialogHeader
,
DialogRoot
}
from
'
toolkit/chakra/dialog
'
;
import
ButtonBackTo
from
'
ui/shared/buttons/ButtonBackTo
'
;
import
AuthModalScreenConnectWallet
from
'
./screens/AuthModalScreenConnectWallet
'
;
import
AuthModalScreenEmail
from
'
./screens/AuthModalScreenEmail
'
;
...
...
@@ -96,7 +96,7 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro
},
[
isSuccess
,
onClose
]);
const
onModalOpenChange
=
React
.
useCallback
(({
open
}:
{
open
:
boolean
})
=>
{
open
&&
onClose
();
!
open
&&
onClose
();
},
[
onClose
]);
const
header
=
(()
=>
{
...
...
@@ -170,23 +170,13 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro
return
(
<
DialogRoot
open
onOpenChange=
{
onModalOpenChange
}
size=
{
{
base
:
'
full
'
,
lg
:
'
sm
'
}
}
>
<
DialogBackdrop
/>
<
DialogContent
p=
{
6
}
maxW=
{
{
lg
:
'
400px
'
}
}
>
<
DialogHeader
fontWeight=
"500"
textStyle=
"h3"
mb=
{
2
}
display=
"flex"
alignItems=
"center"
columnGap=
{
2
}
>
{
steps
.
length
>
1
&&
!
steps
[
steps
.
length
-
1
].
type
.
startsWith
(
'
success
'
)
&&
(
<
IconSvg
name=
"arrows/east"
boxSize=
{
6
}
transform=
"rotate(180deg)"
color=
"gray.400"
flexShrink=
{
0
}
onClick=
{
onPrevStep
}
cursor=
"pointer"
/>
)
}
<
DialogContent
>
<
DialogHeader
startElement=
{
steps
.
length
>
1
&&
!
steps
[
steps
.
length
-
1
].
type
.
startsWith
(
'
success
'
)
&&
<
ButtonBackTo
onClick=
{
onPrevStep
}
/>
}
>
{
header
}
</
DialogHeader
>
<
DialogCloseTrigger
top=
{
6
}
right=
{
6
}
color=
"gray.400"
/>
<
DialogBody
mb=
{
0
}
>
<
DialogBody
>
{
content
}
</
DialogBody
>
</
DialogContent
>
...
...
ui/snippets/auth/screens/AuthModalScreenEmail.tsx
View file @
e91e9ae1
import
{
chakra
,
Button
,
Text
}
from
'
@chakra-ui/react
'
;
import
{
chakra
,
Text
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
SubmitHandler
}
from
'
react-hook-form
'
;
import
{
FormProvider
,
useForm
}
from
'
react-hook-form
'
;
import
type
{
EmailFormFields
,
Screen
}
from
'
../types
'
;
import
{
toaster
}
from
'
toolkit/chakra/toaster
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
import
getErrorObjPayload
from
'
lib/errors/getErrorObjPayload
'
;
import
*
as
mixpanel
from
'
lib/mixpanel
'
;
import
{
Button
}
from
'
toolkit/chakra/button
'
;
import
{
toaster
}
from
'
toolkit/chakra/toaster
'
;
import
FormFieldEmail
from
'
ui/shared/forms/fields/FormFieldEmail
'
;
import
ReCaptcha
from
'
ui/shared/reCaptcha/ReCaptcha
'
;
import
useReCaptcha
from
'
ui/shared/reCaptcha/useReCaptcha
'
;
...
...
@@ -79,16 +80,16 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
<
Text
>
Account email, used for transaction notifications from your watchlist.
</
Text
>
<
FormFieldEmail
<
EmailFormFields
>
name="email"
isR
equired
r
equired
placeholder="Email"
bgColor="dialog
_
bg"
bgColor="dialog
.
bg"
mt=
{
6
}
/
>
<
Button
mt=
{
6
}
type=
"submit"
disabled=
{
formApi
.
formState
.
isSubmitting
}
isL
oading=
{
formApi
.
formState
.
isSubmitting
}
l
oading=
{
formApi
.
formState
.
isSubmitting
}
loadingText=
"Send a code"
>
Send a code
...
...
ui/snippets/user/profile/UserProfileDesktop.tsx
View file @
e91e9ae1
import
{
useDisclosure
,
type
ButtonProps
}
from
'
@chakra-ui/react
'
;
import
{
type
ButtonProps
}
from
'
@chakra-ui/react
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
...
...
@@ -8,6 +8,7 @@ import config from 'configs/app';
import
*
as
mixpanel
from
'
lib/mixpanel
'
;
import
useAccount
from
'
lib/web3/useAccount
'
;
import
{
PopoverBody
,
PopoverContent
,
PopoverRoot
,
PopoverTrigger
}
from
'
toolkit/chakra/popover
'
;
import
{
useDisclosure
}
from
'
toolkit/hooks/useDisclosure
'
;
import
AuthModal
from
'
ui/snippets/auth/AuthModal
'
;
import
useProfileQuery
from
'
ui/snippets/auth/useProfileQuery
'
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment