@mrmeg/expo-ui 0.25.1 → 0.27.0

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.
@@ -0,0 +1,296 @@
1
+ /* eslint-disable */
2
+ // GENERATED FILE - do not edit. Rebuild with `bun run ui:icons`.
3
+ // Source: icon-names.json (145 icons). Each icon is a
4
+ // per-glyph subpath import so consumers bundle only the names listed there.
5
+ import Archive from "lucide-react-native/icons/archive";
6
+ import ArrowDown from "lucide-react-native/icons/arrow-down";
7
+ import ArrowLeft from "lucide-react-native/icons/arrow-left";
8
+ import ArrowRight from "lucide-react-native/icons/arrow-right";
9
+ import ArrowUp from "lucide-react-native/icons/arrow-up";
10
+ import Award from "lucide-react-native/icons/award";
11
+ import Beer from "lucide-react-native/icons/beer";
12
+ import Bell from "lucide-react-native/icons/bell";
13
+ import BellOff from "lucide-react-native/icons/bell-off";
14
+ import Bold from "lucide-react-native/icons/bold";
15
+ import Bookmark from "lucide-react-native/icons/bookmark";
16
+ import Box from "lucide-react-native/icons/box";
17
+ import Briefcase from "lucide-react-native/icons/briefcase";
18
+ import Calendar from "lucide-react-native/icons/calendar";
19
+ import CalendarCheck from "lucide-react-native/icons/calendar-check";
20
+ import Camera from "lucide-react-native/icons/camera";
21
+ import CameraOff from "lucide-react-native/icons/camera-off";
22
+ import ChartNoAxesColumn from "lucide-react-native/icons/chart-no-axes-column";
23
+ import Check from "lucide-react-native/icons/check";
24
+ import ChevronDown from "lucide-react-native/icons/chevron-down";
25
+ import ChevronLeft from "lucide-react-native/icons/chevron-left";
26
+ import ChevronRight from "lucide-react-native/icons/chevron-right";
27
+ import ChevronUp from "lucide-react-native/icons/chevron-up";
28
+ import CircleAlert from "lucide-react-native/icons/circle-alert";
29
+ import CircleCheckBig from "lucide-react-native/icons/circle-check-big";
30
+ import CirclePlus from "lucide-react-native/icons/circle-plus";
31
+ import CircleQuestionMark from "lucide-react-native/icons/circle-question-mark";
32
+ import CircleStop from "lucide-react-native/icons/circle-stop";
33
+ import CircleX from "lucide-react-native/icons/circle-x";
34
+ import Clock from "lucide-react-native/icons/clock";
35
+ import Cloud from "lucide-react-native/icons/cloud";
36
+ import CloudOff from "lucide-react-native/icons/cloud-off";
37
+ import Code from "lucide-react-native/icons/code";
38
+ import Coffee from "lucide-react-native/icons/coffee";
39
+ import Compass from "lucide-react-native/icons/compass";
40
+ import Copy from "lucide-react-native/icons/copy";
41
+ import CreditCard from "lucide-react-native/icons/credit-card";
42
+ import Crosshair from "lucide-react-native/icons/crosshair";
43
+ import Database from "lucide-react-native/icons/database";
44
+ import DollarSign from "lucide-react-native/icons/dollar-sign";
45
+ import Download from "lucide-react-native/icons/download";
46
+ import Droplet from "lucide-react-native/icons/droplet";
47
+ import EllipsisVertical from "lucide-react-native/icons/ellipsis-vertical";
48
+ import ExternalLink from "lucide-react-native/icons/external-link";
49
+ import Eye from "lucide-react-native/icons/eye";
50
+ import EyeOff from "lucide-react-native/icons/eye-off";
51
+ import FaceSlightlySmiling from "lucide-react-native/icons/face-slightly-smiling";
52
+ import File from "lucide-react-native/icons/file";
53
+ import FileText from "lucide-react-native/icons/file-text";
54
+ import Folder from "lucide-react-native/icons/folder";
55
+ import Gift from "lucide-react-native/icons/gift";
56
+ import GitBranch from "lucide-react-native/icons/git-branch";
57
+ import GitPullRequest from "lucide-react-native/icons/git-pull-request";
58
+ import Globe from "lucide-react-native/icons/globe";
59
+ import Grid3x3 from "lucide-react-native/icons/grid-3x3";
60
+ import Hash from "lucide-react-native/icons/hash";
61
+ import Heart from "lucide-react-native/icons/heart";
62
+ import Hexagon from "lucide-react-native/icons/hexagon";
63
+ import House from "lucide-react-native/icons/house";
64
+ import Image from "lucide-react-native/icons/image";
65
+ import Inbox from "lucide-react-native/icons/inbox";
66
+ import Info from "lucide-react-native/icons/info";
67
+ import Italic from "lucide-react-native/icons/italic";
68
+ import Key from "lucide-react-native/icons/key";
69
+ import Layers from "lucide-react-native/icons/layers";
70
+ import LayoutGrid from "lucide-react-native/icons/layout-grid";
71
+ import Link from "lucide-react-native/icons/link";
72
+ import Link2 from "lucide-react-native/icons/link-2";
73
+ import List from "lucide-react-native/icons/list";
74
+ import Loader from "lucide-react-native/icons/loader";
75
+ import Lock from "lucide-react-native/icons/lock";
76
+ import LockOpen from "lucide-react-native/icons/lock-open";
77
+ import LogIn from "lucide-react-native/icons/log-in";
78
+ import LogOut from "lucide-react-native/icons/log-out";
79
+ import Mail from "lucide-react-native/icons/mail";
80
+ import Map from "lucide-react-native/icons/map";
81
+ import MapPin from "lucide-react-native/icons/map-pin";
82
+ import Maximize from "lucide-react-native/icons/maximize";
83
+ import Maximize2 from "lucide-react-native/icons/maximize-2";
84
+ import Menu from "lucide-react-native/icons/menu";
85
+ import MessageCircle from "lucide-react-native/icons/message-circle";
86
+ import MessageSquare from "lucide-react-native/icons/message-square";
87
+ import Mic from "lucide-react-native/icons/mic";
88
+ import MicOff from "lucide-react-native/icons/mic-off";
89
+ import Minus from "lucide-react-native/icons/minus";
90
+ import Monitor from "lucide-react-native/icons/monitor";
91
+ import Moon from "lucide-react-native/icons/moon";
92
+ import Music from "lucide-react-native/icons/music";
93
+ import Navigation from "lucide-react-native/icons/navigation";
94
+ import Package from "lucide-react-native/icons/package";
95
+ import PanelLeft from "lucide-react-native/icons/panel-left";
96
+ import PanelsTopLeft from "lucide-react-native/icons/panels-top-left";
97
+ import PartyPopper from "lucide-react-native/icons/party-popper";
98
+ import Pen from "lucide-react-native/icons/pen";
99
+ import PenLine from "lucide-react-native/icons/pen-line";
100
+ import Pencil from "lucide-react-native/icons/pencil";
101
+ import Percent from "lucide-react-native/icons/percent";
102
+ import Phone from "lucide-react-native/icons/phone";
103
+ import Play from "lucide-react-native/icons/play";
104
+ import Plus from "lucide-react-native/icons/plus";
105
+ import Printer from "lucide-react-native/icons/printer";
106
+ import Radio from "lucide-react-native/icons/radio";
107
+ import RefreshCw from "lucide-react-native/icons/refresh-cw";
108
+ import Repeat from "lucide-react-native/icons/repeat";
109
+ import RotateCw from "lucide-react-native/icons/rotate-cw";
110
+ import Search from "lucide-react-native/icons/search";
111
+ import Send from "lucide-react-native/icons/send";
112
+ import Server from "lucide-react-native/icons/server";
113
+ import Settings from "lucide-react-native/icons/settings";
114
+ import Share from "lucide-react-native/icons/share";
115
+ import Share2 from "lucide-react-native/icons/share-2";
116
+ import Shield from "lucide-react-native/icons/shield";
117
+ import ShieldOff from "lucide-react-native/icons/shield-off";
118
+ import ShoppingBag from "lucide-react-native/icons/shopping-bag";
119
+ import ShoppingCart from "lucide-react-native/icons/shopping-cart";
120
+ import Shuffle from "lucide-react-native/icons/shuffle";
121
+ import Slash from "lucide-react-native/icons/slash";
122
+ import SlidersHorizontal from "lucide-react-native/icons/sliders-horizontal";
123
+ import SlidersVertical from "lucide-react-native/icons/sliders-vertical";
124
+ import Smartphone from "lucide-react-native/icons/smartphone";
125
+ import Square from "lucide-react-native/icons/square";
126
+ import SquareCheckBig from "lucide-react-native/icons/square-check-big";
127
+ import SquarePen from "lucide-react-native/icons/square-pen";
128
+ import Star from "lucide-react-native/icons/star";
129
+ import Sun from "lucide-react-native/icons/sun";
130
+ import Tag from "lucide-react-native/icons/tag";
131
+ import Terminal from "lucide-react-native/icons/terminal";
132
+ import ThumbsUp from "lucide-react-native/icons/thumbs-up";
133
+ import Trash from "lucide-react-native/icons/trash";
134
+ import TrendingDown from "lucide-react-native/icons/trending-down";
135
+ import TrendingUp from "lucide-react-native/icons/trending-up";
136
+ import TriangleAlert from "lucide-react-native/icons/triangle-alert";
137
+ import Truck from "lucide-react-native/icons/truck";
138
+ import Underline from "lucide-react-native/icons/underline";
139
+ import Upload from "lucide-react-native/icons/upload";
140
+ import User from "lucide-react-native/icons/user";
141
+ import UserCheck from "lucide-react-native/icons/user-check";
142
+ import UserMinus from "lucide-react-native/icons/user-minus";
143
+ import UserPlus from "lucide-react-native/icons/user-plus";
144
+ import Users from "lucide-react-native/icons/users";
145
+ import Video from "lucide-react-native/icons/video";
146
+ import WifiOff from "lucide-react-native/icons/wifi-off";
147
+ import Wrench from "lucide-react-native/icons/wrench";
148
+ import X from "lucide-react-native/icons/x";
149
+ import Zap from "lucide-react-native/icons/zap";
150
+ export const ICONS = {
151
+ "archive": Archive,
152
+ "arrow-down": ArrowDown,
153
+ "arrow-left": ArrowLeft,
154
+ "arrow-right": ArrowRight,
155
+ "arrow-up": ArrowUp,
156
+ "award": Award,
157
+ "beer": Beer,
158
+ "bell": Bell,
159
+ "bell-off": BellOff,
160
+ "bold": Bold,
161
+ "bookmark": Bookmark,
162
+ "box": Box,
163
+ "briefcase": Briefcase,
164
+ "calendar": Calendar,
165
+ "calendar-check": CalendarCheck,
166
+ "camera": Camera,
167
+ "camera-off": CameraOff,
168
+ "chart-no-axes-column": ChartNoAxesColumn,
169
+ "check": Check,
170
+ "chevron-down": ChevronDown,
171
+ "chevron-left": ChevronLeft,
172
+ "chevron-right": ChevronRight,
173
+ "chevron-up": ChevronUp,
174
+ "circle-alert": CircleAlert,
175
+ "circle-check-big": CircleCheckBig,
176
+ "circle-plus": CirclePlus,
177
+ "circle-question-mark": CircleQuestionMark,
178
+ "circle-stop": CircleStop,
179
+ "circle-x": CircleX,
180
+ "clock": Clock,
181
+ "cloud": Cloud,
182
+ "cloud-off": CloudOff,
183
+ "code": Code,
184
+ "coffee": Coffee,
185
+ "compass": Compass,
186
+ "copy": Copy,
187
+ "credit-card": CreditCard,
188
+ "crosshair": Crosshair,
189
+ "database": Database,
190
+ "dollar-sign": DollarSign,
191
+ "download": Download,
192
+ "droplet": Droplet,
193
+ "ellipsis-vertical": EllipsisVertical,
194
+ "external-link": ExternalLink,
195
+ "eye": Eye,
196
+ "eye-off": EyeOff,
197
+ "face-slightly-smiling": FaceSlightlySmiling,
198
+ "file": File,
199
+ "file-text": FileText,
200
+ "folder": Folder,
201
+ "gift": Gift,
202
+ "git-branch": GitBranch,
203
+ "git-pull-request": GitPullRequest,
204
+ "globe": Globe,
205
+ "grid-3x3": Grid3x3,
206
+ "hash": Hash,
207
+ "heart": Heart,
208
+ "hexagon": Hexagon,
209
+ "house": House,
210
+ "image": Image,
211
+ "inbox": Inbox,
212
+ "info": Info,
213
+ "italic": Italic,
214
+ "key": Key,
215
+ "layers": Layers,
216
+ "layout-grid": LayoutGrid,
217
+ "link": Link,
218
+ "link-2": Link2,
219
+ "list": List,
220
+ "loader": Loader,
221
+ "lock": Lock,
222
+ "lock-open": LockOpen,
223
+ "log-in": LogIn,
224
+ "log-out": LogOut,
225
+ "mail": Mail,
226
+ "map": Map,
227
+ "map-pin": MapPin,
228
+ "maximize": Maximize,
229
+ "maximize-2": Maximize2,
230
+ "menu": Menu,
231
+ "message-circle": MessageCircle,
232
+ "message-square": MessageSquare,
233
+ "mic": Mic,
234
+ "mic-off": MicOff,
235
+ "minus": Minus,
236
+ "monitor": Monitor,
237
+ "moon": Moon,
238
+ "music": Music,
239
+ "navigation": Navigation,
240
+ "package": Package,
241
+ "panel-left": PanelLeft,
242
+ "panels-top-left": PanelsTopLeft,
243
+ "party-popper": PartyPopper,
244
+ "pen": Pen,
245
+ "pen-line": PenLine,
246
+ "pencil": Pencil,
247
+ "percent": Percent,
248
+ "phone": Phone,
249
+ "play": Play,
250
+ "plus": Plus,
251
+ "printer": Printer,
252
+ "radio": Radio,
253
+ "refresh-cw": RefreshCw,
254
+ "repeat": Repeat,
255
+ "rotate-cw": RotateCw,
256
+ "search": Search,
257
+ "send": Send,
258
+ "server": Server,
259
+ "settings": Settings,
260
+ "share": Share,
261
+ "share-2": Share2,
262
+ "shield": Shield,
263
+ "shield-off": ShieldOff,
264
+ "shopping-bag": ShoppingBag,
265
+ "shopping-cart": ShoppingCart,
266
+ "shuffle": Shuffle,
267
+ "slash": Slash,
268
+ "sliders-horizontal": SlidersHorizontal,
269
+ "sliders-vertical": SlidersVertical,
270
+ "smartphone": Smartphone,
271
+ "square": Square,
272
+ "square-check-big": SquareCheckBig,
273
+ "square-pen": SquarePen,
274
+ "star": Star,
275
+ "sun": Sun,
276
+ "tag": Tag,
277
+ "terminal": Terminal,
278
+ "thumbs-up": ThumbsUp,
279
+ "trash": Trash,
280
+ "trending-down": TrendingDown,
281
+ "trending-up": TrendingUp,
282
+ "triangle-alert": TriangleAlert,
283
+ "truck": Truck,
284
+ "underline": Underline,
285
+ "upload": Upload,
286
+ "user": User,
287
+ "user-check": UserCheck,
288
+ "user-minus": UserMinus,
289
+ "user-plus": UserPlus,
290
+ "users": Users,
291
+ "video": Video,
292
+ "wifi-off": WifiOff,
293
+ "wrench": Wrench,
294
+ "x": X,
295
+ "zap": Zap,
296
+ };
@@ -5,8 +5,9 @@
5
5
  * This package registry supplies a window-independent native blur handle and
