Skip to content

Commit ebd9fb6

Browse files
authored
Fix issue with TextField floating placeholder offset when it includes a leading accessory (#1700)
1 parent 58f878d commit ebd9fb6

File tree

9 files changed

+72
-6
lines changed

9 files changed

+72
-6
lines changed

demo/src/screens/incubatorScreens/IncubatorTextFieldScreen.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,10 @@ export default class TextFieldScreen extends Component {
9898
<TextField
9999
ref={this.input2}
100100
placeholder="Enter URL"
101+
floatingPlaceholder
101102
text70
102103
leadingAccessory={
103-
<Text text70 blue30>
104+
<Text text70 blue30 marginR-2>
104105
Https://
105106
</Text>
106107
}

generatedTypes/src/hooks/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export { default as useCombinedRefs } from './useCombinedRefs';
22
export { default as useToggleValue } from './useToggleValue';
33
export { default as useDidUpdate } from './useDidUpdate';
4+
export { default as useMeasure } from './useMeasure';
45
export { default as useOrientation } from './useOrientation';
56
export { default as useScrollEnabler } from './useScrollEnabler';
67
export { default as useScrollReached } from './useScrollReached';
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference types="react" />
2+
import { View as RNView } from 'react-native';
3+
interface Measurements {
4+
x: number;
5+
y: number;
6+
width: number;
7+
height: number;
8+
pageX: number;
9+
pageY: number;
10+
}
11+
declare const _default: () => {
12+
ref: import("react").MutableRefObject<RNView | undefined>;
13+
measurements: Measurements | undefined;
14+
};
15+
export default _default;

generatedTypes/src/incubator/TextField/FloatingPlaceholder.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ export interface FloatingPlaceholderProps {
1919
*/
2020
floatOnFocus?: boolean;
2121
validationMessagePosition?: ValidationMessagePosition;
22+
extraOffset?: number;
2223
}
2324
declare const FloatingPlaceholder: {
24-
({ placeholder, floatingPlaceholderColor, floatingPlaceholderStyle, floatOnFocus, validationMessagePosition }: FloatingPlaceholderProps): JSX.Element;
25+
({ placeholder, floatingPlaceholderColor, floatingPlaceholderStyle, floatOnFocus, validationMessagePosition, extraOffset }: FloatingPlaceholderProps): JSX.Element;
2526
displayName: string;
2627
};
2728
export default FloatingPlaceholder;

generatedTypes/src/incubator/TextField/usePreset.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ export default function usePreset({ preset, ...props }: InternalTextFieldProps):
146146
floatingPlaceholderColor?: import("./types").ColorType | undefined;
147147
floatingPlaceholderStyle?: ((false | import("react-native").TextStyle | import("react-native").RegisteredStyle<import("react-native").TextStyle> | import("react-native").RecursiveArray<import("react-native").TextStyle | import("react-native").Falsy | import("react-native").RegisteredStyle<import("react-native").TextStyle>> | null) & import("react-native").TextStyle) | undefined;
148148
floatOnFocus?: boolean | undefined;
149+
extraOffset?: number | undefined;
149150
enableErrors?: boolean | undefined;
150151
validationMessage?: string | string[] | undefined;
151152
validationMessageStyle?: import("react-native").TextStyle | undefined;
@@ -475,6 +476,7 @@ export default function usePreset({ preset, ...props }: InternalTextFieldProps):
475476
floatingPlaceholderColor?: import("./types").ColorType | undefined;
476477
floatingPlaceholderStyle?: ((false | import("react-native").TextStyle | import("react-native").RegisteredStyle<import("react-native").TextStyle> | import("react-native").RecursiveArray<import("react-native").TextStyle | import("react-native").Falsy | import("react-native").RegisteredStyle<import("react-native").TextStyle>> | null) & import("react-native").TextStyle) | undefined;
477478
floatOnFocus?: boolean | undefined;
479+
extraOffset?: number | undefined;
478480
enableErrors?: boolean | undefined;
479481
validationMessage?: string | string[] | undefined;
480482
validationMessageStyle?: import("react-native").TextStyle | undefined;
@@ -1036,6 +1038,7 @@ export default function usePreset({ preset, ...props }: InternalTextFieldProps):
10361038
includeFontPadding?: boolean | undefined;
10371039
};
10381040
floatOnFocus?: boolean | undefined;
1041+
extraOffset?: number | undefined;
10391042
enableErrors: boolean;
10401043
validationMessage?: string | string[] | undefined;
10411044
validationMessageStyle?: import("react-native").TextStyle | undefined;

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export {default as useCombinedRefs} from './useCombinedRefs';
22
export {default as useToggleValue} from './useToggleValue';
33
export {default as useDidUpdate} from './useDidUpdate';
4+
export {default as useMeasure} from './useMeasure';
45
export {default as useOrientation} from './useOrientation';
56
export {default as useScrollEnabler} from './useScrollEnabler';
67
export {default as useScrollReached} from './useScrollReached';

src/hooks/useMeasure/index.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {useEffect, useRef, useState} from 'react';
2+
import {MeasureOnSuccessCallback, View as RNView} from 'react-native';
3+
4+
interface Measurements {
5+
x: number;
6+
y: number;
7+
width: number;
8+
height: number;
9+
pageX: number;
10+
pageY: number;
11+
}
12+
13+
export default () => {
14+
const ref = useRef<RNView>();
15+
const [measurements, setMeasurements] = useState<Measurements | undefined>();
16+
17+
const onMeasure: MeasureOnSuccessCallback = (x, y, width, height, pageX, pageY) => {
18+
setMeasurements({
19+
x,
20+
y,
21+
width,
22+
height,
23+
pageX,
24+
pageY
25+
});
26+
};
27+
28+
useEffect(() => {
29+
setTimeout(() => {
30+
ref.current?.measure?.(onMeasure);
31+
}, 0);
32+
}, []);
33+
34+
return {ref, measurements};
35+
};

src/incubator/TextField/FloatingPlaceholder.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface FloatingPlaceholderProps {
2626
*/
2727
floatOnFocus?: boolean;
2828
validationMessagePosition?: ValidationMessagePosition;
29+
extraOffset?: number;
2930
}
3031

3132
const FLOATING_PLACEHOLDER_SCALE = 0.875;
@@ -35,7 +36,8 @@ const FloatingPlaceholder = ({
3536
floatingPlaceholderColor = Colors.grey40,
3637
floatingPlaceholderStyle,
3738
floatOnFocus,
38-
validationMessagePosition
39+
validationMessagePosition,
40+
extraOffset = 0
3941
}: FloatingPlaceholderProps) => {
4042
const context = useContext(FieldContext);
4143
const [placeholderOffset, setPlaceholderOffset] = useState({
@@ -52,14 +54,17 @@ const FloatingPlaceholder = ({
5254
scale: interpolateValue(animation, [1, FLOATING_PLACEHOLDER_SCALE])
5355
},
5456
{
55-
translateX: interpolateValue(animation, [0, -placeholderOffset.left])
57+
translateX: interpolateValue(animation, [
58+
0,
59+
-placeholderOffset.left - extraOffset / FLOATING_PLACEHOLDER_SCALE
60+
])
5661
},
5762
{
5863
translateY: interpolateValue(animation, [0, -placeholderOffset.top])
5964
}
6065
]
6166
};
62-
}, [placeholderOffset]);
67+
}, [placeholderOffset, extraOffset]);
6368

