Commit 1dfdcef2 authored by tom's avatar tom

fix floating labels

parent 5969ae25
import { formAnatomy as parts } from '@chakra-ui/anatomy'; import { formAnatomy as parts } from '@chakra-ui/anatomy';
import { import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
createMultiStyleConfigHelpers, import type { StyleFunctionProps } from '@chakra-ui/theme-tools';
} from '@chakra-ui/styled-system';
import { getColor, mode } from '@chakra-ui/theme-tools'; import { getColor, mode } from '@chakra-ui/theme-tools';
import type { Dict } from '@chakra-ui/utils';
import getDefaultFormColors from '../utils/getDefaultFormColors'; import getDefaultFormColors from '../utils/getDefaultFormColors';
import FormLabel from './FormLabel';
import Input from './Input';
import Textarea from './Textarea';
const { definePartsStyle, defineMultiStyleConfig } = const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys); createMultiStyleConfigHelpers(parts.keys);
const getActiveLabelStyles = (theme: Dict, fc: string, bc: string, size: 'md' | 'lg') => { function getFloatingVariantStylesForSize(size: 'md' | 'lg', props: StyleFunctionProps) {
const baseStyles = { const { theme } = props;
backgroundColor: bc, const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props);
color: getColor(theme, fc),
fontSize: 'xs',
lineHeight: '16px',
borderTopRightRadius: 'none',
};
switch (size) {
case 'md': {
return {
...baseStyles,
padding: '10px 16px 2px 16px',
};
}
case 'lg': { const activeLabelStyles = {
return { ...FormLabel.variants?.floating?.(props)._focusWithin,
...baseStyles, ...FormLabel.sizes?.[size](props)._focusWithin,
padding: '16px 24px 2px 24px', } || {};
};
}
}
};
const getDefaultLabelStyles = (size: 'md' | 'lg') => { const activeInputStyles = (() => {
switch (size) { switch (size) {
case 'md': { case 'md': {
return { return {
fontSize: 'md', paddingTop: '26px',
lineHeight: '20px', paddingBottom: '10px',
padding: '18px 16px',
right: '18px',
}; };
} }
case 'lg': { case 'lg': {
return { return {
fontSize: 'md', paddingTop: '38px',
lineHeight: '24px', paddingBottom: '18px',
padding: '28px 24px',
right: '26px',
}; };
} }
} }
}; })();
const getPaddingX = (size: 'md' | 'lg') => { const inputPx = (() => {
switch (size) { switch (size) {
case 'md': { case 'md': {
return '16px'; return '16px';
...@@ -68,84 +48,35 @@ const getPaddingX = (size: 'md' | 'lg') => { ...@@ -68,84 +48,35 @@ const getPaddingX = (size: 'md' | 'lg') => {
return '24px'; return '24px';
} }
} }
}; })();
const getActiveInputStyles = (size: 'md' | 'lg') => {
switch (size) {
case 'md': {
return {
paddingTop: '26px',
paddingBottom: '10px',
};
}
case 'lg': {
return {
paddingTop: '38px',
paddingBottom: '18px',
};
}
}
};
const variantFloating = definePartsStyle((props) => {
const { theme, backgroundColor, size = 'md' } = props;
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props);
const bc = backgroundColor || mode('white', 'black')(props);
const px = getPaddingX(size);
const activeInputStyles = getActiveInputStyles(size);
const activeLabelStyles = getActiveLabelStyles(theme, fc, bc, size);
return { return {
container: { container: {
// active styles
_focusWithin: { _focusWithin: {
label: { label: activeLabelStyles,
...activeLabelStyles, 'input, textarea': activeInputStyles,
},
'input, textarea': {
...activeInputStyles,
},
'label .chakra-form__required-indicator': {
color: getColor(theme, fc),
},
},
// label's styles
label: {
...getDefaultLabelStyles(size),
left: '2px',
top: '2px',
zIndex: 2,
position: 'absolute',
borderRadius: 'base',
boxSizing: 'border-box',
color: 'gray.500',
backgroundColor: 'transparent',
pointerEvents: 'none',
margin: 0,
transformOrigin: 'top left',
transitionProperty: 'font-size, line-height, padding, top, background-color',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
},
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': {
...activeLabelStyles,
}, },
// label styles
label: FormLabel.sizes?.[size](props) || {},
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': activeLabelStyles,
'input[aria-invalid=true] + label, textarea[aria-invalid=true] + label': { 'input[aria-invalid=true] + label, textarea[aria-invalid=true] + label': {
color: getColor(theme, ec), color: getColor(theme, ec),
}, },
// input's styles
// input styles
input: Input.sizes?.[size].field,
textarea: Textarea.sizes?.[size],
'input, textarea': { 'input, textarea': {
padding: px, padding: inputPx,
}, },
'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': activeInputStyles,
'input[disabled] + label, textarea[disabled] + label': { 'input[disabled] + label, textarea[disabled] + label': {
backgroundColor: 'transparent', backgroundColor: 'transparent',
}, },
'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': {
...activeInputStyles, // indicator styles
},
// indicator's styles
'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': { 'input:not(:placeholder-shown) + label .chakra-form__required-indicator, textarea:not(:placeholder-shown) + label .chakra-form__required-indicator': {
color: getColor(theme, fc), color: getColor(theme, fc),
}, },
...@@ -153,6 +84,11 @@ const variantFloating = definePartsStyle((props) => { ...@@ -153,6 +84,11 @@ const variantFloating = definePartsStyle((props) => {
color: getColor(theme, ec), color: getColor(theme, ec),
}, },
}, },
};
}
const baseStyle = definePartsStyle((props) => {
return {
requiredIndicator: { requiredIndicator: {
marginStart: 0, marginStart: 0,
color: mode('gray.500', 'whiteAlpha.400')(props), color: mode('gray.500', 'whiteAlpha.400')(props),
...@@ -160,12 +96,42 @@ const variantFloating = definePartsStyle((props) => { ...@@ -160,12 +96,42 @@ const variantFloating = definePartsStyle((props) => {
}; };
}); });
const variantFloating = definePartsStyle((props) => {
return {
container: {
label: FormLabel.variants?.floating(props) || {},
},
};
});
const sizes = {
lg: definePartsStyle((props) => {
if (props.variant === 'floating') {
return getFloatingVariantStylesForSize('lg', props);
}
return {};
}),
md: definePartsStyle((props) => {
if (props.variant === 'floating') {
return getFloatingVariantStylesForSize('md', props);
}
return {};
}),
};
const variants = { const variants = {
floating: variantFloating, floating: variantFloating,
}; };
const Form = defineMultiStyleConfig({ const Form = defineMultiStyleConfig({
baseStyle,
variants, variants,
sizes,
defaultProps: {
size: 'md',
},
}); });
export default Form; export default Form;
import { defineStyle, defineStyleConfig } from '@chakra-ui/styled-system';
import { getColor, mode } from '@chakra-ui/theme-tools';
import getDefaultFormColors from '../utils/getDefaultFormColors';
const baseStyle = defineStyle({
fontSize: 'md',
marginEnd: '3',
mb: '2',
fontWeight: 'medium',
transitionProperty: 'common',
transitionDuration: 'normal',
opacity: 1,
_disabled: {
opacity: 0.4,
},
});
const variantFloating = defineStyle((props) => {
const { theme, backgroundColor } = props;
const { focusColor: fc } = getDefaultFormColors(props);
const bc = backgroundColor || mode('white', 'black')(props);
return {
left: '2px',
top: '2px',
zIndex: 2,
position: 'absolute',
borderRadius: 'base',
boxSizing: 'border-box',
color: 'gray.500',
backgroundColor: 'transparent',
pointerEvents: 'none',
margin: 0,
transformOrigin: 'top left',
transitionProperty: 'font-size, line-height, padding, top, background-color',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
_focusWithin: {
backgroundColor: bc,
color: getColor(theme, fc),
fontSize: 'xs',
lineHeight: '16px',
borderTopRightRadius: 'none',
},
};
});
const variants = {
floating: variantFloating,
};
const sizes = {
lg: defineStyle((props) => {
if (props.variant === 'floating') {
return {
fontSize: 'md',
lineHeight: '24px',
padding: '28px 24px',
right: '26px',
_focusWithin: {
padding: '16px 24px 2px 24px',
},
};
}
return {};
}),
md: defineStyle((props) => {
if (props.variant === 'floating') {
return {
fontSize: 'md',
lineHeight: '20px',
padding: '18px 16px',
right: '18px',
_focusWithin: {
padding: '10px 16px 2px 16px',
},
};
}
return {};
}),
};
const FormLabel = defineStyleConfig({
variants,
baseStyle,
sizes,
});
export default FormLabel;
...@@ -4,6 +4,7 @@ import Button from './Button'; ...@@ -4,6 +4,7 @@ import Button from './Button';
import Checkbox from './Checkbox'; import Checkbox from './Checkbox';
import Drawer from './Drawer'; import Drawer from './Drawer';
import Form from './Form'; import Form from './Form';
import FormLabel from './FormLabel';
import Heading from './Heading'; import Heading from './Heading';
import Input from './Input'; import Input from './Input';
import Link from './Link'; import Link from './Link';
...@@ -27,6 +28,7 @@ const components = { ...@@ -27,6 +28,7 @@ const components = {
Heading, Heading,
Input, Input,
Form, Form,
FormLabel,
Link, Link,
Modal, Modal,
Popover, Popover,
......
...@@ -25,7 +25,6 @@ const MyProfile = () => { ...@@ -25,7 +25,6 @@ const MyProfile = () => {
<UserAvatar size={ 64 } data={ data }/> <UserAvatar size={ 64 } data={ data }/>
<FormControl variant="floating" id="name" isRequired size="lg"> <FormControl variant="floating" id="name" isRequired size="lg">
<Input <Input
size="lg"
required required
disabled disabled
value={ data.name || '' } value={ data.name || '' }
...@@ -34,7 +33,6 @@ const MyProfile = () => { ...@@ -34,7 +33,6 @@ const MyProfile = () => {
</FormControl> </FormControl>
<FormControl variant="floating" id="nickname" isRequired size="lg"> <FormControl variant="floating" id="nickname" isRequired size="lg">
<Input <Input
size="lg"
required required
disabled disabled
value={ data.nickname || '' } value={ data.nickname || '' }
...@@ -43,7 +41,6 @@ const MyProfile = () => { ...@@ -43,7 +41,6 @@ const MyProfile = () => {
</FormControl> </FormControl>
<FormControl variant="floating" id="email" isRequired size="lg"> <FormControl variant="floating" id="email" isRequired size="lg">
<Input <Input
size="lg"
required required
disabled disabled
value={ data.email } value={ data.email }
......
import type { InputProps } from '@chakra-ui/react';
import { IconButton, Icon, Flex } from '@chakra-ui/react'; import { IconButton, Icon, Flex } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { ControllerRenderProps, Control, FieldError } from 'react-hook-form'; import type { ControllerRenderProps, Control, FieldError } from 'react-hook-form';
...@@ -17,7 +18,7 @@ interface Props { ...@@ -17,7 +18,7 @@ interface Props {
error?: FieldError; error?: FieldError;
onAddFieldClick: (e: React.SyntheticEvent) => void; onAddFieldClick: (e: React.SyntheticEvent) => void;
onRemoveFieldClick: (index: number) => (e: React.SyntheticEvent) => void; onRemoveFieldClick: (index: number) => (e: React.SyntheticEvent) => void;
size?: string; size?: InputProps['size'];
} }
const MAX_INPUTS_NUM = 10; const MAX_INPUTS_NUM = 10;
......
import type { InputProps } from '@chakra-ui/react';
import { FormControl, FormLabel, Textarea } from '@chakra-ui/react'; import { FormControl, FormLabel, Textarea } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { ControllerRenderProps, Control, FieldError } from 'react-hook-form'; import type { ControllerRenderProps, Control, FieldError } from 'react-hook-form';
...@@ -12,7 +13,7 @@ const TEXT_INPUT_MAX_LENGTH = 255; ...@@ -12,7 +13,7 @@ const TEXT_INPUT_MAX_LENGTH = 255;
interface Props { interface Props {
control: Control<Inputs>; control: Control<Inputs>;
error?: FieldError; error?: FieldError;
size?: string; size?: InputProps['size'];
} }
export default function PublicTagFormComment({ control, error, size }: Props) { export default function PublicTagFormComment({ control, error, size }: Props) {
...@@ -22,7 +23,6 @@ export default function PublicTagFormComment({ control, error, size }: Props) { ...@@ -22,7 +23,6 @@ export default function PublicTagFormComment({ control, error, size }: Props) {
<Textarea <Textarea
{ ...field } { ...field }
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
size={ size }
/> />
<FormLabel> <FormLabel>
{ getPlaceholderWithError('Specify the reason for adding tags and color preference(s)', error?.message) } { getPlaceholderWithError('Specify the reason for adding tags and color preference(s)', error?.message) }
......
...@@ -16,7 +16,6 @@ import type { PublicTags, PublicTag, PublicTagNew, PublicTagErrors } from 'types ...@@ -16,7 +16,6 @@ import type { PublicTags, PublicTag, PublicTagNew, PublicTagErrors } from 'types
import getErrorMessage from 'lib/getErrorMessage'; import getErrorMessage from 'lib/getErrorMessage';
import type { ErrorType } from 'lib/hooks/useFetch'; import type { ErrorType } from 'lib/hooks/useFetch';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import useIsMobile from 'lib/hooks/useIsMobile';
import { EMAIL_REGEXP } from 'lib/validations/email'; import { EMAIL_REGEXP } from 'lib/validations/email';
import FormSubmitAlert from 'ui/shared/FormSubmitAlert'; import FormSubmitAlert from 'ui/shared/FormSubmitAlert';
...@@ -57,9 +56,8 @@ const ADDRESS_INPUT_BUTTONS_WIDTH = 100; ...@@ -57,9 +56,8 @@ const ADDRESS_INPUT_BUTTONS_WIDTH = 100;
const PublicTagsForm = ({ changeToDataScreen, data }: Props) => { const PublicTagsForm = ({ changeToDataScreen, data }: Props) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const isMobile = useIsMobile();
const fetch = useFetch(); const fetch = useFetch();
const inputSize = isMobile ? 'md' : 'lg'; const inputSize = { base: 'md', lg: 'lg' };
const { control, handleSubmit, formState: { errors, isValid }, setError } = useForm<Inputs>({ const { control, handleSubmit, formState: { errors, isValid }, setError } = useForm<Inputs>({
defaultValues: { defaultValues: {
......
import type { InputProps } from '@chakra-ui/react';
import { FormControl, FormLabel, Input } from '@chakra-ui/react'; import { FormControl, FormLabel, Input } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { ControllerRenderProps, FieldError, FieldValues, Path, Control } from 'react-hook-form'; import type { ControllerRenderProps, FieldError, FieldValues, Path, Control } from 'react-hook-form';
...@@ -14,7 +15,7 @@ interface Props<TInputs extends FieldValues> { ...@@ -14,7 +15,7 @@ interface Props<TInputs extends FieldValues> {
control: Control<TInputs, object>; control: Control<TInputs, object>;
pattern?: RegExp; pattern?: RegExp;
error?: FieldError; error?: FieldError;
size?: string; size?: InputProps['size'];
} }
export default function PublicTagsFormInput<Inputs extends FieldValues>({ export default function PublicTagsFormInput<Inputs extends FieldValues>({
...@@ -31,7 +32,6 @@ export default function PublicTagsFormInput<Inputs extends FieldValues>({ ...@@ -31,7 +32,6 @@ export default function PublicTagsFormInput<Inputs extends FieldValues>({
<FormControl variant="floating" id={ field.name } isRequired={ required } size={ size }> <FormControl variant="floating" id={ field.name } isRequired={ required } size={ size }>
<Input <Input
{ ...field } { ...field }
size={ size }
required={ required } required={ required }
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
maxLength={ TEXT_INPUT_MAX_LENGTH } maxLength={ TEXT_INPUT_MAX_LENGTH }
......
import type { InputProps } from '@chakra-ui/react';
import { import {
Input, Input,
FormControl, FormControl,
...@@ -11,7 +12,7 @@ import { ADDRESS_LENGTH } from 'lib/validations/address'; ...@@ -11,7 +12,7 @@ import { ADDRESS_LENGTH } from 'lib/validations/address';
type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = { type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = {
field: ControllerRenderProps<TInputs, TInputName>; field: ControllerRenderProps<TInputs, TInputName>;
size?: string; size?: InputProps['size'];
placeholder?: string; placeholder?: string;
backgroundColor?: string; backgroundColor?: string;
error?: FieldError; error?: FieldError;
...@@ -31,7 +32,6 @@ export default function AddressInput<Inputs extends FieldValues, Name extends Pa ...@@ -31,7 +32,6 @@ export default function AddressInput<Inputs extends FieldValues, Name extends Pa
{ ...field } { ...field }
isInvalid={ Boolean(error) } isInvalid={ Boolean(error) }
maxLength={ ADDRESS_LENGTH } maxLength={ ADDRESS_LENGTH }
size={ size }
/> />
<FormLabel>{ getPlaceholderWithError(placeholder, error?.message) }</FormLabel> <FormLabel>{ getPlaceholderWithError(placeholder, error?.message) }</FormLabel>
</FormControl> </FormControl>
......
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