Skip to content

Commit 1c79eaf

Browse files
committed
fix: error on Android using PlatformColor
1 parent 87b2a37 commit 1c79eaf

11 files changed

Lines changed: 86 additions & 29 deletions

File tree

src/components/TextField/TextField.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ function TextField(props: TextFieldProps) {
168168
$leadingAccessoryStyles,
169169
$trailingAccessoryStyles,
170170
$fieldStyles,
171+
$disabledBackgroundStyles,
171172
$outlineStyles,
172173
$animatedActiveOutlineStyles,
173174
$animatedLabelWrapperStyles,
@@ -193,6 +194,14 @@ function TextField(props: TextFieldProps) {
193194
role="none"
194195
>
195196
<View style={$fieldStyles}>
197+
{/* Disabled tint overlay — filled variant only. A childless
198+
absolutely-positioned View whose translucent fill is applied via the
199+
`opacity` style, so it never affects label/input rendering and works
200+
with PlatformColor on Android. */}
201+
{!!$disabledBackgroundStyles && (
202+
<View pointerEvents="none" style={$disabledBackgroundStyles} />
203+
)}
204+
196205
{/* Inactive indicator — always-visible 1px bottom border (filled) or
197206
full border (outlined); height and color reflect error/disabled state
198207
but do not change on focus */}

src/components/TextField/constants.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,3 @@ export const INACTIVE_INDICATOR_SIZE = 1;
5555
// SHAPE
5656
// ============
5757
export const TEXT_FIELD_BORDER_RADIUS = 4;
58-
59-
// ============
60-
// OPACITY
61-
// ============
62-
63-
export const OUTLINE_ALPHA = 0.12;
64-
export const FILLED_CONTAINER_ALPHA = 0.04;

src/components/TextField/filled/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@ export const ACTIVE_LABEL_TOP_POSITION = TEXT_FIELD_PADDING_VERTICAL;
2525
// ==================
2626

2727
export const PADDING_TOP = ACTIVE_LABEL_FONT_SIZE + TEXT_FIELD_PADDING_VERTICAL;
28+
29+
// ==================
30+
// OPACITY
31+
// ==================
32+
33+
export const FILLED_CONTAINER_ALPHA = 0.04;

src/components/TextField/filled/logic.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,16 @@ import {
1515
} from '../constants';
1616
import {
1717
$disabledStyle,
18-
$supportingTextStyle,
1918
$inputStyle,
2019
$leadingAccessoryStyle,
20+
$supportingTextStyle,
2121
$trailingAccessoryStyle,
2222
} from '../styles';
2323
import type { TextFieldProps, TextFieldSharedApi } from '../TextField';
2424
import {
2525
getFieldBackgroundColor,
26-
getSupportingTextColor,
2726
getLabelColor,
27+
getSupportingTextColor,
2828
} from '../utils';
2929
import {
3030
LABEL_LEFT_OFFSET_WITH_ACCESSORY,
@@ -36,6 +36,7 @@ import {
3636
$fieldStyle,
3737
$labelTextStyle,
3838
$labelWrapperStyle,
39+
$disabledBackgroundStyle,
3940
$outlineStyle,
4041
} from './styles';
4142
import { getOutlineColor } from './utils';
@@ -145,12 +146,25 @@ export const getFilledTextFieldData = (
145146
$fieldStyleOverride,
146147
];
147148

149+
/* Disabled tint (`onSurface @ 0.04`) is rendered as a childless overlay so its
150+
alpha can be applied via the `opacity` style without leaking onto the label
151+
and input. The View accepts `PlatformColor` directly. */
152+
const $disabledBackgroundStyles: StyleProp<ViewStyle> = disabled
153+
? [
154+
$disabledBackgroundStyle,
155+
{
156+
backgroundColor: onSurface,
157+
},
158+
]
159+
: undefined;
160+
148161
const $outlineStyles = [
149162
$outlineStyle,
150163
{
151164
height: INACTIVE_INDICATOR_SIZE,
152165
backgroundColor: outlineColor,
153166
},
167+
disabled && $disabledStyle,
154168
];
155169

156170
const $animatedActiveOutlineStyles: StyleProp<
@@ -161,6 +175,7 @@ export const getFilledTextFieldData = (
161175
height: ACTIVE_INDICATOR_SIZE,
162176
backgroundColor: activeOutlineColor,
163177
},
178+
disabled && $disabledStyle,
164179
$animatedActiveOutlineStyle,
165180
];
166181

@@ -231,6 +246,7 @@ export const getFilledTextFieldData = (
231246
$animatedLabelTextStyles,
232247
$animatedPlaceholderStyles,
233248
$fieldStyles,
249+
$disabledBackgroundStyles,
234250
$outlineStyles,
235251
$animatedActiveOutlineStyles,
236252
$containerStyles,

src/components/TextField/filled/styles.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
TEXT_FIELD_INPUT_WRAPPER_PADDING_HORIZONTAL,
77
TEXT_FIELD_PADDING_VERTICAL,
88
} from '../constants';
9+
import { FILLED_CONTAINER_ALPHA } from './constants';
910

1011
export const $fieldStyle: ViewStyle = {
1112
minHeight: TEXT_FIELD_HEIGHT,
@@ -38,3 +39,17 @@ export const $labelTextStyle: TextStyle = {
3839
paddingVertical: 0,
3940
paddingHorizontal: 0,
4041
};
42+
43+
/* Disabled tint (`onSurface @ 0.04`) is rendered as a childless overlay so its
44+
alpha can be applied via the `opacity` style without leaking onto the label
45+
and input. The View accepts `PlatformColor` directly. */
46+
export const $disabledBackgroundStyle: ViewStyle = {
47+
position: 'absolute',
48+
top: 0,
49+
left: 0,
50+
right: 0,
51+
bottom: 0,
52+
opacity: FILLED_CONTAINER_ALPHA,
53+
borderTopStartRadius: TEXT_FIELD_BORDER_RADIUS,
54+
borderTopEndRadius: TEXT_FIELD_BORDER_RADIUS,
55+
};

src/components/TextField/filled/utils.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import color from 'color';
2-
3-
import { tokens } from '../../../styles/themes/v3/tokens';
41
import type { InternalTheme } from '../../../types';
52

3+
/**
4+
* Returns the raw outline color for a filled field. The disabled state's
5+
* alpha is intentionally NOT baked in here — it is applied via the `opacity`
6+
* style on the (childless) outline View so the value can be a `PlatformColor`
7+
* on Android, which the `color` library cannot parse at runtime.
8+
*/
69
export const getOutlineColor = ({
710
theme,
811
status,
@@ -19,10 +22,7 @@ export const getOutlineColor = ({
1922
} = theme;
2023

2124
if (disabled) {
22-
return color(onSurface)
23-
.alpha(tokens.md.ref.stateOpacity.disabled)
24-
.rgb()
25-
.string();
25+
return onSurface;
2626
}
2727
if (status === 'error') {
2828
return error;

src/components/TextField/outlined/constants.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,9 @@ export const LABEL_TRANSLATE_X_WITHOUT_ACCESSORY =
4747

4848
export const PLACEHOLDER_TOP_POSITION =
4949
TEXT_FIELD_PADDING_VERTICAL + LINE_HEIGHT_DELTA;
50+
51+
// ============
52+
// OPACITY
53+
// ============
54+
55+
export const OUTLINE_ALPHA = 0.12;

src/components/TextField/outlined/logic.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getSupportingTextColor, getLabelColor } from '../utils';
2323
import {
2424
LABEL_LEFT_OFFSET_WITH_ACCESSORY,
2525
LABEL_LEFT_OFFSET_WITHOUT_ACCESSORY,
26+
OUTLINE_ALPHA,
2627
PLACEHOLDER_TOP_POSITION,
2728
} from './constants';
2829
import {
@@ -99,12 +100,16 @@ export const getOutlinedTextFieldData = (
99100

100101
const $fieldStyles = [$fieldStyle, $fieldStyleOverride];
101102

103+
/* The outline is a childless absolutely-positioned View, so applying
104+
`opacity` here is safe and lets us pass `outlineColor` through unchanged
105+
(including PlatformColor values on Android). */
102106
const $outlineStyles = [
103107
$outlineStyle,
104108
{
105109
borderWidth: isFocused ? 2 : 1,
106110
borderColor: outlineColor,
107111
},
112+
disabled && { opacity: OUTLINE_ALPHA },
108113
$fieldStyleOverride,
109114
];
110115

@@ -199,6 +204,7 @@ export const getOutlinedTextFieldData = (
199204
$animatedLabelTextStyles,
200205
$animatedPlaceholderStyles,
201206
$fieldStyles,
207+
$disabledBackgroundStyles: undefined,
202208
$outlineStyles,
203209
$containerStyles,
204210
$supportingTextStyles,

src/components/TextField/outlined/utils.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import color from 'color';
2-
31
import type { InternalTheme } from '../../../types';
4-
import { OUTLINE_ALPHA } from '../constants';
52

3+
/**
4+
* Returns the raw outline color for an outlined field. The disabled state's
5+
* alpha is intentionally NOT baked in here — it is applied via the `opacity`
6+
* style on the (childless) outline View so the value can be a `PlatformColor`
7+
* on Android, which the `color` library cannot parse at runtime.
8+
*/
69
export const getOutlineColor = ({
710
theme,
811
isFocused,
@@ -25,7 +28,7 @@ export const getOutlineColor = ({
2528
}
2629

2730
if (disabled) {
28-
outlineColor = color(onSurface).alpha(OUTLINE_ALPHA).rgb().string();
31+
outlineColor = onSurface;
2932
}
3033

3134
if (hasError) {

src/components/TextField/utils.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import color from 'color';
2-
3-
import { FILLED_CONTAINER_ALPHA } from './constants';
41
import type { InternalTheme } from '../../types';
52

63
export const getAccentColors = ({
@@ -69,19 +66,23 @@ export const getSupportingTextColor = ({
6966
return onSurfaceVariant;
7067
};
7168

69+
/**
70+
* Returns the solid background color for the filled field container, or
71+
* `undefined` when disabled. The disabled tint (`onSurface @ 0.04`) is rendered
72+
* as a separate overlay View whose alpha is applied via the `opacity` style;
73+
* keeping the alpha out of the color string is what makes the component safe
74+
* to use with `PlatformColor` values on Android.
75+
*/
7276
export const getFieldBackgroundColor = ({
7377
theme,
7478
disabled,
7579
}: {
7680
theme: InternalTheme;
7781
disabled: boolean;
78-
}) => {
79-
const {
80-
colors: { onSurface, surfaceContainerHighest },
81-
} = theme;
82+
}): string | undefined => {
8283
if (disabled) {
83-
return color(onSurface).alpha(FILLED_CONTAINER_ALPHA).rgb().string();
84+
return undefined;
8485
}
8586

86-
return surfaceContainerHighest;
87+
return theme.colors.surfaceContainerHighest;
8788
};

0 commit comments

Comments
 (0)