Created
August 6, 2021 09:52
-
-
Save maxichrome/ce9607393f61d12002513db00313000c to your computer and use it in GitHub Desktop.
I made a nice input component 😸
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { ComponentPropsWithRef } from "react"; | |
import { RiAlertLine } from "react-icons/ri"; | |
import styled, { css } from "styled-components"; | |
export interface TextInputProps | |
extends ComponentPropsWithRef<typeof InnerInput> { | |
label: string; | |
id: string; | |
error?: string; | |
} | |
export const TextInput: React.FC<TextInputProps> = (props) => { | |
const { style, className, label, id: inputId, error, value, ...rest } = props; | |
const labelId = `${inputId}.label`; | |
const errorId = `${inputId}.error`; | |
const innerInput = React.useRef<HTMLInputElement>(); | |
React.useEffect(() => { | |
if (!innerInput.current) return; | |
innerInput.current.value = value; | |
}, [value]); | |
return ( | |
<OuterContainer {...{ style, className }}> | |
<InputContainer> | |
<InnerInput | |
id={inputId} | |
aria-labelledby={labelId} | |
aria-describedby={error ? errorId : undefined} | |
ref={innerInput} | |
value={value} | |
error={!!error} | |
{...rest} | |
/> | |
<Label id={labelId} htmlFor={inputId} aria-hidden> | |
{label} | |
</Label> | |
</InputContainer> | |
{error && ( | |
<ErrorContainer id={errorId}> | |
<RiAlertLine size={18} /> | |
{error} | |
</ErrorContainer> | |
)} | |
</OuterContainer> | |
); | |
}; | |
const ErrorContainer = styled.div` | |
display: flex; | |
flex-flow: row nowrap; | |
justify-content: center; | |
align-items: center; | |
margin-top: 8px; | |
color: red; | |
> svg { | |
margin-right: 6px; | |
} | |
`; | |
const Label = styled.label` | |
font-size: 1em; | |
position: absolute; | |
left: 1.125rem; | |
top: 1.1rem; | |
margin: auto 0; | |
color: ${(props) => props.theme.text.lighter}; | |
transform: none; | |
transition: all 0.1s linear; | |
pointer-events: none; | |
`; | |
const InnerInput = styled.input<{ error?: boolean }>` | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
padding: 1rem 0 0 1rem; | |
font: inherit; | |
font-size: 1rem; | |
line-height: 1rem; | |
background: white; | |
outline: none; | |
border: 2px solid #d7d7d7; | |
border-radius: 5px; | |
transition: all 0.2s ease; | |
&:hover { | |
background: #f0f0f0; | |
border-color: ${(props) => props.theme.darkerAccentColor}; | |
} | |
&:focus { | |
background: white; | |
border-color: ${(props) => props.theme.accentColor}; | |
} | |
&:not([value=""]) + label, | |
&:focus + label { | |
transform: translateY(-0.85em); | |
font-size: 12px; | |
} | |
${(props) => | |
props.error && | |
css` | |
border-color: red; | |
`} | |
`; | |
const OuterContainer = styled.div` | |
display: flex; | |
flex-flow: column nowrap; | |
align-items: flex-start; | |
justify-content: flex-start; | |
`; | |
const InputContainer = styled.div` | |
display: block; | |
width: 100%; | |
min-height: 100%; | |
position: relative; | |
`; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment