Commit e91e9ae1 authored by tom's avatar tom

dialog styles and AuthModal first step refactoring

parent 302a4760
import type { ButtonProps } from '@chakra-ui/react'; 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 * as React from 'react';
import { LuX } from 'react-icons/lu'; 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< export const CloseButton = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
CloseButtonProps CloseButtonProps
>(function CloseButton(props, ref) { >(function CloseButton(props, ref) {
const recipe = useRecipe({ recipe: closeButtonRecipe });
const [ recipeProps, restProps ] = recipe.splitVariantProps(props);
const styles = recipe(recipeProps);
return ( return (
<ChakraIconButton variant="ghost" aria-label="Close" ref={ ref } { ...props }> <ChakraIconButton aria-label="Close" ref={ ref } css={ styles } { ...restProps }>
{ props.children ?? <LuX/> } { props.children ?? <LuX/> }
</ChakraIconButton> </ChakraIconButton>
); );
......
...@@ -39,22 +39,37 @@ export const DialogCloseTrigger = React.forwardRef< ...@@ -39,22 +39,37 @@ export const DialogCloseTrigger = React.forwardRef<
>(function DialogCloseTrigger(props, ref) { >(function DialogCloseTrigger(props, ref) {
return ( return (
<ChakraDialog.CloseTrigger <ChakraDialog.CloseTrigger
position="absolute"
top="2"
insetEnd="2"
{ ...props } { ...props }
asChild asChild
> >
<CloseButton size="sm" ref={ ref }> <CloseButton ref={ ref }>
{ props.children } { props.children }
</CloseButton> </CloseButton>
</ChakraDialog.CloseTrigger> </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 DialogRoot = ChakraDialog.Root;
export const DialogFooter = ChakraDialog.Footer; export const DialogFooter = ChakraDialog.Footer;
export const DialogHeader = ChakraDialog.Header;
export const DialogBody = ChakraDialog.Body; export const DialogBody = ChakraDialog.Body;
export const DialogBackdrop = ChakraDialog.Backdrop; export const DialogBackdrop = ChakraDialog.Backdrop;
export const DialogTitle = ChakraDialog.Title; export const DialogTitle = ChakraDialog.Title;
......
...@@ -25,12 +25,13 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>( ...@@ -25,12 +25,13 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
placeholder: ' ', placeholder: ' ',
size: props.size, size: props.size,
floating: props.floating, floating: props.floating,
bgColor: rest.bgColor,
}); });
return ( return (
<ChakraField.Root pos="relative" w="full" ref={ ref } { ...rest }> <ChakraField.Root pos="relative" w="full" ref={ ref } { ...rest }>
{ clonedChild } { clonedChild }
<ChakraField.Label> <ChakraField.Label bgColor={ rest.bgColor }>
{ label } { label }
<ChakraField.RequiredIndicator fallback={ optionalText }/> <ChakraField.RequiredIndicator fallback={ optionalText }/>
{ errorText && ( { errorText && (
......
...@@ -12,8 +12,10 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>( ...@@ -12,8 +12,10 @@ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
py="0" py="0"
height="auto" height="auto"
minW="auto" minW="auto"
flexShrink="0"
ref={ ref } ref={ ref }
{ ...props } { ...props }
visual={ props.visual ?? 'plain' }
/> />
); );
}, },
......
...@@ -46,7 +46,7 @@ export const PopoverCloseTrigger = React.forwardRef< ...@@ -46,7 +46,7 @@ export const PopoverCloseTrigger = React.forwardRef<
asChild asChild
ref={ ref } ref={ ref }
> >
<CloseButton size="sm"/> <CloseButton/>
</ChakraPopover.CloseTrigger> </ChakraPopover.CloseTrigger>
); );
}); });
......
...@@ -140,7 +140,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -140,7 +140,7 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
}, },
input: { input: {
fg: { 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}' }, error: { value: '{colors.text.error}' },
}, },
bg: { bg: {
...@@ -163,6 +163,14 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -163,6 +163,14 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
error: { value: '{colors.red.500}' }, 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: { text: {
primary: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } }, primary: { value: { _light: '{colors.blackAlpha.800}', _dark: '{colors.whiteAlpha.800}' } },
secondary: { value: { _light: '{colors.gray.500}', _dark: '{colors.gray.400}' } }, secondary: { value: { _light: '{colors.gray.500}', _dark: '{colors.gray.400}' } },
...@@ -172,6 +180,9 @@ const semanticTokens: ThemingConfig['semanticTokens'] = { ...@@ -172,6 +180,9 @@ const semanticTokens: ThemingConfig['semanticTokens'] = {
divider: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } }, divider: { value: { _light: '{colors.blackAlpha.200}', _dark: '{colors.whiteAlpha.200}' } },
error: { value: '{colors.red.500}' }, error: { value: '{colors.red.500}' },
}, },
icon: {
backTo: { value: '{colors.gray.400}' },
},
global: { global: {
body: { body: {
bg: { value: { _light: '{colors.white}', _dark: '{colors.black}' } }, bg: { value: { _light: '{colors.white}', _dark: '{colors.black}' } },
......
...@@ -105,6 +105,16 @@ export const recipe = defineRecipe({ ...@@ -105,6 +105,16 @@ export const recipe = defineRecipe({
bg: 'transparent', bg: 'transparent',
}, },
}, },
link: {
bg: 'transparent',
color: 'link.primary',
border: 'none',
fontWeight: '400',
_hover: {
bg: 'transparent',
color: 'link.primary.hovered',
},
},
}, },
size: { size: {
xs: { px: 2, h: 6, fontSize: '12px' }, xs: { px: 2, h: 6, fontSize: '12px' },
......
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',
},
});
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',
},
});
...@@ -51,6 +51,7 @@ export const recipe = defineSlotRecipe({ ...@@ -51,6 +51,7 @@ export const recipe = defineSlotRecipe({
bg: 'bg', bg: 'bg',
top: '2px', top: '2px',
left: '2px', left: '2px',
color: 'gray.500',
width: 'calc(100% - 4px)', width: 'calc(100% - 4px)',
borderRadius: 'base', borderRadius: 'base',
pointerEvents: 'none', pointerEvents: 'none',
...@@ -83,7 +84,7 @@ export const recipe = defineSlotRecipe({ ...@@ -83,7 +84,7 @@ export const recipe = defineSlotRecipe({
}, },
}, },
// special size for textarea // special size for textarea
xxl: { '2xl': {
label: { label: {
fontSize: 'md', fontSize: 'md',
}, },
...@@ -136,7 +137,7 @@ export const recipe = defineSlotRecipe({ ...@@ -136,7 +137,7 @@ export const recipe = defineSlotRecipe({
}, },
}, },
{ {
size: 'xxl', size: '2xl',
floating: true, floating: true,
css: { css: {
label: { label: {
......
import { recipe as alert } from './alert.recipe'; import { recipe as alert } from './alert.recipe';
import { recipe as button } from './button.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 field } from './field.recipe';
import { recipe as input } from './input.recipe'; import { recipe as input } from './input.recipe';
import { recipe as link } from './link.recipe'; import { recipe as link } from './link.recipe';
...@@ -14,6 +16,7 @@ import { recipe as tooltip } from './tooltip.recipe'; ...@@ -14,6 +16,7 @@ import { recipe as tooltip } from './tooltip.recipe';
export const recipes = { export const recipes = {
button, button,
closeButton,
input, input,
link, link,
skeleton, skeleton,
...@@ -22,6 +25,7 @@ export const recipes = { ...@@ -22,6 +25,7 @@ export const recipes = {
export const slotRecipes = { export const slotRecipes = {
alert, alert,
dialog,
field, field,
popover, popover,
progressCircle, progressCircle,
......
...@@ -20,7 +20,7 @@ export const recipe = defineRecipe({ ...@@ -20,7 +20,7 @@ export const recipe = defineRecipe({
}, },
variants: { variants: {
size: { size: {
xxl: { '2xl': {
textStyle: 'md', textStyle: 'md',
px: '6', px: '6',
py: '4', py: '4',
...@@ -76,7 +76,7 @@ export const recipe = defineRecipe({ ...@@ -76,7 +76,7 @@ export const recipe = defineRecipe({
}, },
defaultVariants: { defaultVariants: {
size: 'xxl', size: '2xl',
variant: 'outline', variant: 'outline',
}, },
}); });
...@@ -40,6 +40,7 @@ const ChakraShowcases = () => { ...@@ -40,6 +40,7 @@ const ChakraShowcases = () => {
<Button visual="header">Header</Button> <Button visual="header">Header</Button>
<Button visual="header" selected>Header selected</Button> <Button visual="header" selected>Header selected</Button>
<Button visual="header" selected highlighted>Header highlighted</Button> <Button visual="header" selected highlighted>Header highlighted</Button>
<Button visual="link">Link</Button>
</HStack> </HStack>
</section> </section>
...@@ -102,10 +103,10 @@ const ChakraShowcases = () => { ...@@ -102,10 +103,10 @@ const ChakraShowcases = () => {
<section> <section>
<Heading textStyle="heading.md" mb={ 2 }>Textarea</Heading> <Heading textStyle="heading.md" mb={ 2 }>Textarea</Heading>
<HStack gap={ 4 }> <HStack gap={ 4 }>
<Field label="Description" required floating size="xxl" w="400px"> <Field label="Description" required floating size="2xl" w="400px">
<Textarea/> <Textarea/>
</Field> </Field>
<Field label="Description" required floating size="xxl" w="400px"> <Field label="Description" required floating size="2xl" w="400px">
<Textarea value={ TEXT }/> <Textarea value={ TEXT }/>
</Field> </Field>
</HStack> </HStack>
......
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);
import type { ChakraProps } from '@chakra-ui/react';
import React from '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 { FormFieldPropsBase } from './types';
import type { PartialBy } from 'types/utils'; import type { PartialBy } from 'types/utils';
...@@ -28,9 +27,4 @@ const FormFieldEmail = <FormFields extends FieldValues>( ...@@ -28,9 +27,4 @@ const FormFieldEmail = <FormFields extends FieldValues>(
); );
}; };
export type WrappedComponent = < export default React.memo(FormFieldEmail) as typeof FormFieldEmail;
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;
import type { ChakraProps } from '@chakra-ui/react'; import type { HTMLChakraProps } from '@chakra-ui/react';
import { FormControl, Input, InputGroup, InputRightElement, Textarea, chakra, shouldForwardProp } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { FieldValues, Path } from 'react-hook-form'; import type { FieldValues, Path } from 'react-hook-form';
import { useController, useFormContext } from 'react-hook-form'; import { useController, useFormContext } from 'react-hook-form';
import type { FormFieldPropsBase } from './types'; 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< interface Props<
FormFields extends FieldValues, FormFields extends FieldValues,
...@@ -21,95 +24,82 @@ const FormFieldText = < ...@@ -21,95 +24,82 @@ const FormFieldText = <
>({ >({
name, name,
placeholder, placeholder,
isReadOnly,
isRequired,
rules, rules,
onBlur, onBlur,
type = 'text',
rightElement, rightElement,
inputProps,
asComponent, asComponent,
max, size = 'xl',
disabled,
className, ...restProps
size = 'md',
bgColor,
minH,
maxH,
}: Props<FormFields, Name>) => { }: Props<FormFields, Name>) => {
const { control } = useFormContext<FormFields>(); const { control } = useFormContext<FormFields>();
const { field, fieldState, formState } = useController<FormFields, typeof name>({ const { field, fieldState, formState } = useController<FormFields, typeof name>({
control, control,
name, name,
rules: { ...rules, required: isRequired }, rules: { ...rules, required: restProps.required },
}); });
const isDisabled = formState.isSubmitting;
const handleBlur = React.useCallback(() => { const handleBlur = React.useCallback(() => {
field.onBlur(); field.onBlur();
onBlur?.(); onBlur?.();
}, [ field, onBlur ]); }, [ field, onBlur ]);
const Component = asComponent === 'Textarea' ? Textarea : Input; const input = asComponent === 'Textarea' ? (
const input = ( <Textarea
<Component
{ ...field } { ...field }
autoComplete="off"
{ ...inputProps as HTMLChakraProps<'textarea'> }
onBlur={ handleBlur } onBlur={ handleBlur }
isInvalid={ Boolean(fieldState.error) } />
isDisabled={ isDisabled } ) : (
isReadOnly={ isReadOnly } <Input
{ ...field }
autoComplete="off" autoComplete="off"
type={ type } { ...inputProps as HTMLChakraProps<'input'> }
placeholder=" " onBlur={ handleBlur }
max={ max }
size={ size }
bgColor={ bgColor }
minH={ minH }
maxH={ maxH }
/> />
); );
const inputPlaceholder = size !== 'xs' && <FormInputPlaceholder text={ placeholder } error={ fieldState.error }/>;
return ( return (
<FormControl <Field
className={ className } label={ placeholder }
variant="floating" errorText={ getFieldErrorText(fieldState.error) }
isDisabled={ isDisabled } invalid={ Boolean(fieldState.error) }
isRequired={ isRequired } disabled={ formState.isSubmitting || disabled }
size={ size } size={ size }
bgColor={ bgColor } floating
{ ...restProps }
> >
{ rightElement ? (
<InputGroup>
{ input } { input }
{ inputPlaceholder } </Field>
<InputRightElement h="100%"> { rightElement({ field }) } </InputRightElement>
</InputGroup>
) : (
<>
{ input }
{ inputPlaceholder }
</>
) }
</FormControl>
); );
};
const WrappedFormFieldText = chakra(FormFieldText, {
shouldForwardProp: (prop) => {
const isChakraProp = !shouldForwardProp(prop);
if (isChakraProp && ![ 'bgColor', 'size', 'minH', 'maxH' ].includes(prop)) { // TODO @tom2drum add input group
return false;
}
return true; // return (
}, // <FormControl
}); // className={ className }
// variant="floating"
export type WrappedComponent = < // isDisabled={ isDisabled }
FormFields extends FieldValues, // isRequired={ isRequired }
Name extends Path<FormFields> = Path<FormFields>, // size={ size }
>(props: Props<FormFields, Name> & ChakraProps) => React.JSX.Element; // bgColor={ bgColor }
// >
// { rightElement ? (
// <InputGroup>
// { input }
// { inputPlaceholder }
// <InputRightElement h="100%"> { rightElement({ field }) } </InputRightElement>
// </InputGroup>
// ) : (
// <>
// { input }
// { inputPlaceholder }
// </>
// ) }
// </FormControl>
// );
};
export default React.memo(WrappedFormFieldText) as WrappedComponent; export default React.memo(FormFieldText) as typeof FormFieldText;
import type { FormControlProps } from '@chakra-ui/react'; import type { HTMLChakraProps } from '@chakra-ui/react';
import type React from 'react'; import type React from 'react';
import type { ControllerRenderProps, FieldValues, Path, RegisterOptions } from 'react-hook-form'; import type { ControllerRenderProps, FieldValues, Path, RegisterOptions } from 'react-hook-form';
import type { FieldProps } from 'toolkit/chakra/field';
export interface FormFieldPropsBase< export interface FormFieldPropsBase<
FormFields extends FieldValues, FormFields extends FieldValues,
Name extends Path<FormFields> = Path<FormFields>, Name extends Path<FormFields> = Path<FormFields>,
> { > extends Omit<FieldProps, 'children'> {
name: Name; name: Name;
placeholder: string; placeholder: string;
isReadOnly?: boolean;
isRequired?: boolean;
rules?: Omit<RegisterOptions<FormFields, Name>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>; rules?: Omit<RegisterOptions<FormFields, Name>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'disabled'>;
onBlur?: () => void; onBlur?: () => void;
onChange?: () => void; onChange?: () => void;
type?: HTMLInputElement['type'];
rightElement?: ({ field }: { field: ControllerRenderProps<FormFields, Name> }) => React.ReactNode; rightElement?: ({ field }: { field: ControllerRenderProps<FormFields, Name> }) => React.ReactNode;
max?: HTMLInputElement['max']; inputProps?: HTMLChakraProps<'input' | 'textarea'>;
// styles
size?: FormControlProps['size'];
bgColor?: FormControlProps['bgColor'];
maxH?: FormControlProps['maxH'];
minH?: FormControlProps['minH'];
className?: string;
} }
...@@ -9,6 +9,7 @@ interface Props { ...@@ -9,6 +9,7 @@ interface Props {
isFancy?: boolean; isFancy?: boolean;
} }
// TODO @tom2drum remove this component
const FormInputPlaceholder = ({ text, icon, error, isFancy }: Props) => { const FormInputPlaceholder = ({ text, icon, error, isFancy }: Props) => {
let errorMessage = error?.message; let errorMessage = error?.message;
......
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;
}
...@@ -8,8 +8,8 @@ import config from 'configs/app'; ...@@ -8,8 +8,8 @@ import config from 'configs/app';
import { getResourceKey } from 'lib/api/useApiQuery'; import { getResourceKey } from 'lib/api/useApiQuery';
import useGetCsrfToken from 'lib/hooks/useGetCsrfToken'; import useGetCsrfToken from 'lib/hooks/useGetCsrfToken';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
import { DialogBackdrop, DialogBody, DialogCloseTrigger, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog'; import { DialogBackdrop, DialogBody, DialogContent, DialogHeader, DialogRoot } from 'toolkit/chakra/dialog';
import IconSvg from 'ui/shared/IconSvg'; import ButtonBackTo from 'ui/shared/buttons/ButtonBackTo';
import AuthModalScreenConnectWallet from './screens/AuthModalScreenConnectWallet'; import AuthModalScreenConnectWallet from './screens/AuthModalScreenConnectWallet';
import AuthModalScreenEmail from './screens/AuthModalScreenEmail'; import AuthModalScreenEmail from './screens/AuthModalScreenEmail';
...@@ -96,7 +96,7 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro ...@@ -96,7 +96,7 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro
}, [ isSuccess, onClose ]); }, [ isSuccess, onClose ]);
const onModalOpenChange = React.useCallback(({ open }: { open: boolean }) => { const onModalOpenChange = React.useCallback(({ open }: { open: boolean }) => {
open && onClose(); !open && onClose();
}, [ onClose ]); }, [ onClose ]);
const header = (() => { const header = (() => {
...@@ -170,23 +170,13 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro ...@@ -170,23 +170,13 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro
return ( return (
<DialogRoot open onOpenChange={ onModalOpenChange } size={{ base: 'full', lg: 'sm' }}> <DialogRoot open onOpenChange={ onModalOpenChange } size={{ base: 'full', lg: 'sm' }}>
<DialogBackdrop/> <DialogBackdrop/>
<DialogContent p={ 6 } maxW={{ lg: '400px' }}> <DialogContent>
<DialogHeader fontWeight="500" textStyle="h3" mb={ 2 } display="flex" alignItems="center" columnGap={ 2 }> <DialogHeader
{ steps.length > 1 && !steps[steps.length - 1].type.startsWith('success') && ( startElement={ steps.length > 1 && !steps[steps.length - 1].type.startsWith('success') && <ButtonBackTo onClick={ onPrevStep }/> }
<IconSvg >
name="arrows/east"
boxSize={ 6 }
transform="rotate(180deg)"
color="gray.400"
flexShrink={ 0 }
onClick={ onPrevStep }
cursor="pointer"
/>
) }
{ header } { header }
</DialogHeader> </DialogHeader>
<DialogCloseTrigger top={ 6 } right={ 6 } color="gray.400"/> <DialogBody>
<DialogBody mb={ 0 }>
{ content } { content }
</DialogBody> </DialogBody>
</DialogContent> </DialogContent>
......
import { chakra, Button, Text } from '@chakra-ui/react'; import { chakra, Text } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { SubmitHandler } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
import type { EmailFormFields, Screen } from '../types'; import type { EmailFormFields, Screen } from '../types';
import { toaster } from 'toolkit/chakra/toaster';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from 'lib/api/useApiFetch';
import getErrorMessage from 'lib/errors/getErrorMessage'; import getErrorMessage from 'lib/errors/getErrorMessage';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import * as mixpanel from 'lib/mixpanel'; 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 FormFieldEmail from 'ui/shared/forms/fields/FormFieldEmail';
import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha'; import ReCaptcha from 'ui/shared/reCaptcha/ReCaptcha';
import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha'; import useReCaptcha from 'ui/shared/reCaptcha/useReCaptcha';
...@@ -79,16 +80,16 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { ...@@ -79,16 +80,16 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
<Text>Account email, used for transaction notifications from your watchlist.</Text> <Text>Account email, used for transaction notifications from your watchlist.</Text>
<FormFieldEmail<EmailFormFields> <FormFieldEmail<EmailFormFields>
name="email" name="email"
isRequired required
placeholder="Email" placeholder="Email"
bgColor="dialog_bg" bgColor="dialog.bg"
mt={ 6 } mt={ 6 }
/> />
<Button <Button
mt={ 6 } mt={ 6 }
type="submit" type="submit"
disabled={ formApi.formState.isSubmitting } disabled={ formApi.formState.isSubmitting }
isLoading={ formApi.formState.isSubmitting } loading={ formApi.formState.isSubmitting }
loadingText="Send a code" loadingText="Send a code"
> >
Send a code Send a code
......
import { useDisclosure, type ButtonProps } from '@chakra-ui/react'; import { type ButtonProps } from '@chakra-ui/react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
...@@ -8,6 +8,7 @@ import config from 'configs/app'; ...@@ -8,6 +8,7 @@ import config from 'configs/app';
import * as mixpanel from 'lib/mixpanel'; import * as mixpanel from 'lib/mixpanel';
import useAccount from 'lib/web3/useAccount'; import useAccount from 'lib/web3/useAccount';
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'toolkit/chakra/popover';
import { useDisclosure } from 'toolkit/hooks/useDisclosure';
import AuthModal from 'ui/snippets/auth/AuthModal'; import AuthModal from 'ui/snippets/auth/AuthModal';
import useProfileQuery from 'ui/snippets/auth/useProfileQuery'; import useProfileQuery from 'ui/snippets/auth/useProfileQuery';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment