@expo/ui 56.0.16 → 56.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/expo/modules/ui/ExpoUIModule.kt +40 -6
  4. package/android/src/main/java/expo/modules/ui/HostView.kt +0 -2
  5. package/android/src/main/java/expo/modules/ui/ModifierRegistry.kt +20 -0
  6. package/android/src/main/java/expo/modules/ui/RNHostView.kt +182 -6
  7. package/android/src/main/java/expo/modules/ui/textfield/BasicTextField.kt +203 -0
  8. package/android/src/main/java/expo/modules/ui/{TextFieldView.kt → textfield/TextField.kt} +34 -248
  9. package/android/src/main/java/expo/modules/ui/textfield/TextFieldShared.kt +299 -0
  10. package/build/State/useNativeState.d.ts +8 -3
  11. package/build/State/useNativeState.d.ts.map +1 -1
  12. package/build/community/pager-view/PagerView.android.d.ts.map +1 -1
  13. package/build/jetpack-compose/TextField/BasicTextField.d.ts +36 -0
  14. package/build/jetpack-compose/TextField/BasicTextField.d.ts.map +1 -0
  15. package/build/jetpack-compose/TextField/TextField.d.ts +131 -0
  16. package/build/jetpack-compose/TextField/TextField.d.ts.map +1 -0
  17. package/build/jetpack-compose/TextField/index.d.ts +3 -244
  18. package/build/jetpack-compose/TextField/index.d.ts.map +1 -1
  19. package/build/jetpack-compose/TextField/shared.d.ts +171 -0
  20. package/build/jetpack-compose/TextField/shared.d.ts.map +1 -0
  21. package/build/jetpack-compose/index.d.ts +1 -1
  22. package/build/jetpack-compose/index.d.ts.map +1 -1
  23. package/build/jetpack-compose/modifiers/index.d.ts +11 -0
  24. package/build/jetpack-compose/modifiers/index.d.ts.map +1 -1
  25. package/build/swift-ui/Image/index.d.ts +3 -1
  26. package/build/swift-ui/Image/index.d.ts.map +1 -1
  27. package/build/swift-ui/modifiers/index.d.ts +33 -4
  28. package/build/swift-ui/modifiers/index.d.ts.map +1 -1
  29. package/build/universal/TextInput/index.android.d.ts.map +1 -1
  30. package/build/universal/TextInput/types.d.ts +5 -1
  31. package/build/universal/TextInput/types.d.ts.map +1 -1
  32. package/expo-module.config.json +1 -1
  33. package/ios/ImageView.swift +1 -5
  34. package/ios/Modifiers/ImageScaleModifier.swift +29 -0
  35. package/ios/Modifiers/OnGeometryChangeModifier.swift +8 -16
  36. package/ios/Modifiers/ViewModifierRegistry.swift +32 -0
  37. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.16/expo.modules.ui-56.0.16-sources.jar → 56.0.17/expo.modules.ui-56.0.17-sources.jar} +0 -0
  38. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.md5 +1 -0
  39. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.sha1 +1 -0
  40. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.sha256 +1 -0
  41. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17-sources.jar.sha512 +1 -0
  42. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar +0 -0
  43. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.md5 +1 -0
  44. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.sha1 +1 -0
  45. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.sha256 +1 -0
  46. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.aar.sha512 +1 -0
  47. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.16/expo.modules.ui-56.0.16.module → 56.0.17/expo.modules.ui-56.0.17.module} +22 -22
  48. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.md5 +1 -0
  49. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.sha1 +1 -0
  50. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.sha256 +1 -0
  51. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.module.sha512 +1 -0
  52. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.16/expo.modules.ui-56.0.16.pom → 56.0.17/expo.modules.ui-56.0.17.pom} +1 -1
  53. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.md5 +1 -0
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.sha1 +1 -0
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.sha256 +1 -0
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.17/expo.modules.ui-56.0.17.pom.sha512 +1 -0
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  62. package/package.json +3 -3
  63. package/src/State/index.fx.ts +4 -1
  64. package/src/State/useNativeState.ts +24 -5
  65. package/src/community/datetime-picker/DateTimePicker.tsx +1 -1
  66. package/src/community/menu/MenuView.ios.tsx +1 -1
  67. package/src/community/pager-view/PagerView.android.tsx +16 -2
  68. package/src/community/pager-view/PagerView.ios.tsx +1 -1
  69. package/src/community/picker/Picker.ios.tsx +1 -1
  70. package/src/community/slider/Slider.ios.tsx +1 -1
  71. package/src/jetpack-compose/TextField/BasicTextField.tsx +118 -0
  72. package/src/jetpack-compose/TextField/TextField.tsx +198 -0
  73. package/src/jetpack-compose/TextField/index.ts +19 -0
  74. package/src/jetpack-compose/TextField/{index.tsx → shared.ts} +71 -203
  75. package/src/jetpack-compose/index.ts +6 -0
  76. package/src/jetpack-compose/modifiers/index.ts +13 -0
  77. package/src/swift-ui/BottomSheet/index.tsx +1 -1
  78. package/src/swift-ui/Image/index.tsx +12 -3
  79. package/src/swift-ui/modifiers/index.ts +42 -5
  80. package/src/universal/TextInput/index.android.tsx +26 -60
  81. package/src/universal/TextInput/types.ts +5 -1
  82. package/android/src/main/java/expo/modules/ui/ShadowNodeSyncFlush.kt +0 -28
  83. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.md5 +0 -1
  84. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.sha1 +0 -1
  85. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.sha256 +0 -1
  86. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16-sources.jar.sha512 +0 -1
  87. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar +0 -0
  88. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.md5 +0 -1
  89. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.sha1 +0 -1
  90. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.sha256 +0 -1
  91. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.aar.sha512 +0 -1
  92. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.md5 +0 -1
  93. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.sha1 +0 -1
  94. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.sha256 +0 -1
  95. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.module.sha512 +0 -1
  96. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.md5 +0 -1
  97. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.sha1 +0 -1
  98. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.sha256 +0 -1
  99. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.16/expo.modules.ui-56.0.16.pom.sha512 +0 -1