6
6
  * focus presence even when keyboard-controller cannot observe the keyboard
7
7
  * (for example, in an isolated native sheet window). The package `TextInput`
8
- * registers on focus; tap-away dismissal (`keyboardDismiss.ts`) and the
9
- * `BottomSheet` overlay resign the field through that handle.
8
+ * registers on focus; tap-away dismissal (`keyboardDismiss.ts`, mounted by
9
+ * `DismissKeyboard` and `BottomSheet.Content`) resigns the field through that
10
+ * handle.
10
11
  */
11
12
  export type KeyboardFocusedInputToken = object;
12
13
  /**
@@ -23,5 +24,12 @@ export declare function hasKeyboardFocusedInput(): boolean;
23
24
  /**
24
25
  * Resign the currently-focused native field, dismissing its keyboard. Returns
25
26
  * `true` if a field was focused and a blur handle was available.
27
+ *
28
+ * Presence is cleared here, in the same call, rather than left to the field's
29
+ * later `onBlur`: consumers call `dismissKeyboard()` from submit handlers and
30
+ * the native blur callback is not guaranteed to arrive (isolated sheet window,
31
+ * iOS secure/plain handoff), which used to leave a stale registration behind.
32
+ * The clear is token-guarded, so a field that takes focus synchronously during
33
+ * `blur()` keeps its registration; the field's own later clear is idempotent.
26
34
  */
