Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adjust TextInput #3108

Merged
merged 11 commits into from
May 30, 2022
801 changes: 421 additions & 380 deletions example/src/Examples/TextInputExample.tsx

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion src/components/TextInput/Adornment/TextInputAdornment.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import TextInputIcon, { IconAdornment } from './TextInputIcon';
import TextInputAffix, { AffixAdornment } from './TextInputAffix';
import { ADORNMENT_OFFSET, OUTLINED_INPUT_OFFSET } from '../constants';
import type {
LayoutChangeEvent,
TextStyle,
Expand All @@ -13,6 +12,7 @@ import type {
AdornmentStyleAdjustmentForNativeInput,
} from './types';
import { AdornmentSide, AdornmentType, InputMode } from './enums';
import { getConstants } from '../helpers';

export function getAdornmentConfig({
left,
Expand Down Expand Up @@ -52,14 +52,18 @@ export function getAdornmentStyleAdjustmentForNativeInput({
paddingHorizontal,
inputOffset = 0,
mode,
isV3,
}: {
inputOffset?: number;
adornmentConfig: AdornmentConfig[];
leftAffixWidth: number;
rightAffixWidth: number;
mode?: 'outlined' | 'flat';
paddingHorizontal?: number | string;
isV3?: boolean;
}): AdornmentStyleAdjustmentForNativeInput | {} {
const { OUTLINED_INPUT_OFFSET, ADORNMENT_OFFSET } = getConstants(isV3);

if (adornmentConfig.length) {
const adornmentStyleAdjustmentForNativeInput = adornmentConfig.map(
({ type, side }: AdornmentConfig) => {
Expand Down
5 changes: 3 additions & 2 deletions src/components/TextInput/Adornment/TextInputAffix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import {
import { withTheme } from '../../../core/theming';
import { AdornmentSide } from './enums';
import type { Theme } from '../../../types';

const AFFIX_OFFSET = 12;
import { getConstants } from '../helpers';

export type Props = {
/**
Expand Down Expand Up @@ -109,6 +108,8 @@ const AffixAdornment: React.FunctionComponent<
*/

const TextInputAffix = ({ text, textStyle: labelStyle, theme }: Props) => {
const { AFFIX_OFFSET } = getConstants(theme.isV3);

const { textStyle, onLayout, topPosition, side, visible, paddingHorizontal } =
React.useContext(AffixContext);
const textColor = color(
Expand Down
26 changes: 22 additions & 4 deletions src/components/TextInput/Adornment/TextInputIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { View, StyleSheet, StyleProp, ViewStyle } from 'react-native';
import IconButton from '../../IconButton';
import type { $Omit, Theme } from '../../../types';
import type { IconSource } from '../../Icon';
import { useTheme } from '../../../core/theming';
import { getConstants } from '../helpers';
import { ICON_SIZE } from '../constants';

export type Props = $Omit<
React.ComponentProps<typeof IconButton>,
Expand Down Expand Up @@ -32,9 +35,6 @@ export type Props = $Omit<
theme?: Theme;
};

export const ICON_SIZE = 24;
const ICON_OFFSET = 12;

type StyleContextType = {
style: StyleProp<ViewStyle>;
isTextInputFocused: boolean;
Expand All @@ -55,6 +55,9 @@ const IconAdornment: React.FunctionComponent<
side: 'left' | 'right';
} & Omit<StyleContextType, 'style'>
> = ({ icon, topPosition, side, isTextInputFocused, forceFocus }) => {
const { isV3 } = useTheme();
const { ICON_OFFSET } = getConstants(isV3);
lukewalczak marked this conversation as resolved.
Show resolved Hide resolved

const style = {
top: topPosition,
[side]: ICON_OFFSET,
Expand Down Expand Up @@ -113,14 +116,29 @@ const TextInputIcon = ({
onPress?.();
}, [forceTextInputFocus, forceFocus, isTextInputFocused, onPress]);

const theme = useTheme();

let iconColor = color;

if (theme.isV3) {
if (rest.disabled) {
iconColor = theme.colors.onSurface;
}
iconColor = theme.colors.onSurfaceVariant;
} else {
iconColor = theme.colors.text;
}

return (
<View style={[styles.container, style]}>
<IconButton
icon={name}
style={styles.iconButton}
size={ICON_SIZE}
onPress={onPressWithFocusControl}
color={typeof color === 'function' ? color(isTextInputFocused) : color}
color={
typeof color === 'function' ? color(isTextInputFocused) : iconColor
}
{...rest}
/>
</View>
Expand Down
23 changes: 20 additions & 3 deletions src/components/TextInput/Label/InputLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from 'react';
import { Animated, StyleSheet } from 'react-native';
import AnimatedText from '../../Typography/AnimatedText';
import { useTheme } from '../../../core/theming';

import type { InputLabelProps } from '../types';

const InputLabel = (props: InputLabelProps) => {
const { parentState, labelBackground } = props;

const { isV3 } = useTheme();
const { parentState, labelBackground, mode } = props;
const {
label,
error,
Expand Down Expand Up @@ -70,6 +71,15 @@ const InputLabel = (props: InputLabelProps) => {
],
};

let textColor = placeholderColor;

if (error && errorColor) {
textColor = errorColor;
}
if (isV3 && parentState.value && mode !== 'outlined') {
textColor = activeColor;
}

return label ? (
// Position colored placeholder and gray placeholder on top of each other and crossfade them
// This gives the effect of animating the color, but allows us to use native driver
Expand All @@ -96,6 +106,7 @@ const InputLabel = (props: InputLabelProps) => {
labelProps: props.labelProps,
})}
<AnimatedText
variant="bodySmall"
onLayout={onLayoutAnimatedText}
style={[
placeholderStyle,
Expand All @@ -111,12 +122,14 @@ const InputLabel = (props: InputLabelProps) => {
outputRange: [hasActiveOutline ? 1 : 0, 0],
}),
},
isV3 && styles.md3TextLine,
]}
numberOfLines={1}
>
{label}
</AnimatedText>
<AnimatedText
variant={parentState.focused ? 'bodyLarge' : 'bodySmall'}
style={[
placeholderStyle,
{
Expand All @@ -125,9 +138,10 @@ const InputLabel = (props: InputLabelProps) => {
labelStyle,
paddingOffset,
{
color: error && errorColor ? errorColor : placeholderColor,
color: textColor,
opacity: placeholderOpacity,
},
isV3 && styles.md3TextLine,
]}
numberOfLines={1}
>
Expand All @@ -141,6 +155,9 @@ const styles = StyleSheet.create({
labelContainer: {
zIndex: 3,
},
md3TextLine: {
lineHeight: undefined,
},
});

export default InputLabel;
61 changes: 38 additions & 23 deletions src/components/TextInput/Label/LabelBackground.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { Animated, StyleSheet } from 'react-native';
import { useTheme } from '../../../core/theming';

import AnimatedText from '../../Typography/AnimatedText';

Expand All @@ -24,17 +25,37 @@ const LabelBackground = ({
outputRange: [hasFocus ? 1 : 0, 0],
});

const { isV3, colors } = useTheme();

const labelTranslationX = {
transform: [
{
translateX: parentState.labeled.interpolate({
inputRange: [0, 1],
outputRange: [-baseLabelTranslateX, 0],
}),
},
],
translateX: parentState.labeled.interpolate({
inputRange: [0, 1],
outputRange: [-baseLabelTranslateX, 0],
}),
};

const labelTextScaleY = {
scaleY: parentState.labeled.interpolate({
inputRange: [0, 1],
outputRange: [0.2, 1],
}),
};

const labelTextTransform = isV3
? [...labelStyle.transform]
: [...labelStyle.transform, labelTextScaleY];

const labelTextWidth = isV3
? {
width:
parentState.labelLayout.width - placeholderStyle.paddingHorizontal,
}
: {
maxWidth:
parentState.labelLayout.width -
2 * placeholderStyle.paddingHorizontal,
};

return label
? [
<Animated.View
Expand All @@ -48,8 +69,8 @@ const LabelBackground = ({
maxHeight: Math.max(roundness / 3, 2),
opacity,
bottom: Math.max(roundness, 2),
transform: [labelTranslationX],
},
labelTranslationX,
]}
/>,
<AnimatedText
Expand All @@ -58,23 +79,14 @@ const LabelBackground = ({
placeholderStyle,
labelStyle,
styles.outlinedLabel,
isV3 && styles.md3OutlinedLabel,
{
top: topPosition + 1,
backgroundColor,
top: topPosition + (isV3 ? 0 : 1),
backgroundColor: isV3 ? colors.surface : backgroundColor,
opacity,
transform: [
...labelStyle.transform,
{
scaleY: parentState.labeled.interpolate({
inputRange: [0, 1],
outputRange: [0.2, 1],
}),
},
],
maxWidth:
parentState.labelLayout.width -
2 * placeholderStyle.paddingHorizontal,
transform: labelTextTransform,
},
labelTextWidth,
]}
numberOfLines={1}
>
Expand All @@ -99,4 +111,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 0,
color: 'transparent',
},
md3OutlinedLabel: {
left: 8,
},
});
Loading