6469
useEffect(() => {
6570
const toValue = floatOnFocus ? context.isFocused || context.hasValue : context.hasValue;

src/incubator/TextField/index.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
} from '../../commons/new';
2121
import View from '../../components/view';
2222
import {Colors} from '../../style';
23+
import {useMeasure} from '../../hooks';
2324
import {ValidationMessagePosition, Validator} from './types';
2425
import {shouldHidePlaceholder} from './Presenter';
2526
import Input, {InputProps} from './Input';
@@ -151,6 +152,7 @@ const TextField = (props: InternalTextFieldProps) => {
151152
children,
152153
...others
153154
} = usePreset(props);
155+
const {ref: leadingAccessoryRef, measurements: leadingAccessoryMeasurements} = useMeasure();
154156
const {onFocus, onBlur, onChangeText, fieldState, validateField} = useFieldState(others);
155157

156158
const context = useMemo(() => {
@@ -186,7 +188,8 @@ const TextField = (props: InternalTextFieldProps) => {
186188
)}
187189
<View style={[paddings, fieldStyle]} row centerV>
188190
{/* <View row centerV> */}
189-
{leadingAccessory}
191+
{/* @ts-expect-error */}
192+
{leadingAccessory && <View ref={leadingAccessoryRef}>{leadingAccessory}</View>}
190193
<View flexG>
191194
{floatingPlaceholder && (
192195
<FloatingPlaceholder
@@ -195,6 +198,7 @@ const TextField = (props: InternalTextFieldProps) => {
195198
floatingPlaceholderColor={floatingPlaceholderColor}
196199
floatOnFocus={floatOnFocus}
197200
validationMessagePosition={validationMessagePosition}
201+
extraOffset={leadingAccessoryMeasurements?.width}
198202
/>
199203
)}
200204
{children || (

0 commit comments

Comments
 (0)