27
35
  export declare function dismissKeyboardFocusedInput(): boolean;
@@ -38,11 +38,19 @@ export function hasKeyboardFocusedInput() {
38
38
  /**
39
39
  * Resign the currently-focused native field, dismissing its keyboard. Returns
40
40
  * `true` if a field was focused and a blur handle was available.
41
+ *
42
+ * Presence is cleared here, in the same call, rather than left to the field's
43
+ * later `onBlur`: consumers call `dismissKeyboard()` from submit handlers and
44
+ * the native blur callback is not guaranteed to arrive (isolated sheet window,
45
+ * iOS secure/plain handoff), which used to leave a stale registration behind.
46
+ * The clear is token-guarded, so a field that takes focus synchronously during
47
+ * `blur()` keeps its registration; the field's own later clear is idempotent.
41
48
  */
42
49
  export function dismissKeyboardFocusedInput() {
43
- if (focusedInput) {
44
- focusedInput.blur();
45
- return true;
46
- }
47
- return false;
50
+ const entry = focusedInput;
51
+ if (!entry)
52
+ return false;
53
+ entry.blur();
54
+ clearKeyboardFocusedInput(entry.token);
55
+ return true;
48
56
  }
@@ -0,0 +1,3 @@
1
+ import type { TextInputProps } from "@expo/ui";
2
+ export type NativeTextFieldProps = TextInputProps;
3
+ export declare function NativeTextField({ ref, value, onChangeText, placeholder, autoFocus, editable: editableProp, multiline, keyboardType: keyboardTypeProp, autoCapitalize, autoCorrect, returnKeyType: returnKeyTypeProp, onSubmitEditing, onFocus, onBlur, cursorColor, textAlign, readOnly, inputMode, enterKeyHint, defaultValue, numberOfLines: numberOfLinesProp, rows, testID, placeholderTextColor, textStyle, style, secureTextEntry, autoComplete, onContentSizeChange, maxLength, caretHidden, selectionColor, selectionHandleColor, selection, onSelectionChange, selectTextOnFocus, modifiers: userModifiers, }: NativeTextFieldProps): import("react").JSX.Element;
@@ -0,0 +1,226 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Native text field indirection — Android variant.
4
+ *
5
+ * A package-owned port of `@expo/ui`'s universal Android `TextInput`
6
+ * (`@expo/ui` 58.0.2, `src/universal/TextInput/index.android.tsx`, MIT
7
+ * licensed, Copyright (c) 650 Industries, Inc.), built on the public
8
+ * `@expo/ui/jetpack-compose` `BasicTextField`. It keeps the universal field's
9
+ * prop surface (`NativeTextFieldProps` *is* `@expo/ui`'s `TextInputProps`), its
10
+ * `useNativeState` fallback buffer, its ref handle, its slot layout and its
11
+ * modifier order, and differs in exactly two places:
12
+ *
13
+ * 1. **Secure fields declare a password keyboard.** The universal field maps
14
+ * `secureTextEntry` to `visualTransformation="password"` only, so the IME
15
+ * still sees a plain text field: Gboard shows its suggestion strip, learns
16
+ * the password, and `dumpsys input_method` reports `TYPE_CLASS_TEXT` with no
17
+ * password variation. Here a secure field sets `keyboardType` to
18
+ * `'password'` (`textPassword`), or `'numberPassword'` when the requested
19
+ * keyboard is numeric, and defaults `autoCorrectEnabled` to `false` and
20
+ * `capitalization` to `'none'`. Once a mounted field has been secure the
21
+ * password keyboard options stay constant for its lifetime, so the eye
22
+ * toggle (`secureTextEntry` true -> false on the same element) only drops the
23
+ * masking transformation: Compose treats `KeyboardType.Password` with
24
+ * `VisualTransformation.None` as the canonical revealed-password state, and
25
+ * an unchanged `KeyboardOptions` avoids an IME restart, preserving focus,
26
+ * selection and the input connection.
27
+ * 2. **Box style maps only the padding keys the package sends.** The universal
28
+ * field's `transformToModifiers` is not a public export. `TextInput.tsx`
29
+ * hands the field `{ backgroundColor: "transparent", paddingHorizontal,
30
+ * paddingVertical }` and paints the surface (fill, border, radius) on its RN
31
+ * wrapper, so `styleToModifiers` below handles the padding cascade and
32
+ * ignores `backgroundColor`.
33
+ *
34
+ * Only `@expo/ui/jetpack-compose` and `@expo/ui/jetpack-compose/modifiers` are
35
+ * imported, both in the peer's export map since SDK 56.
36
+ */
37
+ import { useImperativeHandle, useRef } from "react";
38
+ import { BasicTextField, Box, Text, useNativeState, } from "@expo/ui/jetpack-compose";
39
+ import { fillMaxWidth, onSizeChanged, padding, paddingAll, semantics, testID as testIDModifier, } from "@expo/ui/jetpack-compose/modifiers";
40
+ // region Prop mapping (ported verbatim from @expo/ui's TextInput/utils.ts)
41
+ function inputModeToKeyboardType(inputMode) {
42
+ if (!inputMode || inputMode === "none")
43
+ return undefined;
44
+ switch (inputMode) {
45
+ case "text":
46
+ return "default";
47
+ case "decimal":
48
+ return "decimal-pad";
49
+ case "tel":
50
+ return "phone-pad";
51
+ case "search":
52
+ return "web-search";
53
+ case "email":
54
+ return "email-address";
55
+ default:
56
+ return inputMode;
57
+ }
58
+ }
59
+ function enterKeyHintToReturnKeyType(hint) {
60
+ if (!hint)
61
+ return undefined;
62
+ if (hint === "enter")
63
+ return "default";
64
+ return hint;
65
+ }
66
+ function resolveEditable(editable, readOnly) {
67
+ if (editable !== undefined)
68
+ return editable;
69
+ if (readOnly === true)
70
+ return false;
71
+ return undefined;
72
+ }
73
+ function mapReturnKeyType(value) {
74
+ if (value === "google" || value === "yahoo")
75
+ return "search";
76
+ if (value === "join" || value === "route" || value === "emergency-call")
77
+ return "default";
78
+ return value;
79
+ }
80
+ function mapKeyboardType(value) {
81
+ switch (value) {
82
+ case "default":
83
+ return "text";
84
+ case "email-address":
85
+ return "email";
86
+ case "numeric":
87
+ return "decimal";
88
+ case "number-pad":
89
+ return "number";
90
+ case "decimal-pad":
91
+ return "decimal";
92
+ case "phone-pad":
93
+ return "phone";
94
+ case "url":
95
+ return "uri";
96
+ case "ascii-capable":
97
+ case "numbers-and-punctuation":
98
+ case "name-phone-pad":
99
+ case "twitter":
100
+ case "web-search":
101
+ case "visible-password":
102
+ return "text";
103
+ default:
104
+ return "text";
105
+ }
106
+ }
107
+ // endregion Prop mapping
108
+ // region Style mapping
109
+ /** Compose modifiers take dp numbers; anything else (percent strings, animated values) is ignored. */
110
+ function dp(value) {
111
+ return typeof value === "number" ? value : undefined;
112
+ }
113
+ /**
114
+ * Maps the box-style padding keys to Compose padding modifiers, with the same
115
+ * cascade as `@expo/ui`'s `transformToModifiers`: a specific side beats the
116
+ * directional shorthand, which beats `padding`. `backgroundColor` is
117
+ * deliberately not mapped — the RN wrapper in `TextInput.tsx` paints the
118
+ * surface.
119
+ */
120
+ function styleToModifiers(style) {
121
+ if (!style)
122
+ return [];
123
+ const all = dp(style.padding) ?? 0;
124
+ const top = dp(style.paddingTop) ?? dp(style.paddingVertical) ?? all;
125
+ const bottom = dp(style.paddingBottom) ?? dp(style.paddingVertical) ?? all;
126
+ const start = dp(style.paddingLeft) ?? dp(style.paddingHorizontal) ?? all;
127
+ const end = dp(style.paddingRight) ?? dp(style.paddingHorizontal) ?? all;
128
+ if (!top && !bottom && !start && !end)
129
+ return [];
130
+ if (top === bottom && bottom === start && start === end)
131
+ return [paddingAll(top)];
132
+ return [padding(start, top, end, bottom)];
133
+ }
134
+ // endregion Style mapping
135
+ /**
136
+ * Picks the password keyboard for a secure field: numeric keyboards become
137
+ * `numberPassword` (a `TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_PASSWORD`
138
+ * field), everything else `password` (`TYPE_TEXT_VARIATION_PASSWORD`).
139
+ */
140
+ function passwordKeyboardType(mapped) {
141
+ return mapped === "number" || mapped === "decimal" || mapped === "phone" ? "numberPassword" : "password";
142
+ }
143
+ export function NativeTextField({ ref, value, onChangeText, placeholder, autoFocus, editable: editableProp, multiline, keyboardType: keyboardTypeProp, autoCapitalize, autoCorrect, returnKeyType: returnKeyTypeProp, onSubmitEditing, onFocus, onBlur, cursorColor, textAlign, readOnly, inputMode, enterKeyHint, defaultValue, numberOfLines: numberOfLinesProp, rows, testID, placeholderTextColor, textStyle, style, secureTextEntry, autoComplete, onContentSizeChange, maxLength, caretHidden, selectionColor, selectionHandleColor, selection, onSelectionChange, selectTextOnFocus, modifiers: userModifiers, }) {
144
+ const editable = resolveEditable(editableProp, readOnly);
145
+ const numberOfLines = numberOfLinesProp ?? rows;
146
+ const keyboardType = keyboardTypeProp ?? inputModeToKeyboardType(inputMode);
147
+ const returnKeyType = returnKeyTypeProp ?? enterKeyHintToReturnKeyType(enterKeyHint);
148
+ const initialFallbackRef = useRef(defaultValue ?? "");
149
+ const fallback = useNativeState(initialFallbackRef.current);
150
+ const state = (value ?? fallback);
151
+ const innerRef = useRef(null);
152
+ const isFocusedRef = useRef(false);
153
+ useImperativeHandle(ref, () => ({
154
+ focus: () => {
155
+ innerRef.current?.focus();
156
+ },
157
+ blur: () => {
158
+ innerRef.current?.blur();
159
+ },
160
+ clear: () => {
161
+ innerRef.current?.clear();
162
+ },
163
+ isFocused: () => isFocusedRef.current,
164
+ setSelection: (start, end) => innerRef.current?.setSelection(start, end) ?? Promise.resolve(),
165
+ }), []);
166
+ const handleFocusChanged = (focused) => {
167
+ isFocusedRef.current = focused;
168
+ if (focused && selectTextOnFocus) {
169
+ innerRef.current?.setSelection(0, state.value.length);
170
+ }
171
+ if (focused)
172
+ onFocus?.();
173
+ else
174
+ onBlur?.();
175
+ };
176
+ // Sticky for the life of the mounted field: revealing a password
177
+ // (`secureTextEntry` -> false on the same element) must keep the password
178
+ // keyboard options unchanged so the IME neither restarts nor starts
179
+ // suggesting/learning the now-visible text. Written during render like the
180
+ // wrapper's own `activeSecureRef`; a fresh mount starts from the prop.
181
+ const everSecureRef = useRef(!!secureTextEntry);
182
+ if (secureTextEntry)
183
+ everSecureRef.current = true;
184
+ const passwordField = everSecureRef.current;
185
+ const mappedKeyboardType = keyboardType ? mapKeyboardType(keyboardType) : undefined;
186
+ const resolvedKeyboardType = passwordField
187
+ ? passwordKeyboardType(mappedKeyboardType)
188
+ : mappedKeyboardType;
189
+ const capitalization = autoCapitalize ?? (passwordField ? "none" : undefined);
190
+ const autoCorrectEnabled = autoCorrect ?? (passwordField ? false : undefined);
191
+ const keyboardOptions = resolvedKeyboardType || capitalization || autoCorrectEnabled !== undefined || returnKeyType
192
+ ? {
193
+ ...(resolvedKeyboardType ? { keyboardType: resolvedKeyboardType } : null),
194
+ ...(capitalization ? { capitalization } : null),
195
+ ...(autoCorrectEnabled !== undefined ? { autoCorrectEnabled } : null),
196
+ ...(returnKeyType ? { imeAction: mapReturnKeyType(returnKeyType) } : null),
197
+ }
198
+ : undefined;
199
+ const keyboardActions = onSubmitEditing
200
+ ? {
201
+ onDone: onSubmitEditing,
202
+ onGo: onSubmitEditing,
203
+ onNext: onSubmitEditing,
204
+ onSearch: onSubmitEditing,
205
+ onSend: onSubmitEditing,
206
+ onPrevious: onSubmitEditing,
207
+ }
208
+ : undefined;
209
+ return (_jsx(BasicTextField, { ref: innerRef, modifiers: [
210
+ ...(userModifiers ?? []),
211
+ ...styleToModifiers(style),
212
+ ...(testID ? [testIDModifier(testID)] : []),
213
+ ...(autoComplete ? [semantics({ contentType: autoComplete })] : []),
214
+ ...(onContentSizeChange ? [onSizeChanged(onContentSizeChange)] : []),
215
+ ], value: state, autoFocus: autoFocus, readOnly: editable === false, singleLine: !multiline, maxLines: multiline && numberOfLines && numberOfLines > 0 ? numberOfLines : undefined, minLines: multiline && numberOfLines && numberOfLines > 0 ? numberOfLines : undefined, cursorColor: caretHidden ? "transparent" : (cursorColor ?? selectionColor), textStyle: textStyle || (textAlign && textAlign !== "auto")
216
+ ? {
217
+ ...textStyle,
218
+ ...(textAlign && textAlign !== "auto" ? { textAlign } : null),
219
+ }
220
+ : undefined, visualTransformation: secureTextEntry ? "password" : undefined, textSelectionColors: selectionColor || selectionHandleColor
221
+ ? {
222
+ handleColor: selectionHandleColor ?? selectionColor,
223
+ backgroundColor: selectionColor,
224
+ }
225
+ : undefined, keyboardOptions: keyboardOptions, keyboardActions: keyboardActions, onValueChange: onChangeText, maxLength: maxLength, onFocusChanged: handleFocusChanged, selection: selection, onSelectionChange: onSelectionChange, children: _jsx(BasicTextField.DecorationBox, { children: _jsxs(Box, { modifiers: [fillMaxWidth()], contentAlignment: textAlign === "center" ? "topCenter" : textAlign === "right" ? "topEnd" : undefined, children: [placeholder != null ? (_jsx(BasicTextField.Placeholder, { children: _jsx(Text, { color: placeholderTextColor, modifiers: [fillMaxWidth()], style: textAlign && textAlign !== "auto" ? { textAlign } : undefined, children: placeholder }) })) : null, _jsx(BasicTextField.InnerTextField, {})] }) }) }));
226
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Native text field indirection — default variant (iOS; also what Jest and
3
+ * TypeScript resolve).
4
+ *
5
+ * Re-exports `@expo/ui`'s universal `TextInput`, which on iOS bridges to
6
+ * SwiftUI's `TextField`/`SecureField` and already reports a password field to
7
+ * the keyboard when `secureTextEntry` is set.
8
+ *
9
+ * Metro resolves `nativeTextField.android.tsx` on Android instead: the package
10
+ * owns the Compose field there so a secure entry can declare a password
11
+ * keyboard type (see that file). Both variants must export the same names with
12
+ * the same types — consumers and the type checker only ever see this one.
13
+ */
14
+ export { TextInput as NativeTextField, type TextInputProps as NativeTextFieldProps, } from "@expo/ui";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Native text field indirection — default variant (iOS; also what Jest and
3
+ * TypeScript resolve).
4
+ *
5
+ * Re-exports `@expo/ui`'s universal `TextInput`, which on iOS bridges to
6
+ * SwiftUI's `TextField`/`SecureField` and already reports a password field to
7
+ * the keyboard when `secureTextEntry` is set.
8
+ *
9
+ * Metro resolves `nativeTextField.android.tsx` on Android instead: the package
10
+ * owns the Compose field there so a secure entry can declare a password
11
+ * keyboard type (see that file). Both variants must export the same names with
12
+ * the same types — consumers and the type checker only ever see this one.
13
+ */
14
+ export { TextInput as NativeTextField, } from "@expo/ui";