import React, {InputHTMLAttributes, forwardRef} from 'react' import {IconElement, Icon} from '../data/icon' import {themeMiddleware, Theme} from '../style/themeContext' import { FontSize, TransitionDuration, LineHeight, Spacing, MarginProps, extractStyleProps, WidthProps, FlexChildProps } from '../style/helpers' import {cssRule, styled} from '@karma.run/react' interface TextInputStyleProps { readonly hasError: boolean readonly hasIcon: boolean readonly theme: Theme } interface TextInputLayoutProps extends MarginProps, WidthProps, FlexChildProps {} const IconStyle = cssRule(() => ({ position: 'absolute' })) const TextInputWrapper = styled('div', (props: TextInputLayoutProps) => ({ paddingTop: 16, ...props })) const TextInputLabelWrapper = styled( 'label', ({theme}) => ({ position: 'relative', display: 'flex', alignItems: 'center', fontSize: FontSize.Medium, fill: theme.colors.dark }), themeMiddleware ) const TextInputLabel = styled( 'span', ({hasError, theme}: TextInputStyleProps) => ({ color: hasError ? theme.colors.alert : theme.colors.grayDark, position: 'absolute', top: -FontSize.Medium, left: 0, fontSize: FontSize.Small, opacity: 1, transform: 'translateY(0%)', transitionProperty: 'transform, opacity, color', transitionTimingFunction: 'ease-in-out', transitionDuration: TransitionDuration.Slow }), themeMiddleware ) const TextInputElement = styled( 'input', ({hasIcon, theme}: TextInputStyleProps) => ({ width: '100%', color: theme.colors.dark, fontFamily: 'inherit', fontSize: 'inherit', lineHeight: LineHeight.Default, border: 'none', borderBottom: `1px solid ${theme.colors.gray}`, backgroundColor: 'transparent', transitionProperty: 'border-color', transitionTimingFunction: 'ease-in', transitionDuration: TransitionDuration.Slow, paddingLeft: hasIcon ? FontSize.Medium + Spacing.Tiny : undefined, '::placeholder': { color: theme.colors.gray }, ':focus': { outline: 'none', borderColor: theme.colors.action }, ':focus:valid': { borderColor: theme.colors.action }, ':focus:valid + span': { color: theme.colors.action }, ':focus:invalid': { borderColor: theme.colors.alert }, ':focus:invalid + span': { color: theme.colors.alert }, ':invalid': { borderColor: theme.colors.alert }, ':disabled': { opacity: 0.5, borderBottomStyle: 'dashed' }, ':invalid + span': { color: theme.colors.alert }, ':placeholder-shown + span': { opacity: 0, transform: 'translateY(30%)' }, ':focus + span': { color: theme.colors.action } }), themeMiddleware ) const TextInputInfo = styled( 'div', ({theme}) => ({ color: theme.colors.gray, fontSize: FontSize.Small, marginTop: Spacing.Tiny }), themeMiddleware ) const TextInputError = styled( 'div', ({theme}) => ({ color: theme.colors.alert, fontSize: FontSize.Small, marginTop: Spacing.Tiny }), themeMiddleware ) export interface TextInputProps extends MarginProps, WidthProps, FlexChildProps, Omit, 'width'> { readonly label?: string readonly description?: string readonly errorMessage?: string readonly icon?: IconElement } export const TextInput = forwardRef(function TextInput( {label, description, errorMessage, icon, ...props}, ref ) { const styleProps = {hasError: errorMessage != undefined, hasIcon: icon != undefined} const [layoutProps, elementProps] = extractStyleProps(props) return ( {icon && } {label} {description && {description}} {errorMessage && {errorMessage}} ) })