import { YStack } from 'tamagui'
import { MnInput } from 'yourpath/ui'
import { Eye, User2 } from '@tamagui/lucide-icons'
const DemoInput = () => {
return (
<>
<YStack minHeight={250} overflow="hidden" gap="$4" margin="$3" padding="$2">
<MnInput />
<MnInput iconLeft={User2} />
<MnInput iconRight={Eye} roundedBorder />
<MnInput helperText="Tamagui is great" placeholder="Placeholder" />
<MnInput errorText="This is not correct" placeholder="Placeholder" error />
<MnInput roundedBorder />
</YStack>
</>
)
}
Props
MnInput Code
import { getSpace } from '@tamagui/get-token'
import { SizeTokens, styled, ThemeableStack } from 'tamagui'
export const IconContainer = styled(ThemeableStack, {
name: 'IconContainer',
justifyContent: 'center',
backgroundColor: '$backgroundColor',
variants: {
unstyled: {
false: {
size: '$true',
backgroundColor: '$background',
borderRadius: 0,
},
},
size: {
'...size': (val: SizeTokens, { tokens }) => {
return {
paddingHorizontal: getSpace(val, {
shift: -4,
}),
}
},
},
disabled: {
true: {
opacity: 0.5,
// TODO breaking types
pointerEvents: 'none' as any,
},
},
} as const,
defaultVariants: {
unstyled: process.env.TAMAGUI_HEADLESS === '1',
},
})
import { styled, Input } from 'tamagui'
export const StyledInput = styled(Input, {
borderColor: 'transparent',
flex: 1,
focusVisibleStyle: {
outlineColor: '$outlineColor',
outlineWidth: 0,
outlineStyle: 'solid',
},
focusStyle: {
borderColor: 'transparent',
},
hoverStyle: {
borderColor: 'transparent',
},
})
import React, { FunctionComponent, useState } from 'react'
import {
composeEventHandlers,
getFontSize,
SizableText,
styled,
InputProps as TamaInputProps,
useComposedRefs,
useGetThemedIcon,
useProps,
XGroup,
YStack,
} from 'tamagui'
import { IconContainer } from './IconContainer'
import { StyledInput } from './StyledInput'
type MnInputVariant = 'outlined'
type InputComponentIconProps = { color?: any; size?: any }
type IconProp = JSX.Element | FunctionComponent<InputComponentIconProps> | null
interface MnInputProps extends TamaInputProps {
iconLeft?: IconProp
iconRight?: IconProp
scaleIcon?: number
scaleSpace?: number
variant?: MnInputVariant
roundedBorder?: boolean
error?: boolean
helperText?: string
errorText?: string
}
const XGroupStyled = styled(XGroup, {
name: 'Input',
borderStyle: 'solid',
borderWidth: '$0.5',
borderColor: '$borderColor',
overflow: 'hidden',
variants: {
unstyled: {
false: {
height: 'auto',
borderRadius: 0,
hoverStyle: {
borderColor: '$color7',
},
},
},
variant: {
outlined: {
borderColor: '$borderColor',
},
},
roundedBorder: {
true: {
borderRadius: '$2',
},
},
isFocused: {
true: {
// This should have been the outlineColor, need to understand the theme better
outlineColor: '$color7',
outlineWidth: 2,
outlineStyle: 'solid',
},
},
isError: {
true: {
borderColor: 'red',
outlineColor: 'red',
hoverStyle: {
borderColor: 'red',
},
},
},
} as const,
defaultVariants: {
unstyled: process.env.TAMAGUI_HEADLESS === '1',
},
})
export const MnInput = React.forwardRef((inProps: MnInputProps, forwardedRef) => {
const {
onFocus,
onBlur,
variant = 'outlined',
iconLeft,
iconRight,
scaleIcon = 1,
scaleSpace = 1,
helperText,
errorText,
roundedBorder = false,
error = false,
color,
space,
...rest
} = useProps(inProps)
const [isFocused, setIsFocused] = useState(false)
const ref = React.useRef<HTMLInputElement>(null)
const composedRefs = useComposedRefs<any>(forwardedRef, ref)
const size = inProps.size || '$true'
const iconSize = getFontSize(size as any) * scaleIcon
const getThemedIcon = useGetThemedIcon({ size: iconSize, color: color as any })
const [themedIconLeft, themedIconRight] = [iconLeft, iconRight].map(getThemedIcon)
return (
<YStack>
<XGroupStyled
testID="mn-input"
variant={variant}
isFocused={isFocused}
isError={error}
roundedBorder={roundedBorder}
size={'$0'}
>
<XGroup.Item>
{themedIconLeft && <IconContainer>{themedIconLeft}</IconContainer>}
</XGroup.Item>
<XGroup.Item>
<StyledInput
ref={composedRefs}
onFocus={composeEventHandlers(onFocus, () => {
setIsFocused(true)
})}
onBlur={composeEventHandlers(onBlur, () => {
setIsFocused(false)
})}
{...(iconLeft && { paddingLeft: 0 })}
{...(iconRight && { paddingRight: 0 })}
height={'auto'}
{...(error && { placeholderTextColor: '0px' })}
{...rest}
/>
</XGroup.Item>
<XGroup.Item>
{themedIconRight && <IconContainer>{themedIconRight}</IconContainer>}
</XGroup.Item>
</XGroupStyled>
{helperText && (
<SizableText theme="alt1" size="$1">
{helperText}
</SizableText>
)}
{error && (
<SizableText theme="red_alt1" size="$1">
{errorText}
</SizableText>
)}
</YStack>
)
})