Skip to content

Instantly share code, notes, and snippets.

@maxichrome
Created August 6, 2021 09:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maxichrome/ce9607393f61d12002513db00313000c to your computer and use it in GitHub Desktop.
Save maxichrome/ce9607393f61d12002513db00313000c to your computer and use it in GitHub Desktop.
I made a nice input component 😸
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