mirror of
https://github.com/minibits-cash/minibits_wallet.git
synced 2026-08-10 00:27:00 +00:00
276 lines
7.2 KiB
TypeScript
276 lines
7.2 KiB
TypeScript
import React, { ComponentType, forwardRef, Ref, useImperativeHandle, useRef } from "react"
|
|
import {
|
|
StyleProp,
|
|
TextInput,
|
|
TextInputProps,
|
|
TextStyle,
|
|
TouchableOpacity,
|
|
View,
|
|
ViewStyle,
|
|
} from "react-native"
|
|
import { isRTL, translate } from "../i18n"
|
|
import { colors, spacing, typography } from "../theme"
|
|
import { Text, TextProps } from "./Text"
|
|
|
|
export interface TextFieldAccessoryProps {
|
|
style: StyleProp<any>
|
|
status: TextFieldProps["status"]
|
|
multiline: boolean
|
|
editable: boolean
|
|
}
|
|
|
|
export interface TextFieldProps extends Omit<TextInputProps, "ref"> {
|
|
/**
|
|
* A style modifier for different input states.
|
|
*/
|
|
status?: "error" | "disabled"
|
|
/**
|
|
* The label text to display if not using `labelTx`.
|
|
*/
|
|
label?: TextProps["text"]
|
|
/**
|
|
* Label text which is looked up via i18n.
|
|
*/
|
|
labelTx?: TextProps["tx"]
|
|
/**
|
|
* Optional label options to pass to i18n. Useful for interpolation
|
|
* as well as explicitly setting locale or translation fallbacks.
|
|
*/
|
|
labelTxOptions?: TextProps["txOptions"]
|
|
/**
|
|
* Pass any additional props directly to the label Text component.
|
|
*/
|
|
LabelTextProps?: TextProps
|
|
/**
|
|
* The helper text to display if not using `helperTx`.
|
|
*/
|
|
helper?: TextProps["text"]
|
|
/**
|
|
* Helper text which is looked up via i18n.
|
|
*/
|
|
helperTx?: TextProps["tx"]
|
|
/**
|
|
* Optional helper options to pass to i18n. Useful for interpolation
|
|
* as well as explicitly setting locale or translation fallbacks.
|
|
*/
|
|
helperTxOptions?: TextProps["txOptions"]
|
|
/**
|
|
* Pass any additional props directly to the helper Text component.
|
|
*/
|
|
HelperTextProps?: TextProps
|
|
/**
|
|
* The placeholder text to display if not using `placeholderTx`.
|
|
*/
|
|
placeholder?: TextProps["text"]
|
|
/**
|
|
* Placeholder text which is looked up via i18n.
|
|
*/
|
|
placeholderTx?: TextProps["tx"]
|
|
/**
|
|
* Optional placeholder options to pass to i18n. Useful for interpolation
|
|
* as well as explicitly setting locale or translation fallbacks.
|
|
*/
|
|
placeholderTxOptions?: TextProps["txOptions"]
|
|
/**
|
|
* Optional input style override.
|
|
*/
|
|
style?: StyleProp<TextStyle>
|
|
/**
|
|
* Style overrides for the container
|
|
*/
|
|
containerStyle?: StyleProp<ViewStyle>
|
|
/**
|
|
* Style overrides for the input wrapper
|
|
*/
|
|
inputWrapperStyle?: StyleProp<ViewStyle>
|
|
/**
|
|
* An optional component to render on the right side of the input.
|
|
* Example: `RightAccessory={(props) => <Icon icon="ladybug" containerStyle={props.style} color={props.editable ? colors.textDim : colors.text} />}`
|
|
* Note: It is a good idea to memoize this.
|
|
*/
|
|
RightAccessory?: ComponentType<TextFieldAccessoryProps>
|
|
/**
|
|
* An optional component to render on the left side of the input.
|
|
* Example: `LeftAccessory={(props) => <Icon icon="ladybug" containerStyle={props.style} color={props.editable ? colors.textDim : colors.text} />}`
|
|
* Note: It is a good idea to memoize this.
|
|
*/
|
|
LeftAccessory?: ComponentType<TextFieldAccessoryProps>
|
|
}
|
|
|
|
/**
|
|
* A component that allows for the entering and editing of text.
|
|
*
|
|
* - [Documentation and Examples](https://github.com/infinitered/ignite/blob/master/docs/Components-TextField.md)
|
|
*/
|
|
export const TextField = forwardRef(function TextField(props: TextFieldProps, ref: Ref<TextInput>) {
|
|
const {
|
|
labelTx,
|
|
label,
|
|
labelTxOptions,
|
|
placeholderTx,
|
|
placeholder,
|
|
placeholderTxOptions,
|
|
helper,
|
|
helperTx,
|
|
helperTxOptions,
|
|
status,
|
|
RightAccessory,
|
|
LeftAccessory,
|
|
HelperTextProps,
|
|
LabelTextProps,
|
|
style: $inputStyleOverride,
|
|
containerStyle: $containerStyleOverride,
|
|
inputWrapperStyle: $inputWrapperStyleOverride,
|
|
...TextInputProps
|
|
} = props
|
|
const input = useRef<TextInput>()
|
|
|
|
const disabled = TextInputProps.editable === false || status === "disabled"
|
|
|
|
const placeholderContent = placeholderTx
|
|
? translate(placeholderTx, placeholderTxOptions)
|
|
: placeholder
|
|
|
|
const $containerStyles = [$containerStyleOverride]
|
|
|
|
const $labelStyles = [$labelStyle, LabelTextProps?.style]
|
|
|
|
const $inputWrapperStyles = [
|
|
$inputWrapperStyle,
|
|
status === "error" && { borderColor: colors.light.error },
|
|
TextInputProps.multiline && { minHeight: 112 },
|
|
LeftAccessory && { paddingStart: 0 },
|
|
RightAccessory && { paddingEnd: 0 },
|
|
$inputWrapperStyleOverride,
|
|
]
|
|
|
|
const $inputStyles = [
|
|
$inputStyle,
|
|
disabled && { color: colors.light.textDim },
|
|
isRTL && { textAlign: "right" as TextStyle["textAlign"] },
|
|
TextInputProps.multiline && { height: "auto" },
|
|
$inputStyleOverride,
|
|
]
|
|
|
|
const $helperStyles = [
|
|
$helperStyle,
|
|
status === "error" && { color: colors.light.error },
|
|
HelperTextProps?.style,
|
|
]
|
|
|
|
function focusInput() {
|
|
if (disabled) return
|
|
|
|
input.current?.focus()
|
|
}
|
|
|
|
useImperativeHandle(ref, () => input.current)
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
activeOpacity={1}
|
|
style={$containerStyles}
|
|
onPress={focusInput}
|
|
accessibilityState={{ disabled }}
|
|
>
|
|
{!!(label || labelTx) && (
|
|
<Text
|
|
preset="formLabel"
|
|
text={label}
|
|
tx={labelTx}
|
|
txOptions={labelTxOptions}
|
|
{...LabelTextProps}
|
|
style={$labelStyles}
|
|
/>
|
|
)}
|
|
|
|
<View style={$inputWrapperStyles}>
|
|
{!!LeftAccessory && (
|
|
<LeftAccessory
|
|
style={$leftAccessoryStyle}
|
|
status={status}
|
|
editable={!disabled}
|
|
multiline={TextInputProps.multiline}
|
|
/>
|
|
)}
|
|
|
|
<TextInput
|
|
ref={input}
|
|
underlineColorAndroid={colors.light.transparent}
|
|
textAlignVertical="top"
|
|
placeholder={placeholderContent}
|
|
placeholderTextColor={colors.light.textDim}
|
|
{...TextInputProps}
|
|
editable={!disabled}
|
|
style={$inputStyles}
|
|
/>
|
|
|
|
{!!RightAccessory && (
|
|
<RightAccessory
|
|
style={$rightAccessoryStyle}
|
|
status={status}
|
|
editable={!disabled}
|
|
multiline={TextInputProps.multiline}
|
|
/>
|
|
)}
|
|
</View>
|
|
|
|
{!!(helper || helperTx) && (
|
|
<Text
|
|
preset="formHelper"
|
|
text={helper}
|
|
tx={helperTx}
|
|
txOptions={helperTxOptions}
|
|
{...HelperTextProps}
|
|
style={$helperStyles}
|
|
/>
|
|
)}
|
|
</TouchableOpacity>
|
|
)
|
|
})
|
|
|
|
const $labelStyle: TextStyle = {
|
|
marginBottom: spacing.extraSmall,
|
|
}
|
|
|
|
const $inputWrapperStyle: ViewStyle = {
|
|
flexDirection: "row",
|
|
alignItems: "flex-start",
|
|
borderWidth: 1,
|
|
borderRadius: 4,
|
|
backgroundColor: colors.palette.neutral200,
|
|
borderColor: colors.palette.neutral400,
|
|
overflow: "hidden",
|
|
}
|
|
|
|
const $inputStyle: TextStyle = {
|
|
flex: 1,
|
|
alignSelf: "stretch",
|
|
fontFamily: typography.primary?.normal,
|
|
color: colors.light.text,
|
|
fontSize: 16,
|
|
height: 24,
|
|
// https://github.com/facebook/react-native/issues/21720#issuecomment-532642093
|
|
paddingVertical: 0,
|
|
paddingHorizontal: 0,
|
|
marginVertical: spacing.extraSmall,
|
|
marginHorizontal: spacing.small,
|
|
}
|
|
|
|
const $helperStyle: TextStyle = {
|
|
marginTop: spacing.extraSmall,
|
|
}
|
|
|
|
const $rightAccessoryStyle: ViewStyle = {
|
|
marginEnd: spacing.extraSmall,
|
|
height: 40,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
}
|
|
const $leftAccessoryStyle: ViewStyle = {
|
|
marginStart: spacing.extraSmall,
|
|
height: 40,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
}
|