Commit c9a77057 authored by tom's avatar tom

add underlay for floating labels

parent 70bea3dd
...@@ -6,21 +6,89 @@ import type { Dict } from '@chakra-ui/utils'; ...@@ -6,21 +6,89 @@ import type { Dict } from '@chakra-ui/utils';
import getDefaultFormColors from '../utils/getDefaultFormColors'; import getDefaultFormColors from '../utils/getDefaultFormColors';
const activeInputStyles = { const getActiveLabelStyles = (theme: Dict, fc: string, bc: string, size: 'md' | 'lg') => {
paddingTop: '30px', const baseStyles = {
paddingBottom: '10px', backgroundColor: bc,
color: getColor(theme, fc),
fontSize: 'xs',
lineHeight: '16px',
};
switch (size) {
case 'md': {
return {
...baseStyles,
padding: '10px 16px 2px 16px',
};
}
case 'lg': {
return {
...baseStyles,
padding: '16px 24px 2px 24px',
};
}
}
};
const getDefaultLabelStyles = (size: 'md' | 'lg') => {
switch (size) {
case 'md': {
return {
fontSize: 'md',
lineHeight: '20px',
padding: '18px 16px',
};
}
case 'lg': {
return {
fontSize: 'md',
lineHeight: '24px',
padding: '28px 24px',
};
}
}
};
const getPaddingX = (size: 'md' | 'lg') => {
switch (size) {
case 'md': {
return '16px';
}
case 'lg': {
return '24px';
}
}
}; };
const getActiveLabelStyles = (theme: Dict, fc: string) => ({ const getActiveInputStyles = (size: 'md' | 'lg') => {
color: getColor(theme, fc), switch (size) {
transform: 'scale(0.75) translateY(-10px)', case 'md': {
}); return {
paddingTop: '26px',
paddingBottom: '10px',
};
}
case 'lg': {
return {
paddingTop: '38px',
paddingBottom: '18px',
};
}
}
};
const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionProps) => { const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionProps) => {
const { theme } = props; const { theme, backgroundColor, size = 'md' } = props;
const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props); const { focusColor: fc, errorColor: ec } = getDefaultFormColors(props);
const bc = backgroundColor || mode('white', 'black')(props);
const activeLabelStyles = getActiveLabelStyles(theme, fc); const px = getPaddingX(size);
const activeInputStyles = getActiveInputStyles(size);
const activeLabelStyles = getActiveLabelStyles(theme, fc, bc, size);
return { return {
container: { container: {
...@@ -37,22 +105,20 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP ...@@ -37,22 +105,20 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP
}, },
// label's styles // label's styles
label: { label: {
left: '22px', ...getDefaultLabelStyles(size),
left: '2px',
right: '2px',
top: '2px',
zIndex: 2, zIndex: 2,
position: 'absolute', position: 'absolute',
borderRadius: 'base',
boxSizing: 'border-box',
color: mode('gray.500', 'whiteAlpha.400')(props), color: mode('gray.500', 'whiteAlpha.400')(props),
backgroundColor: 'transparent', backgroundColor: 'transparent',
pointerEvents: 'none', pointerEvents: 'none',
margin: 0, margin: 0,
transformOrigin: 'left top', transformOrigin: 'top left',
fontSize: 'md', transition: 'font-size 200ms ease, line-height 200ms ease, padding 200ms ease, top 200ms ease, background-color 200ms ease 200ms',
lineHeight: '20px',
},
'input + label': {
top: 'calc(50% - 10px);',
},
'textarea + label': {
top: '20px',
}, },
'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': { 'input:not(:placeholder-shown) + label, textarea:not(:placeholder-shown) + label': {
...activeLabelStyles, ...activeLabelStyles,
...@@ -62,7 +128,10 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP ...@@ -62,7 +128,10 @@ const variantFloating: PartsStyleFunction<typeof parts> = (props: StyleFunctionP
}, },
// input's styles // input's styles
'input, textarea': { 'input, textarea': {
padding: '20px', padding: px,
},
'input[disabled] + label, textarea[disabled] + label': {
backgroundColor: 'transparent',
}, },
'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': { 'input:not(:placeholder-shown), textarea:not(:placeholder-shown)': {
...activeInputStyles, ...activeInputStyles,
......
...@@ -4,6 +4,7 @@ import { ...@@ -4,6 +4,7 @@ import {
FormControl, FormControl,
FormLabel, FormLabel,
Input, Input,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
...@@ -28,6 +29,7 @@ const NAME_MAX_LENGTH = 100; ...@@ -28,6 +29,7 @@ const NAME_MAX_LENGTH = 100;
const ApiKeyForm: React.FC<Props> = ({ data, onClose }) => { const ApiKeyForm: React.FC<Props> = ({ data, onClose }) => {
const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>(); const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const formBackgroundColor = useColorModeValue('white', 'gray.800');
useEffect(() => { useEffect(() => {
setValue('token', data?.api_key || ''); setValue('token', data?.api_key || '');
...@@ -88,7 +90,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -88,7 +90,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose }) => {
const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => { const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => {
return ( return (
<FormControl variant="floating" id="name" isRequired> <FormControl variant="floating" id="name" isRequired backgroundColor={ formBackgroundColor }>
<Input <Input
{ ...field } { ...field }
isInvalid={ Boolean(errors.name) } isInvalid={ Boolean(errors.name) }
...@@ -97,7 +99,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -97,7 +99,7 @@ const ApiKeyForm: React.FC<Props> = ({ data, onClose }) => {
<FormLabel>Application name for API key (e.g Web3 project)</FormLabel> <FormLabel>Application name for API key (e.g Web3 project)</FormLabel>
</FormControl> </FormControl>
); );
}, [ errors ]); }, [ errors, formBackgroundColor ]);
return ( return (
<> <>
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
FormLabel, FormLabel,
Input, Input,
Textarea, Textarea,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
...@@ -47,6 +48,8 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -47,6 +48,8 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => {
return fetch(`/api/account/custom-abis/${ data.id }`, { method: 'PUT', body }); return fetch(`/api/account/custom-abis/${ data.id }`, { method: 'PUT', body });
}; };
const formBackgroundColor = useColorModeValue('white', 'gray.800');
const mutation = useMutation(customAbiKey, { const mutation = useMutation(customAbiKey, {
onSuccess: async(data) => { onSuccess: async(data) => {
const response: CustomAbi = await data.json(); const response: CustomAbi = await data.json();
...@@ -79,7 +82,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -79,7 +82,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => {
const renderContractAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'contract_address_hash'>}) => { const renderContractAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'contract_address_hash'>}) => {
return ( return (
<FormControl variant="floating" id="contract_address_hash" isRequired> <FormControl variant="floating" id="contract_address_hash" isRequired backgroundColor={ formBackgroundColor }>
<Input <Input
{ ...field } { ...field }
isInvalid={ Boolean(errors.contract_address_hash) } isInvalid={ Boolean(errors.contract_address_hash) }
...@@ -87,11 +90,11 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -87,11 +90,11 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => {
<FormLabel>Smart contract address (0x...)</FormLabel> <FormLabel>Smart contract address (0x...)</FormLabel>
</FormControl> </FormControl>
); );
}, [ errors ]); }, [ errors, formBackgroundColor ]);
const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => { const renderNameInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'name'>}) => {
return ( return (
<FormControl variant="floating" id="name" isRequired> <FormControl variant="floating" id="name" isRequired backgroundColor={ formBackgroundColor }>
<Input <Input
{ ...field } { ...field }
isInvalid={ Boolean(errors.name) } isInvalid={ Boolean(errors.name) }
...@@ -100,11 +103,11 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -100,11 +103,11 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => {
<FormLabel>Project name</FormLabel> <FormLabel>Project name</FormLabel>
</FormControl> </FormControl>
); );
}, [ errors ]); }, [ errors, formBackgroundColor ]);
const renderAbiInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'abi'>}) => { const renderAbiInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'abi'>}) => {
return ( return (
<FormControl variant="floating" id="abi" isRequired> <FormControl variant="floating" id="abi" isRequired backgroundColor={ formBackgroundColor }>
<Textarea <Textarea
{ ...field } { ...field }
size="lg" size="lg"
...@@ -113,7 +116,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -113,7 +116,7 @@ const CustomAbiForm: React.FC<Props> = ({ data, onClose }) => {
<FormLabel>{ `Custom ABI [{...}] (JSON format)` }</FormLabel> <FormLabel>{ `Custom ABI [{...}] (JSON format)` }</FormLabel>
</FormControl> </FormControl>
); );
}, [ errors ]); }, [ errors, formBackgroundColor ]);
return ( return (
<> <>
......
import { import {
Box, Box,
Button, Button,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
...@@ -28,6 +29,7 @@ type Inputs = { ...@@ -28,6 +29,7 @@ type Inputs = {
const AddressForm: React.FC<Props> = ({ data, onClose }) => { const AddressForm: React.FC<Props> = ({ data, onClose }) => {
const [ pending, setPending ] = useState(false); const [ pending, setPending ] = useState(false);
const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>(); const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>();
const formBackgroundColor = useColorModeValue('white', 'gray.800');
useEffect(() => { useEffect(() => {
setValue('address', data?.address_hash || ''); setValue('address', data?.address_hash || '');
...@@ -67,12 +69,12 @@ const AddressForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -67,12 +69,12 @@ const AddressForm: React.FC<Props> = ({ data, onClose }) => {
}; };
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => { const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => {
return <AddressInput<Inputs, 'address'> field={ field } isInvalid={ Boolean(errors.address) }/>; return <AddressInput<Inputs, 'address'> field={ field } isInvalid={ Boolean(errors.address) } backgroundColor={ formBackgroundColor }/>;
}, [ errors ]); }, [ errors, formBackgroundColor ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => { const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput field={ field } isInvalid={ Boolean(errors.tag) }/>; return <TagInput field={ field } isInvalid={ Boolean(errors.tag) } backgroundColor={ formBackgroundColor }/>;
}, [ errors ]); }, [ errors, formBackgroundColor ]);
return ( return (
<> <>
......
import { import {
Box, Box,
Button, Button,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
...@@ -28,6 +29,7 @@ type Inputs = { ...@@ -28,6 +29,7 @@ type Inputs = {
const TransactionForm: React.FC<Props> = ({ data, onClose }) => { const TransactionForm: React.FC<Props> = ({ data, onClose }) => {
const [ pending, setPending ] = useState(false); const [ pending, setPending ] = useState(false);
const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>(); const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>();
const formBackgroundColor = useColorModeValue('white', 'gray.800');
useEffect(() => { useEffect(() => {
setValue('transaction', data?.transaction_hash || ''); setValue('transaction', data?.transaction_hash || '');
...@@ -68,12 +70,12 @@ const TransactionForm: React.FC<Props> = ({ data, onClose }) => { ...@@ -68,12 +70,12 @@ const TransactionForm: React.FC<Props> = ({ data, onClose }) => {
}; };
const renderTransactionInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'transaction'>}) => { const renderTransactionInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'transaction'>}) => {
return <TransactionInput field={ field } isInvalid={ Boolean(errors.transaction) }/>; return <TransactionInput field={ field } isInvalid={ Boolean(errors.transaction) } backgroundColor={ formBackgroundColor }/>;
}, [ errors ]); }, [ errors, formBackgroundColor ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => { const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput field={ field } isInvalid={ Boolean(errors.tag) }/>; return <TagInput field={ field } isInvalid={ Boolean(errors.tag) } backgroundColor={ formBackgroundColor }/>;
}, [ errors ]); }, [ errors, formBackgroundColor ]);
return ( return (
<> <>
......
...@@ -12,7 +12,7 @@ interface Props { ...@@ -12,7 +12,7 @@ interface Props {
export default function PublicTagFormComment({ control }: Props) { export default function PublicTagFormComment({ control }: Props) {
const renderComment = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'comment'>}) => { const renderComment = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'comment'>}) => {
return ( return (
<FormControl variant="floating" id={ field.name }> <FormControl variant="floating" id={ field.name } size="lg">
<Textarea <Textarea
{ ...field } { ...field }
size="lg" size="lg"
......
...@@ -13,7 +13,7 @@ interface Props<TInputs extends FieldValues> { ...@@ -13,7 +13,7 @@ interface Props<TInputs extends FieldValues> {
export default function PublicTagsFormInput<Inputs extends FieldValues>({ label, control, required, fieldName }: Props<Inputs>) { export default function PublicTagsFormInput<Inputs extends FieldValues>({ label, control, required, fieldName }: Props<Inputs>) {
const renderInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, typeof fieldName>}) => { const renderInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, typeof fieldName>}) => {
return ( return (
<FormControl variant="floating" id={ field.name } isRequired={ required }> <FormControl variant="floating" id={ field.name } isRequired={ required } size="lg">
<Input <Input
{ ...field } { ...field }
size="lg" size="lg"
......
...@@ -13,6 +13,7 @@ type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = { ...@@ -13,6 +13,7 @@ type Props<TInputs extends FieldValues, TInputName extends Path<TInputs>> = {
isInvalid: boolean; isInvalid: boolean;
size?: string; size?: string;
placeholder?: string; placeholder?: string;
backgroundColor?: string;
} }
export default function AddressInput<Inputs extends FieldValues, Name extends Path<Inputs>>( export default function AddressInput<Inputs extends FieldValues, Name extends Path<Inputs>>(
...@@ -21,9 +22,10 @@ export default function AddressInput<Inputs extends FieldValues, Name extends Pa ...@@ -21,9 +22,10 @@ export default function AddressInput<Inputs extends FieldValues, Name extends Pa
isInvalid, isInvalid,
size, size,
placeholder = 'Address (0x...)', placeholder = 'Address (0x...)',
backgroundColor,
}: Props<Inputs, Name>) { }: Props<Inputs, Name>) {
return ( return (
<FormControl variant="floating" id="address" isRequired> <FormControl variant="floating" id="address" isRequired backgroundColor={ backgroundColor } size={ size }>
<Input <Input
{ ...field } { ...field }
isInvalid={ isInvalid } isInvalid={ isInvalid }
......
...@@ -11,11 +11,12 @@ const TAG_MAX_LENGTH = 35; ...@@ -11,11 +11,12 @@ const TAG_MAX_LENGTH = 35;
type Props<Field> = { type Props<Field> = {
field: Field; field: Field;
isInvalid: boolean; isInvalid: boolean;
backgroundColor?: string;
} }
function TagInput<Field extends Partial<ControllerRenderProps<FieldValues, 'tag'>>>({ field, isInvalid }: Props<Field>) { function TagInput<Field extends Partial<ControllerRenderProps<FieldValues, 'tag'>>>({ field, isInvalid, backgroundColor }: Props<Field>) {
return ( return (
<FormControl variant="floating" id="tag" isRequired> <FormControl variant="floating" id="tag" isRequired backgroundColor={ backgroundColor }>
<Input <Input
{ ...field } { ...field }
isInvalid={ isInvalid } isInvalid={ isInvalid }
......
...@@ -11,11 +11,12 @@ const HASH_LENGTH = 66; ...@@ -11,11 +11,12 @@ const HASH_LENGTH = 66;
type Props<Field> = { type Props<Field> = {
field: Field; field: Field;
isInvalid: boolean; isInvalid: boolean;
backgroundColor?: string;
} }
function AddressInput<Field extends Partial<ControllerRenderProps<FieldValues, 'transaction'>>>({ field, isInvalid }: Props<Field>) { function AddressInput<Field extends Partial<ControllerRenderProps<FieldValues, 'transaction'>>>({ field, isInvalid, backgroundColor }: Props<Field>) {
return ( return (
<FormControl variant="floating" id="transaction" isRequired> <FormControl variant="floating" id="transaction" isRequired backgroundColor={ backgroundColor }>
<Input <Input
{ ...field } { ...field }
isInvalid={ isInvalid } isInvalid={ isInvalid }
......
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
Text, Text,
Grid, Grid,
GridItem, GridItem,
useColorModeValue,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useCallback, useEffect } from 'react'; import React, { useCallback, useEffect } from 'react';
import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form'; import type { SubmitHandler, ControllerRenderProps } from 'react-hook-form';
...@@ -30,6 +31,7 @@ type Inputs = { ...@@ -30,6 +31,7 @@ type Inputs = {
const AddressForm: React.FC<Props> = ({ data }) => { const AddressForm: React.FC<Props> = ({ data }) => {
const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>(); const { control, handleSubmit, formState: { errors }, setValue } = useForm<Inputs>();
const formBackgroundColor = useColorModeValue('white', 'gray.800');
useEffect(() => { useEffect(() => {
setValue('address', data?.address || ''); setValue('address', data?.address || '');
...@@ -41,12 +43,12 @@ const AddressForm: React.FC<Props> = ({ data }) => { ...@@ -41,12 +43,12 @@ const AddressForm: React.FC<Props> = ({ data }) => {
const onSubmit: SubmitHandler<Inputs> = data => console.log(data); const onSubmit: SubmitHandler<Inputs> = data => console.log(data);
const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => { const renderAddressInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'address'>}) => {
return <AddressInput<Inputs, 'address'> field={ field } isInvalid={ Boolean(errors.address) }/>; return <AddressInput<Inputs, 'address'> field={ field } isInvalid={ Boolean(errors.address) } backgroundColor={ formBackgroundColor }/>;
}, [ errors ]); }, [ errors, formBackgroundColor ]);
const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => { const renderTagInput = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'tag'>}) => {
return <TagInput field={ field } isInvalid={ Boolean(errors.tag) }/>; return <TagInput field={ field } isInvalid={ Boolean(errors.tag) } backgroundColor={ formBackgroundColor }/>;
}, [ errors ]); }, [ errors, formBackgroundColor ]);
const renderCheckbox = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'notification'>}) => ( const renderCheckbox = useCallback(({ field }: {field: ControllerRenderProps<Inputs, 'notification'>}) => (
<Checkbox <Checkbox
......
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