@@ -1,17 +1,13 @@
1
- import { requireNativeView } from 'expo';
2
- import type { Ref } from 'react';
1
+ import type { ReactNode, Ref } from 'react';
3
2
  import type { ColorValue } from 'react-native';
4
3
 
5
4
  import { getStateId, type ObservableState, useWorkletProp, worklets } from '../../State';
6
5
  import type { ModifierConfig, ViewEvent } from '../../types';
7
- import { parseJSXShape, type ShapeJSXElement, type ShapeRecordProps } from '../Shape';
8
- import { Slot } from '../SlotView';
9
6
  import { createViewModifierEventListener } from '../modifiers/utils';
10
7
 
11
- // region Types
12
-
13
8
  /**
14
- * Can be used for imperatively focusing and setting text/selection on the `TextField` component.
9
+ * Can be used for imperatively focusing and setting text/selection on the
10
+ * `TextField`, `OutlinedTextField`, and `BasicTextField` components.
15
11
  */
16
12
  export type TextFieldRef = {
17
13
  setText: (newText: string) => Promise<void>;
@@ -76,56 +72,37 @@ export type TextFieldKeyboardActions = {
76
72
  };
77
73
 
78
74
  /**
79
- * Colors for `TextField` and `OutlinedTextField`.
80
- * Maps to `TextFieldColors` in Compose, shared by both variants.
75
+ * Text styling for a text field's content. Maps to Compose's `TextStyle`.
76
+ * Shared by `TextField`, `OutlinedTextField`, and `BasicTextField`.
81
77
  */
82
- export type TextFieldColors = {
83
- focusedTextColor?: ColorValue;
84
- unfocusedTextColor?: ColorValue;
85
- disabledTextColor?: ColorValue;
86
- errorTextColor?: ColorValue;
87
- focusedContainerColor?: ColorValue;
88
- unfocusedContainerColor?: ColorValue;
89
- disabledContainerColor?: ColorValue;
90
- errorContainerColor?: ColorValue;
91
- cursorColor?: ColorValue;
92
- errorCursorColor?: ColorValue;
93
- focusedIndicatorColor?: ColorValue;
94
- unfocusedIndicatorColor?: ColorValue;
95
- disabledIndicatorColor?: ColorValue;
96
- errorIndicatorColor?: ColorValue;
97
- focusedLeadingIconColor?: ColorValue;
98
- unfocusedLeadingIconColor?: ColorValue;
99
- disabledLeadingIconColor?: ColorValue;
100
- errorLeadingIconColor?: ColorValue;
101
- focusedTrailingIconColor?: ColorValue;
102
- unfocusedTrailingIconColor?: ColorValue;
103
- disabledTrailingIconColor?: ColorValue;
104
- errorTrailingIconColor?: ColorValue;
105
- focusedLabelColor?: ColorValue;
106
- unfocusedLabelColor?: ColorValue;
107
- disabledLabelColor?: ColorValue;
108
- errorLabelColor?: ColorValue;
109
- focusedPlaceholderColor?: ColorValue;
110
- unfocusedPlaceholderColor?: ColorValue;
111
- disabledPlaceholderColor?: ColorValue;
112
- errorPlaceholderColor?: ColorValue;
113
- focusedSupportingTextColor?: ColorValue;
114
- unfocusedSupportingTextColor?: ColorValue;
115
- disabledSupportingTextColor?: ColorValue;
116
- errorSupportingTextColor?: ColorValue;
117
- focusedPrefixColor?: ColorValue;
118
- unfocusedPrefixColor?: ColorValue;
119
- disabledPrefixColor?: ColorValue;
120
- errorPrefixColor?: ColorValue;
121
- focusedSuffixColor?: ColorValue;
122
- unfocusedSuffixColor?: ColorValue;
123
- disabledSuffixColor?: ColorValue;
124
- errorSuffixColor?: ColorValue;
78
+ export type TextFieldTextStyle = {
79
+ textAlign?: 'left' | 'right' | 'center' | 'justify';
80
+ color?: ColorValue;
81
+ fontSize?: number;
82
+ fontFamily?: string;
83
+ fontWeight?:
84
+ | '100'
85
+ | '200'
86
+ | '300'
87
+ | '400'
88
+ | '500'
89
+ | '600'
90
+ | '700'
91
+ | '800'
92
+ | '900'
93
+ | 'normal'
94
+ | 'bold';
95
+ lineHeight?: number;
96
+ letterSpacing?: number;
125
97
  };
126
98
 
127
- /** Shared props between `TextField` and `OutlinedTextField`. */
128
- type BaseTextFieldProps = {
99
+ /**
100
+ * Props shared by every Compose text field variant — `TextField`,
101
+ * `OutlinedTextField`, and `BasicTextField`. The Material variants add their
102
+ * own decoration props (`isError`, `shape`, `colors`, slot children);
103
+ * `BasicTextField` adds `cursorColor`.
104
+ */
105
+ export type CommonTextFieldProperties = {
129
106
  ref?: Ref<TextFieldRef>;
130
107
  /**
131
108
  * An observable state that holds the current text value. Create one with
@@ -140,8 +117,6 @@ type BaseTextFieldProps = {
140
117
  /** @default false */
141
118
  readOnly?: boolean;
142
119
  /** @default false */
143
- isError?: boolean;
144
- /** @default false */
145
120
  singleLine?: boolean;
146
121
  maxLines?: number;
147
122
  minLines?: number;
@@ -151,17 +126,6 @@ type BaseTextFieldProps = {
151
126
  */
152
127
  visualTransformation?: 'password' | 'none';
153
128
 
154
- /**
155
- * Selection-related colors. Maps to Compose's `TextSelectionColors` via
156
- * `LocalTextSelectionColors`. `handleColor` controls the drag handles;
157
- * `backgroundColor` is the highlighted-text background (typically the same
158
- * tint at lower alpha so the underlying text stays readable).
159
- */
160
- textSelectionColors?: {
161
- handleColor?: ColorValue;
162
- backgroundColor?: ColorValue;
163
- };
164
-
165
129
  /**
166
130
  * Observable state holding the current selection range. Create with
167
131
  * `useNativeState({ start: 0, end: 0 })`. The field writes user-driven
@@ -178,28 +142,21 @@ type BaseTextFieldProps = {
178
142
  onSelectionChange?: (selection: { start: number; end: number }) => void;
179
143
 
180
144
  /**
181
- * Text styling for the field's content. Maps to Compose's `TextStyle`.
145
+ * Selection-related colors. Maps to Compose's `TextSelectionColors` via
146
+ * `LocalTextSelectionColors`. `handleColor` controls the drag handles (and
147
+ * the caret's drag handle); `backgroundColor` is the highlighted-text
148
+ * background (typically the same tint at lower alpha so the underlying text
149
+ * stays readable). Independent of `cursorColor`, which tints the caret line.
182
150
  */
183
- textStyle?: {
184
- textAlign?: 'left' | 'right' | 'center' | 'justify';
185
- color?: ColorValue;
186
- fontSize?: number;
187
- fontFamily?: string;
188
- fontWeight?:
189
- | '100'
190
- | '200'
191
- | '300'
192
- | '400'
193
- | '500'
194
- | '600'
195
- | '700'
196
- | '800'
197
- | '900'
198
- | 'normal'
199
- | 'bold';
200
- lineHeight?: number;
201
- letterSpacing?: number;
151
+ textSelectionColors?: {
152
+ handleColor?: ColorValue;
153
+ backgroundColor?: ColorValue;
202
154
  };
155
+
156
+ /**
157
+ * Text styling for the field's content. Maps to Compose's `TextStyle`.
158
+ */
159
+ textStyle?: TextFieldTextStyle;
203
160
  keyboardOptions?: TextFieldKeyboardOptions;
204
161
  keyboardActions?: TextFieldKeyboardActions;
205
162
  /**
@@ -211,44 +168,35 @@ type BaseTextFieldProps = {
211
168
  onValueChange?: (value: string) => void;
212
169
  /** A callback triggered when the field gains or loses focus. */
213
170
  onFocusChanged?: (focused: boolean) => void;
214
- /**
215
- * Shape used for the field's container outline/fill. Use the helpers from
216
- * `Shape` (for example, `<Shape.Pill />` or `<Shape.RoundedCorner cornerRadii={...} />`).
217
- * Defaults to the Material `OutlinedTextFieldDefaults.shape`/`TextFieldDefaults.shape`.
218
- */
219
- shape?: ShapeJSXElement;
220
171
  modifiers?: ModifierConfig[];
221
- /** Slot children (e.g. `TextField.Label`, `TextField.Placeholder`). */
222
- children?: React.ReactNode;
223
- };
224
-
225
- export type TextFieldProps = BaseTextFieldProps & {
226
- colors?: TextFieldColors;
227
- };
228
-
229
- export type OutlinedTextFieldProps = BaseTextFieldProps & {
230
- colors?: TextFieldColors;
172
+ /** Slot children that configure the field's decoration. */
173
+ children?: ReactNode;
231
174
  };
232
175
 
233
- // endregion Types
176
+ // region Native transform
234
177
 
235
- // region Native
236
-
237
- type NativeTextFieldProps = Omit<
238
- BaseTextFieldProps,
178
+ /**
179
+ * Keys consumed (and reshaped) by {@link useCommonTextFieldProps}. Everything
180
+ * else on the props passes through untouched.
181
+ */
182
+ type TransformedKeys =
239
183
  | 'value'
240
184
  | 'selection'
185
+ | 'modifiers'
186
+ | 'children'
187
+ | 'keyboardActions'
241
188
  | 'onValueChange'
242
189
  | 'onFocusChanged'
243
- | 'onSelectionChange'
244
- | 'keyboardActions'
245
- | 'children'
246
- | 'shape'
247
- > & {
248
- variant: 'filled' | 'outlined';
249
- colors?: TextFieldColors;
250
- shape?: ShapeRecordProps;
251
- children?: React.ReactNode;
190
+ | 'onSelectionChange';
191
+
192
+ /**
193
+ * Native-facing prop shape shared by every Compose text field variant. The
194
+ * observable-backed props collapse to shared-object ids, and the public
195
+ * callbacks become `nativeEvent`-wrapped listeners.
196
+ */
197
+ export type CommonNativeTextFieldProps = {
198
+ modifiers?: ModifierConfig[];
199
+ children?: ReactNode;
252
200
  value?: number | null;
253
201
  selection?: number | null;
254
202
  onValueChangeSync?: number | null;
@@ -257,15 +205,9 @@ type NativeTextFieldProps = Omit<
257
205
  ViewEvent<'onSelectionChange', { start: number; end: number }> &
258
206
  ViewEvent<'onKeyboardAction', { action: string; value: string }>;
259
207
 
260
- const TextFieldNativeView: React.ComponentType<NativeTextFieldProps> = requireNativeView(
261
- 'ExpoUI',
262
- 'TextFieldView'
263
- );
264
-
265
- function useTransformedProps(
266
- props: TextFieldProps | OutlinedTextFieldProps,
267
- variant: 'filled' | 'outlined'
268
- ): NativeTextFieldProps {
208
+ export function useCommonTextFieldProps<T extends CommonTextFieldProperties>(
209
+ props: T
210
+ ): CommonNativeTextFieldProps & Omit<T, TransformedKeys> {
269
211
  const {
270
212
  value,
271
213
  selection,
@@ -275,19 +217,16 @@ function useTransformedProps(
275
217
  onValueChange,
276
218
  onFocusChanged,
277
219
  onSelectionChange,
278
- shape,
279
- ...restProps
220
+ ...rest
280
221
  } = props;
281
222
 
282
223
  const isWorklet = !!onValueChange && !!worklets?.isWorkletFunction?.(onValueChange);
283
224
  const workletCallback = useWorkletProp(isWorklet ? onValueChange : undefined, 'onValueChange');
284
225
 
285
226
  return {
227
+ ...rest,
286
228
  modifiers,
287
229
  ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
288
- ...restProps,
289
- variant,
290
- shape: parseJSXShape(shape),
291
230
  children,
292
231
  value: getStateId(value),
293
232
  selection: getStateId(selection),
@@ -315,75 +254,4 @@ function useTransformedProps(
315
254
  };
316
255
  }
317
256
 
318
- // endregion Native
319
-
320
- // region Slot components
321
-
322
- function Label(props: { children: React.ReactNode }) {
323
- return <Slot slotName="label">{props.children}</Slot>;
324
- }
325
-
326
- function Placeholder(props: { children: React.ReactNode }) {
327
- return <Slot slotName="placeholder">{props.children}</Slot>;
328
- }
329
-
330
- function LeadingIcon(props: { children: React.ReactNode }) {
331
- return <Slot slotName="leadingIcon">{props.children}</Slot>;
332
- }
333
-
334
- function TrailingIcon(props: { children: React.ReactNode }) {
335
- return <Slot slotName="trailingIcon">{props.children}</Slot>;
336
- }
337
-
338
- function Prefix(props: { children: React.ReactNode }) {
339
- return <Slot slotName="prefix">{props.children}</Slot>;
340
- }
341
-
342
- function Suffix(props: { children: React.ReactNode }) {
343
- return <Slot slotName="suffix">{props.children}</Slot>;
344
- }
345
-
346
- function SupportingText(props: { children: React.ReactNode }) {
347
- return <Slot slotName="supportingText">{props.children}</Slot>;
348
- }
349
-
350
- // endregion Slot components
351
-
352
- // region Components
353
-
354
- /**
355
- * A Material3 `TextField`.
356
- */
357
- function TextFieldComponent(props: TextFieldProps) {
358
- return <TextFieldNativeView {...useTransformedProps(props, 'filled')} />;
359
- }
360
-
361
- TextFieldComponent.Label = Label;
362
- TextFieldComponent.Placeholder = Placeholder;
363
- TextFieldComponent.LeadingIcon = LeadingIcon;
364
- TextFieldComponent.TrailingIcon = TrailingIcon;
365
- TextFieldComponent.Prefix = Prefix;
366
- TextFieldComponent.Suffix = Suffix;
367
- TextFieldComponent.SupportingText = SupportingText;
368
-
369
- /**
370
- * A Material3 `OutlinedTextField` with a transparent background and border outline.
371
- */
372
- function OutlinedTextFieldComponent(props: OutlinedTextFieldProps) {
373
- return <TextFieldNativeView {...useTransformedProps(props, 'outlined')} />;
374
- }
375
-
376
- OutlinedTextFieldComponent.Label = Label;
377
- OutlinedTextFieldComponent.Placeholder = Placeholder;
378
- OutlinedTextFieldComponent.LeadingIcon = LeadingIcon;
379
- OutlinedTextFieldComponent.TrailingIcon = TrailingIcon;
380
- OutlinedTextFieldComponent.Prefix = Prefix;
381
- OutlinedTextFieldComponent.Suffix = Suffix;
382
- OutlinedTextFieldComponent.SupportingText = SupportingText;
383
-
384
- // endregion Components
385
-
386
- export { TextFieldComponent as TextField, OutlinedTextFieldComponent as OutlinedTextField };
387
-
388
- // Exported for docs api data
389
- export { type ObservableState };
257
+ // endregion Native transform
@@ -29,7 +29,11 @@ export * from './SyncSwitch';
29
29
  export {
30
30
  TextField,
31
31
  OutlinedTextField,
32
+ BasicTextField,
32
33
  type TextFieldProps,
34
+ type OutlinedTextFieldProps,
35
+ type BasicTextFieldProps,
36
+ type BasicTextFieldRef,
33
37
  type TextFieldRef,
34
38
  type TextFieldCapitalization,
35
39
  type TextFieldImeAction,
@@ -37,6 +41,8 @@ export {
37
41
  type TextFieldKeyboardType,
38
42
  type TextFieldKeyboardActions,
39
43
  type TextFieldColors,
44
+ type TextFieldTextStyle,
45
+ type CommonTextFieldProperties,
40
46
  } from './TextField';
41
47
  export * from './ToggleButton';
42
48
  export * from './Shape';
@@ -420,6 +420,19 @@ export const onSizeChanged = (handler: (size: { width: number; height: number })
420
420
  handler(size)
421
421
  );
422
422
 
423
+ /**
424
+ * Calls the handler whenever the composable is positioned, with its position and size.
425
+ * `x` and `y` are relative to the window. All values are in dp.
426
+ * @param handler - Function called with the new layout.
427
+ */
428
+ export const onGloballyPositioned = (
429
+ handler: (layout: { x: number; y: number; width: number; height: number }) => void
430
+ ) =>
431
+ createModifierWithEventListener(
432
+ 'onGloballyPositioned',
433
+ (layout: { x: number; y: number; width: number; height: number }) => handler(layout)
434
+ );
435
+
423
436
  // =============================================================================
424
437
  // Utility Modifiers
425
438
  // =============================================================================
@@ -1,6 +1,6 @@
1
1
  import { requireNativeView } from 'expo';
2
2
  import { useState, type ComponentType } from 'react';
3
- import type { NativeSyntheticEvent } from 'react-native';
3
+ import { type NativeSyntheticEvent } from 'react-native';
4
4
 
5
5
  import { createViewModifierEventListener } from '../modifiers/utils';
6
6
  import { type CommonViewModifierProps } from '../types';
@@ -3,6 +3,7 @@ import type { ColorValue } from 'react-native';
3
3
  import type { SFSymbol } from 'sf-symbols-typescript';
4
4
 
5
5
  import type { ViewEvent } from '../../types';
6
+ import { font, foregroundStyle } from '../modifiers';
6
7
  import { createViewModifierEventListener } from '../modifiers/utils';
7
8
  import type { CommonViewModifierProps } from '../types';
8
9
 
@@ -23,7 +24,9 @@ export interface ImageProps extends CommonViewModifierProps {
23
24
  */
24
25
  uiImage?: string;
25
26
  /**
26
- * The size of the system image.
27
+ * The fixed size of the system image in points. Does not scale with Dynamic
28
+ * Type. Use the `font` modifier with `textStyle` for that. Ignored when a
29
+ * `font` modifier is supplied.
27
30
  */
28
31
  size?: number;
29
32
  /**
@@ -53,9 +56,15 @@ type TapEvent = ViewEvent<'onTap', object> & {
53
56
  type NativeImageProps = Omit<ImageProps, 'onPress'> | TapEvent;
54
57
 
55
58
  function transformNativeProps(props: ImageProps): NativeImageProps {
56
- const { onPress, modifiers, ...restProps } = props;
59
+ const { onPress, modifiers, size, color, ...restProps } = props;
60
+ const hasFontModifier = modifiers?.some((modifier) => modifier.$type === 'font');
61
+ const allModifiers = [
62
+ ...(modifiers ?? []),
63
+ ...(hasFontModifier ? [] : [font({ size: size ?? 24 })]),
64
+ ...(color != null ? [foregroundStyle(color)] : []),
65
+ ];
57
66
  return {
58
- modifiers,
67
+ modifiers: allModifiers,
59
68
  ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
60
69
  ...restProps,
61
70
  ...(onPress ? { useTapGesture: true, onTap: () => onPress() } : null),
@@ -190,13 +190,17 @@ export const onDisappear = (handler: () => void) =>
190
190
  createModifierWithEventListener('onDisappear', handler);
191
191
 
192
192
  /**
193
- * Calls the handler whenever the view's geometry changes. Sizes are in points.
194
- * @param handler - Function called with the new size.
193
+ * Calls the handler whenever the view's geometry changes, with its position and size.
194
+ * `x` and `y` are in the global coordinate space (relative to the window); all values are in points.
195
+ * @param handler - Function called with the new frame.
195
196
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/ongeometrychange(for:of:action:)).
196
197
  */
197
- export const onGeometryChange = (handler: (size: { width: number; height: number }) => void) =>
198
- createModifierWithEventListener('onGeometryChange', (size: { width: number; height: number }) =>
199
- handler(size)
198
+ export const onGeometryChange = (
199
+ handler: (frame: { x: number; y: number; width: number; height: number }) => void
200
+ ) =>
201
+ createModifierWithEventListener(
202
+ 'onGeometryChange',
203
+ (frame: { x: number; y: number; width: number; height: number }) => handler(frame)
200
204
  );
201
205
 
202
206
  /**
@@ -561,6 +565,14 @@ export const toggleStyle = (style: 'automatic' | 'switch' | 'button') =>
561
565
  export const controlSize = (size: 'mini' | 'small' | 'regular' | 'large' | 'extraLarge') =>
562
566
  createModifier('controlSize', { size });
563
567
 
568
+ /**
569
+ * Scales SF Symbols within this view relative to the surrounding text, using one of the standard sizes.
570
+ * @param scale - The relative image scale.
571
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/imagescale(_:)).
572
+ */
573
+ export const imageScale = (scale: 'small' | 'medium' | 'large') =>
574
+ createModifier('imageScale', { scale });
575
+
564
576
  /**
565
577
  * Sets the style for labels within this view.
566
578
  * @param style - The label style.
@@ -731,6 +743,16 @@ export const accessibilityHint = (hint: string) => createModifier('accessibility
731
743
  export const accessibilityValue = (value: string) =>
732
744
  createModifier('accessibilityValue', { value });
733
745
 
746
+ /**
747
+ * Sets alternative spoken phrases that Voice Control uses to refer to the view.
748
+ * Each label is read as a `Text` element on iOS. For example, an "End" button
749
+ * might offer "Hang up" so users can trigger it by saying that phrase.
750
+ * @param inputLabels - The spoken phrases that select the view.
751
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/accessibilityinputlabels(_:)).
752
+ */
753
+ export const accessibilityInputLabels = (inputLabels: string[]) =>
754
+ createModifier('accessibilityInputLabels', { inputLabels });
755
+
734
756
  /**
735
757
  * Sets an accessibility identifier for the view.
736
758
  *
@@ -893,6 +915,19 @@ export const truncationMode = (mode: 'head' | 'middle' | 'tail') =>
893
915
  * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/allowstightening(_:)).
894
916
  */
895
917
  export const allowsTightening = (value: boolean) => createModifier('allowsTightening', { value });
918
+ /**
919
+ * Sets the minimum amount that text in this view scales down to fit in the available space.
920
+ *
921
+ * Use this modifier if the text you place in a view doesn't fit and it's okay if the text shrinks
922
+ * to accommodate. For example, a label with a minimum scale factor of `0.5` draws its text in a
923
+ * font size as small as half of the actual font if needed.
924
+ * @param factor - A fraction between `0` and `1` (including `0` and `1`) that specifies the amount
925
+ * of text to draw. For example, a value of `0.5` draws the text in a font size as small as half the
926
+ * actual font if needed.
927
+ * @see Official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui/view/minimumscalefactor(_:)).
928
+ */
929
+ export const minimumScaleFactor = (factor: number) =>
930
+ createModifier('minimumScaleFactor', { factor });
896
931
  /**
897
932
  * Sets the spacing, or kerning, between characters for the text in this view.
898
933
  * @default 0
@@ -1428,6 +1463,7 @@ export type BuiltInModifier =
1428
1463
  | ReturnType<typeof buttonBorderShape>
1429
1464
  | ReturnType<typeof toggleStyle>
1430
1465
  | ReturnType<typeof controlSize>
1466
+ | ReturnType<typeof imageScale>
1431
1467
  | ReturnType<typeof labelStyle>
1432
1468
  | ReturnType<typeof labelsHidden>
1433
1469
  | ReturnType<typeof textFieldStyle>
@@ -1435,6 +1471,7 @@ export type BuiltInModifier =
1435
1471
  | ReturnType<typeof accessibilityLabel>
1436
1472
  | ReturnType<typeof accessibilityHint>
1437
1473
  | ReturnType<typeof accessibilityValue>
1474
+ | ReturnType<typeof accessibilityInputLabels>
1438
1475
  | ReturnType<typeof accessibilityIdentifier>
1439
1476
  | ReturnType<typeof accessibilityHidden>
1440
1477
  | ReturnType<typeof layoutPriority>
@@ -1,5 +1,6 @@
1
1
  import {
2
- TextField as ComposeTextField,
2
+ BasicTextField,
3
+ Box,
3
4
  Text,
4
5
  type TextFieldImeAction,
5
6
  type TextFieldKeyboardType,
@@ -97,8 +98,6 @@ export function TextInput({
97
98
  const numberOfLines = numberOfLinesProp ?? rows;
98
99
  const keyboardType = keyboardTypeProp ?? inputModeToKeyboardType(inputMode);
99
100
  const returnKeyType = returnKeyTypeProp ?? enterKeyHintToReturnKeyType(enterKeyHint);
100
- const { backgroundColor: styleBackgroundColor, ...boxStyle } = style ?? {};
101
- const hideIndicator = boxStyle.borderWidth === 0 && underlineColorAndroid == null;
102
101
 
103
102
  const initialFallbackRef = useRef(defaultValue ?? '');
104
103
  const fallback = useNativeState<string>(initialFallbackRef.current);
@@ -156,11 +155,11 @@ export function TextInput({
156
155
  : undefined;
157
156
 
158
157
  return (
159
- <ComposeTextField
158
+ <BasicTextField
160
159
  ref={innerRef}
161
160
  modifiers={[
162
161
  ...(userModifiers ?? []),
163
- ...transformToModifiers(boxStyle, {}),
162
+ ...transformToModifiers(style, {}),
164
163
  ...(testID ? [testIDModifier(testID)] : []),
165
164
  ...(autoComplete ? [semantics({ contentType: autoComplete })] : []),
166
165
  ...(onContentSizeChange ? [onSizeChanged(onContentSizeChange)] : []),
@@ -171,50 +170,7 @@ export function TextInput({
171
170
  singleLine={!multiline}
172
171
  maxLines={multiline && numberOfLines && numberOfLines > 0 ? numberOfLines : undefined}
173
172
  minLines={multiline && numberOfLines && numberOfLines > 0 ? numberOfLines : undefined}
174
- colors={
175
- caretHidden ||
176
- cursorColor ||
177
- underlineColorAndroid ||
178
- placeholderTextColor ||
179
- styleBackgroundColor ||
180
- hideIndicator
181
- ? {
182
- ...(caretHidden
183
- ? { cursorColor: 'transparent' }
184
- : cursorColor
185
- ? { cursorColor }
186
- : null),
187
- ...(styleBackgroundColor
188
- ? {
189
- focusedContainerColor: styleBackgroundColor,
190
- unfocusedContainerColor: styleBackgroundColor,
191
- disabledContainerColor: styleBackgroundColor,
192
- errorContainerColor: styleBackgroundColor,
193
- }
194
- : null),
195
- ...(underlineColorAndroid
196
- ? {
197
- unfocusedIndicatorColor: underlineColorAndroid,
198
- focusedIndicatorColor: underlineColorAndroid,
199
- }
200
- : hideIndicator
201
- ? {
202
- focusedIndicatorColor: 'transparent',
203
- unfocusedIndicatorColor: 'transparent',
204
- disabledIndicatorColor: 'transparent',
205
- errorIndicatorColor: 'transparent',
206
- }
207
- : null),
208
- ...(placeholderTextColor
209
- ? {
210
- unfocusedPlaceholderColor: placeholderTextColor,
211
- focusedPlaceholderColor: placeholderTextColor,
212
- disabledPlaceholderColor: placeholderTextColor,
213
- }
214
- : null),
215
- }
216
- : undefined
217
- }
173
+ cursorColor={caretHidden ? 'transparent' : (cursorColor ?? selectionColor)}
218
174
  textStyle={
219
175
  textStyle || (textAlign && textAlign !== 'auto')
220
176
  ? {
@@ -237,18 +193,28 @@ export function TextInput({
237
193
  onValueChange={onChangeText}
238
194
  maxLength={maxLength}
239
195
  onFocusChanged={handleFocusChanged}
240
- selection={selection as Parameters<typeof ComposeTextField>[0]['selection']}
196
+ selection={selection as Parameters<typeof BasicTextField>[0]['selection']}
241
197
  onSelectionChange={onSelectionChange}>
242
- {placeholder ? (
243
- <ComposeTextField.Placeholder>
244
- <Text
245
- modifiers={[fillMaxWidth()]}
246
- style={textAlign && textAlign !== 'auto' ? { textAlign } : undefined}>
247
- {placeholder}
248
- </Text>
249
- </ComposeTextField.Placeholder>
250
- ) : null}
251
- </ComposeTextField>
198
+ <BasicTextField.DecorationBox>
199
+ <Box
200
+ modifiers={[fillMaxWidth()]}
201
+ contentAlignment={
202
+ textAlign === 'center' ? 'topCenter' : textAlign === 'right' ? 'topEnd' : undefined
203
+ }>
204
+ {placeholder != null ? (
205
+ <BasicTextField.Placeholder>
206
+ <Text
207
+ color={placeholderTextColor as string | undefined}
208
+ modifiers={[fillMaxWidth()]}
209
+ style={textAlign && textAlign !== 'auto' ? { textAlign } : undefined}>
210
+ {placeholder}
211
+ </Text>
212
+ </BasicTextField.Placeholder>
213
+ ) : null}
214
+ <BasicTextField.InnerTextField />
215
+ </Box>
216
+ </BasicTextField.DecorationBox>
217
+ </BasicTextField>
252
218
  );
253
219
  }
254
220
 
@@ -194,7 +194,11 @@ export interface TextInputProps {
194
194
  rows?: number;
195
195
 
196
196
  /**
197
- * Color of the underline indicator on Android. iOS / web ignore this.
197
+ * Color of the underline indicator on Android.
198
+ *
199
+ * @deprecated The Android `TextInput` renders an unstyled `BasicTextField` that
200
+ * has no underline indicator, so this has no effect. To draw your own border,
201
+ * pass it through `style` or `modifiers`.
198
202
  * @platform android
199
203
  */
200
204
  underlineColorAndroid?: ColorValue;