@oxyhq/bloom 0.19.1 → 0.20.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.
Files changed (170) hide show
  1. package/lib/commonjs/benefit-list/index.js +119 -0
  2. package/lib/commonjs/benefit-list/index.js.map +1 -0
  3. package/lib/commonjs/benefit-list/types.js +6 -0
  4. package/lib/commonjs/benefit-list/types.js.map +1 -0
  5. package/lib/commonjs/connection-dots/ConnectionDots.js +112 -0
  6. package/lib/commonjs/connection-dots/ConnectionDots.js.map +1 -0
  7. package/lib/commonjs/connection-dots/ConnectionDots.web.js +116 -0
  8. package/lib/commonjs/connection-dots/ConnectionDots.web.js.map +1 -0
  9. package/lib/commonjs/connection-dots/ConnectionDotsBase.js +55 -0
  10. package/lib/commonjs/connection-dots/ConnectionDotsBase.js.map +1 -0
  11. package/lib/commonjs/connection-dots/index.js +13 -0
  12. package/lib/commonjs/connection-dots/index.js.map +1 -0
  13. package/lib/commonjs/connection-dots/index.web.js +13 -0
  14. package/lib/commonjs/connection-dots/index.web.js.map +1 -0
  15. package/lib/commonjs/connection-dots/types.js +6 -0
  16. package/lib/commonjs/connection-dots/types.js.map +1 -0
  17. package/lib/commonjs/design-tokens/color-roles.js +112 -0
  18. package/lib/commonjs/design-tokens/color-roles.js.map +1 -0
  19. package/lib/commonjs/design-tokens/index.js +89 -0
  20. package/lib/commonjs/design-tokens/index.js.map +1 -0
  21. package/lib/commonjs/design-tokens/scales.js +168 -0
  22. package/lib/commonjs/design-tokens/scales.js.map +1 -0
  23. package/lib/commonjs/design-tokens/shadows.js +48 -0
  24. package/lib/commonjs/design-tokens/shadows.js.map +1 -0
  25. package/lib/commonjs/design-tokens/shadows.native.js +53 -0
  26. package/lib/commonjs/design-tokens/shadows.native.js.map +1 -0
  27. package/lib/commonjs/design-tokens/tailwind-preset.js +139 -0
  28. package/lib/commonjs/design-tokens/tailwind-preset.js.map +1 -0
  29. package/lib/commonjs/design-tokens/theme-css.js +98 -0
  30. package/lib/commonjs/design-tokens/theme-css.js.map +1 -0
  31. package/lib/commonjs/index.js +208 -93
  32. package/lib/commonjs/index.js.map +1 -1
  33. package/lib/commonjs/index.web.js +223 -108
  34. package/lib/commonjs/index.web.js.map +1 -1
  35. package/lib/commonjs/text-field/index.js +156 -0
  36. package/lib/commonjs/text-field/index.js.map +1 -1
  37. package/lib/module/benefit-list/index.js +114 -0
  38. package/lib/module/benefit-list/index.js.map +1 -0
  39. package/lib/module/benefit-list/types.js +4 -0
  40. package/lib/module/benefit-list/types.js.map +1 -0
  41. package/lib/module/connection-dots/ConnectionDots.js +107 -0
  42. package/lib/module/connection-dots/ConnectionDots.js.map +1 -0
  43. package/lib/module/connection-dots/ConnectionDots.web.js +111 -0
  44. package/lib/module/connection-dots/ConnectionDots.web.js.map +1 -0
  45. package/lib/module/connection-dots/ConnectionDotsBase.js +50 -0
  46. package/lib/module/connection-dots/ConnectionDotsBase.js.map +1 -0
  47. package/lib/module/connection-dots/index.js +4 -0
  48. package/lib/module/connection-dots/index.js.map +1 -0
  49. package/lib/module/connection-dots/index.web.js +4 -0
  50. package/lib/module/connection-dots/index.web.js.map +1 -0
  51. package/lib/module/connection-dots/types.js +4 -0
  52. package/lib/module/connection-dots/types.js.map +1 -0
  53. package/lib/module/design-tokens/color-roles.js +108 -0
  54. package/lib/module/design-tokens/color-roles.js.map +1 -0
  55. package/lib/module/design-tokens/index.js +27 -0
  56. package/lib/module/design-tokens/index.js.map +1 -0
  57. package/lib/module/design-tokens/scales.js +164 -0
  58. package/lib/module/design-tokens/scales.js.map +1 -0
  59. package/lib/module/design-tokens/shadows.js +43 -0
  60. package/lib/module/design-tokens/shadows.js.map +1 -0
  61. package/lib/module/design-tokens/shadows.native.js +48 -0
  62. package/lib/module/design-tokens/shadows.native.js.map +1 -0
  63. package/lib/module/design-tokens/tailwind-preset.js +136 -0
  64. package/lib/module/design-tokens/tailwind-preset.js.map +1 -0
  65. package/lib/module/design-tokens/theme-css.js +94 -0
  66. package/lib/module/design-tokens/theme-css.js.map +1 -0
  67. package/lib/module/index.js +6 -1
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/index.web.js +6 -1
  70. package/lib/module/index.web.js.map +1 -1
  71. package/lib/module/text-field/index.js +158 -2
  72. package/lib/module/text-field/index.js.map +1 -1
  73. package/lib/typescript/commonjs/benefit-list/index.d.ts +6 -0
  74. package/lib/typescript/commonjs/benefit-list/index.d.ts.map +1 -0
  75. package/lib/typescript/commonjs/benefit-list/types.d.ts +35 -0
  76. package/lib/typescript/commonjs/benefit-list/types.d.ts.map +1 -0
  77. package/lib/typescript/commonjs/connection-dots/ConnectionDots.d.ts +4 -0
  78. package/lib/typescript/commonjs/connection-dots/ConnectionDots.d.ts.map +1 -0
  79. package/lib/typescript/commonjs/connection-dots/ConnectionDots.web.d.ts +14 -0
  80. package/lib/typescript/commonjs/connection-dots/ConnectionDots.web.d.ts.map +1 -0
  81. package/lib/typescript/commonjs/connection-dots/ConnectionDotsBase.d.ts +18 -0
  82. package/lib/typescript/commonjs/connection-dots/ConnectionDotsBase.d.ts.map +1 -0
  83. package/lib/typescript/commonjs/connection-dots/index.d.ts +3 -0
  84. package/lib/typescript/commonjs/connection-dots/index.d.ts.map +1 -0
  85. package/lib/typescript/commonjs/connection-dots/index.web.d.ts +3 -0
  86. package/lib/typescript/commonjs/connection-dots/index.web.d.ts.map +1 -0
  87. package/lib/typescript/commonjs/connection-dots/types.d.ts +41 -0
  88. package/lib/typescript/commonjs/connection-dots/types.d.ts.map +1 -0
  89. package/lib/typescript/commonjs/design-tokens/color-roles.d.ts +102 -0
  90. package/lib/typescript/commonjs/design-tokens/color-roles.d.ts.map +1 -0
  91. package/lib/typescript/commonjs/design-tokens/index.d.ts +28 -0
  92. package/lib/typescript/commonjs/design-tokens/index.d.ts.map +1 -0
  93. package/lib/typescript/commonjs/design-tokens/scales.d.ts +152 -0
  94. package/lib/typescript/commonjs/design-tokens/scales.d.ts.map +1 -0
  95. package/lib/typescript/commonjs/design-tokens/shadows.d.ts +34 -0
  96. package/lib/typescript/commonjs/design-tokens/shadows.d.ts.map +1 -0
  97. package/lib/typescript/commonjs/design-tokens/shadows.native.d.ts +19 -0
  98. package/lib/typescript/commonjs/design-tokens/shadows.native.d.ts.map +1 -0
  99. package/lib/typescript/commonjs/design-tokens/tailwind-preset.d.ts +59 -0
  100. package/lib/typescript/commonjs/design-tokens/tailwind-preset.d.ts.map +1 -0
  101. package/lib/typescript/commonjs/design-tokens/theme-css.d.ts +39 -0
  102. package/lib/typescript/commonjs/design-tokens/theme-css.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/index.d.ts +6 -0
  104. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/index.web.d.ts +6 -0
  106. package/lib/typescript/commonjs/index.web.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/text-field/index.d.ts +14 -1
  108. package/lib/typescript/commonjs/text-field/index.d.ts.map +1 -1
  109. package/lib/typescript/module/benefit-list/index.d.ts +6 -0
  110. package/lib/typescript/module/benefit-list/index.d.ts.map +1 -0
  111. package/lib/typescript/module/benefit-list/types.d.ts +35 -0
  112. package/lib/typescript/module/benefit-list/types.d.ts.map +1 -0
  113. package/lib/typescript/module/connection-dots/ConnectionDots.d.ts +4 -0
  114. package/lib/typescript/module/connection-dots/ConnectionDots.d.ts.map +1 -0
  115. package/lib/typescript/module/connection-dots/ConnectionDots.web.d.ts +14 -0
  116. package/lib/typescript/module/connection-dots/ConnectionDots.web.d.ts.map +1 -0
  117. package/lib/typescript/module/connection-dots/ConnectionDotsBase.d.ts +18 -0
  118. package/lib/typescript/module/connection-dots/ConnectionDotsBase.d.ts.map +1 -0
  119. package/lib/typescript/module/connection-dots/index.d.ts +3 -0
  120. package/lib/typescript/module/connection-dots/index.d.ts.map +1 -0
  121. package/lib/typescript/module/connection-dots/index.web.d.ts +3 -0
  122. package/lib/typescript/module/connection-dots/index.web.d.ts.map +1 -0
  123. package/lib/typescript/module/connection-dots/types.d.ts +41 -0
  124. package/lib/typescript/module/connection-dots/types.d.ts.map +1 -0
  125. package/lib/typescript/module/design-tokens/color-roles.d.ts +102 -0
  126. package/lib/typescript/module/design-tokens/color-roles.d.ts.map +1 -0
  127. package/lib/typescript/module/design-tokens/index.d.ts +28 -0
  128. package/lib/typescript/module/design-tokens/index.d.ts.map +1 -0
  129. package/lib/typescript/module/design-tokens/scales.d.ts +152 -0
  130. package/lib/typescript/module/design-tokens/scales.d.ts.map +1 -0
  131. package/lib/typescript/module/design-tokens/shadows.d.ts +34 -0
  132. package/lib/typescript/module/design-tokens/shadows.d.ts.map +1 -0
  133. package/lib/typescript/module/design-tokens/shadows.native.d.ts +19 -0
  134. package/lib/typescript/module/design-tokens/shadows.native.d.ts.map +1 -0
  135. package/lib/typescript/module/design-tokens/tailwind-preset.d.ts +59 -0
  136. package/lib/typescript/module/design-tokens/tailwind-preset.d.ts.map +1 -0
  137. package/lib/typescript/module/design-tokens/theme-css.d.ts +39 -0
  138. package/lib/typescript/module/design-tokens/theme-css.d.ts.map +1 -0
  139. package/lib/typescript/module/index.d.ts +6 -0
  140. package/lib/typescript/module/index.d.ts.map +1 -1
  141. package/lib/typescript/module/index.web.d.ts +6 -0
  142. package/lib/typescript/module/index.web.d.ts.map +1 -1
  143. package/lib/typescript/module/text-field/index.d.ts +14 -1
  144. package/lib/typescript/module/text-field/index.d.ts.map +1 -1
  145. package/package.json +50 -1
  146. package/src/__tests__/BenefitList.test.tsx +108 -0
  147. package/src/__tests__/ConnectionDots.test.tsx +86 -0
  148. package/src/__tests__/TextFieldFloatingLabel.test.tsx +93 -0
  149. package/src/__tests__/design-tokens.test.ts +169 -0
  150. package/src/benefit-list/BenefitList.stories.tsx +50 -0
  151. package/src/benefit-list/index.tsx +133 -0
  152. package/src/benefit-list/types.ts +36 -0
  153. package/src/connection-dots/ConnectionDots.stories.tsx +54 -0
  154. package/src/connection-dots/ConnectionDots.tsx +139 -0
  155. package/src/connection-dots/ConnectionDots.web.tsx +122 -0
  156. package/src/connection-dots/ConnectionDotsBase.tsx +62 -0
  157. package/src/connection-dots/index.ts +2 -0
  158. package/src/connection-dots/index.web.ts +2 -0
  159. package/src/connection-dots/types.ts +41 -0
  160. package/src/design-tokens/color-roles.ts +110 -0
  161. package/src/design-tokens/index.ts +45 -0
  162. package/src/design-tokens/scales.ts +134 -0
  163. package/src/design-tokens/shadows.native.ts +44 -0
  164. package/src/design-tokens/shadows.ts +42 -0
  165. package/src/design-tokens/tailwind-preset.ts +139 -0
  166. package/src/design-tokens/theme-css.ts +99 -0
  167. package/src/index.ts +33 -0
  168. package/src/index.web.ts +33 -0
  169. package/src/text-field/TextField.stories.tsx +46 -0
  170. package/src/text-field/index.tsx +212 -1
package/src/index.web.ts CHANGED
@@ -12,6 +12,35 @@ export type { ViewStyleProp, TextStyleProp } from './styles';
12
12
  export * as tokens from './styles/tokens';
13
13
  export { web, native, ios, android, platform, select } from './styles/platform';
14
14
 
15
+ // Design tokens (Oxy Unified Design Language) — semantic Tailwind/NativeWind
16
+ // vocabulary. The Tailwind preset is consumed from `@oxyhq/bloom/tailwind-preset`
17
+ // in a config file; these named exports cover programmatic/runtime use.
18
+ export {
19
+ bloomTailwindPreset,
20
+ bloomThemeCss,
21
+ bloomThemeBlock,
22
+ FILL_ROLES,
23
+ TEXT_ROLES,
24
+ BORDER_ROLES,
25
+ SPACING,
26
+ RADIUS,
27
+ BORDER_WIDTH,
28
+ TYPOGRAPHY,
29
+ FONT_FAMILY_VARS,
30
+ SHADOW_BOX,
31
+ bloomShadowStyle,
32
+ } from './design-tokens';
33
+ export type {
34
+ TailwindPreset,
35
+ TailwindPresetThemeExtend,
36
+ FillRole,
37
+ TextRole,
38
+ BorderRole,
39
+ TypeRole,
40
+ TypeRoleName,
41
+ ShadowRole,
42
+ } from './design-tokens';
43
+
15
44
  // Hooks
16
45
  export { useInteractionState } from './hooks/useInteractionState';
17
46
  export { useDelayedLoading } from './hooks/useDelayedLoading';
@@ -74,6 +103,10 @@ export * as Skeleton from './skeleton';
74
103
  export * as Grid from './grid';
75
104
  export { Fill } from './fill';
76
105
  export { IconCircle } from './icon-circle';
106
+ export { ConnectionDots } from './connection-dots/index.web';
107
+ export type { ConnectionDotsProps } from './connection-dots/index.web';
108
+ export { BenefitRow, BenefitList } from './benefit-list';
109
+ export type { BenefitRowProps, BenefitListProps } from './benefit-list';
77
110
 
78
111
  // Form components
79
112
  export * from './text-field';
@@ -43,6 +43,34 @@ function ControlledField({
43
43
  );
44
44
  }
45
45
 
46
+ function FloatingField({
47
+ label,
48
+ initial = '',
49
+ isInvalid,
50
+ editable = true,
51
+ }: {
52
+ label: string;
53
+ initial?: string;
54
+ isInvalid?: boolean;
55
+ editable?: boolean;
56
+ }) {
57
+ const [value, setValue] = useState(initial);
58
+ return (
59
+ <View style={{ width: 320 }}>
60
+ <TextField isInvalid={isInvalid}>
61
+ <TextFieldInput
62
+ floatingLabel
63
+ label={label}
64
+ value={value}
65
+ onChangeText={setValue}
66
+ editable={editable}
67
+ isInvalid={isInvalid}
68
+ />
69
+ </TextField>
70
+ </View>
71
+ );
72
+ }
73
+
46
74
  export const Basic: Story = {
47
75
  render: () => (
48
76
  <ControlledField label="Username" placeholder="oxylander" />
@@ -88,3 +116,21 @@ export const Composition: Story = {
88
116
  </View>
89
117
  ),
90
118
  };
119
+
120
+ export const FloatingLabel: Story = {
121
+ render: () => <FloatingField label="Email" />,
122
+ };
123
+
124
+ export const FloatingLabelWithValue: Story = {
125
+ render: () => <FloatingField label="Email" initial="nate@oxy.so" />,
126
+ };
127
+
128
+ export const FloatingLabelComposition: Story = {
129
+ render: () => (
130
+ <View style={{ gap: 16 }}>
131
+ <FloatingField label="Full name" />
132
+ <FloatingField label="Email" initial="ada@oxy.so" />
133
+ <FloatingField label="Email" initial="invalid-email" isInvalid />
134
+ </View>
135
+ ),
136
+ };
@@ -1,6 +1,7 @@
1
- import { createContext, useContext, useMemo, useRef } from 'react';
1
+ import { createContext, useContext, useEffect, useMemo, useRef } from 'react';
2
2
  import {
3
3
  type AccessibilityProps,
4
+ Animated,
4
5
  Platform,
5
6
  StyleSheet,
6
7
  TextInput,
@@ -9,6 +10,7 @@ import {
9
10
  View,
10
11
  type ViewStyle,
11
12
  } from 'react-native';
13
+ import { useReducedMotion } from 'react-native-reanimated';
12
14
 
13
15
  import { useTheme } from '../theme/use-theme';
14
16
  import { useInteractionState } from '../hooks/useInteractionState';
@@ -132,6 +134,19 @@ export type TextFieldInputProps = Omit<
132
134
  isInvalid?: boolean;
133
135
  inputRef?: React.RefObject<TextInput | null> | React.ForwardedRef<TextInput>;
134
136
  placeholder?: string | null | undefined;
137
+ /**
138
+ * Render the field with a Material-style floating label. When `true`, the
139
+ * {@link label} sits inside the field as the placeholder while the input is
140
+ * empty AND unfocused; on focus OR when a value is present it animates up to a
141
+ * small caption pinned to the top of the field and the typed value shows
142
+ * below it. Opt-in — the default (`false`) keeps the existing chrome where the
143
+ * label lives above the field (`TextFieldLabel`) and the placeholder is plain.
144
+ *
145
+ * Cross-platform: the animation is driven by focus + value-presence state (no
146
+ * CSS `:placeholder-shown` / `peer-focus`), so web and native behave
147
+ * identically. Respects reduced-motion (snaps instead of animating).
148
+ */
149
+ floatingLabel?: boolean;
135
150
  };
136
151
 
137
152
  export function TextFieldInput({
@@ -144,6 +159,7 @@ export function TextFieldInput({
144
159
  isInvalid,
145
160
  inputRef,
146
161
  style,
162
+ floatingLabel = false,
147
163
  ...rest
148
164
  }: TextFieldInputProps) {
149
165
  const theme = useTheme();
@@ -162,6 +178,11 @@ export function TextFieldInput({
162
178
  value={value}
163
179
  onChangeText={onChangeText}
164
180
  isInvalid={isInvalid}
181
+ floatingLabel={floatingLabel}
182
+ style={style}
183
+ inputRef={inputRef}
184
+ onFocus={onFocus}
185
+ onBlur={onBlur}
165
186
  {...rest}
166
187
  />
167
188
  </TextField>
@@ -174,6 +195,26 @@ export function TextFieldInput({
174
195
  ),
175
196
  );
176
197
 
198
+ if (floatingLabel) {
199
+ return (
200
+ <FloatingLabelInput
201
+ label={label}
202
+ value={value}
203
+ onChangeText={onChangeText}
204
+ onFocus={onFocus}
205
+ onBlur={onBlur}
206
+ isInvalid={isInvalid}
207
+ refs={refs}
208
+ style={style}
209
+ chromeHover={chromeHover}
210
+ chromeFocus={chromeFocus}
211
+ chromeError={chromeError}
212
+ chromeErrorHover={chromeErrorHover}
213
+ {...rest}
214
+ />
215
+ );
216
+ }
217
+
177
218
  const flattened: TextStyle = StyleSheet.flatten([
178
219
  a.relative,
179
220
  a.z_20,
@@ -246,6 +287,176 @@ export function TextFieldInput({
246
287
  );
247
288
  }
248
289
 
290
+ /** Vertical band the floating label travels between rest (centered) and floated (top). */
291
+ const FLOAT_TRAVEL = 11;
292
+ /** Resting label size (matches the input text) vs. floated caption size. */
293
+ const FLOAT_LABEL_REST_SIZE = a.text_md.fontSize;
294
+ const FLOAT_LABEL_FLOAT_SIZE = a.text_xs.fontSize;
295
+
296
+ type FloatingLabelInputProps = Omit<
297
+ TextInputProps,
298
+ 'value' | 'onChangeText' | 'placeholder'
299
+ > & {
300
+ label: string;
301
+ value?: string;
302
+ onChangeText?: (value: string) => void;
303
+ isInvalid?: boolean;
304
+ refs: (instance: TextInput | null) => void;
305
+ chromeHover: ViewStyle;
306
+ chromeFocus: ViewStyle;
307
+ chromeError: ViewStyle;
308
+ chromeErrorHover: ViewStyle;
309
+ };
310
+
311
+ /**
312
+ * The floating-label rendering of {@link TextFieldInput}.
313
+ *
314
+ * State, not CSS, drives the motion so web and native behave identically:
315
+ * `floated = focused || hasValue`. A single `Animated.Value` (0 → rest, 1 →
316
+ * floated) interpolates the label's `top`, `fontSize`, and `color`; the same
317
+ * `Animated` API maps to CSS transforms on web via react-native-web, so no
318
+ * `:placeholder-shown` / `peer-focus` is needed. Reduced-motion snaps the value
319
+ * (duration 0) instead of animating.
320
+ *
321
+ * The native placeholder is intentionally suppressed — the floating label IS the
322
+ * placeholder at rest. The label still drives `accessibilityLabel`, and the
323
+ * chrome / focus / error / disabled infra is shared with the default variant.
324
+ */
325
+ function FloatingLabelInput({
326
+ label,
327
+ value,
328
+ onChangeText,
329
+ onFocus,
330
+ onBlur,
331
+ isInvalid,
332
+ refs,
333
+ style,
334
+ chromeHover,
335
+ chromeFocus,
336
+ chromeError,
337
+ chromeErrorHover,
338
+ ...rest
339
+ }: FloatingLabelInputProps) {
340
+ const theme = useTheme();
341
+ const ctx = useContext(Context);
342
+ const reduceMotion = useReducedMotion();
343
+
344
+ const hasValue = (value?.length ?? 0) > 0;
345
+ const floated = ctx.focused || hasValue;
346
+ const invalid = ctx.isInvalid || isInvalid;
347
+
348
+ const progress = useRef(new Animated.Value(floated ? 1 : 0)).current;
349
+
350
+ useEffect(() => {
351
+ const target = floated ? 1 : 0;
352
+ if (reduceMotion) {
353
+ progress.setValue(target);
354
+ return;
355
+ }
356
+ const timing = Animated.timing(progress, {
357
+ toValue: target,
358
+ duration: tokens.animation.duration.fast,
359
+ useNativeDriver: false,
360
+ });
361
+ timing.start();
362
+ return () => timing.stop();
363
+ }, [floated, reduceMotion, progress]);
364
+
365
+ const labelTop = progress.interpolate({
366
+ inputRange: [0, 1],
367
+ outputRange: [16, 16 - FLOAT_TRAVEL],
368
+ });
369
+ const labelSize = progress.interpolate({
370
+ inputRange: [0, 1],
371
+ outputRange: [FLOAT_LABEL_REST_SIZE, FLOAT_LABEL_FLOAT_SIZE],
372
+ });
373
+ const labelColor = progress.interpolate({
374
+ inputRange: [0, 1],
375
+ outputRange: [
376
+ theme.colors.textSecondary,
377
+ invalid ? theme.colors.negative : theme.colors.primary,
378
+ ],
379
+ });
380
+
381
+ const inputStyle: TextStyle = StyleSheet.flatten([
382
+ a.relative,
383
+ a.z_20,
384
+ a.flex_1,
385
+ a.text_md,
386
+ { color: theme.colors.text },
387
+ a.px_xs,
388
+ {
389
+ lineHeight: a.text_md.fontSize * 1.2,
390
+ minWidth: 0,
391
+ paddingTop: 26,
392
+ paddingBottom: 8,
393
+ },
394
+ android({ paddingTop: 24, paddingBottom: 6 }),
395
+ web({ paddingTop: 25, paddingBottom: 7 }),
396
+ style,
397
+ ]) as TextStyle;
398
+
399
+ return (
400
+ <>
401
+ <Animated.Text
402
+ accessibilityElementsHidden
403
+ importantForAccessibility="no-hide-descendants"
404
+ aria-hidden
405
+ numberOfLines={1}
406
+ style={[
407
+ a.absolute,
408
+ a.z_30,
409
+ a.pointer_events_none,
410
+ {
411
+ left: tokens.space.xs + tokens.space._2xs,
412
+ top: labelTop,
413
+ fontSize: labelSize,
414
+ color: labelColor,
415
+ },
416
+ ]}>
417
+ {label}
418
+ </Animated.Text>
419
+
420
+ <TextInput
421
+ accessibilityHint={undefined}
422
+ hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
423
+ {...rest}
424
+ accessibilityLabel={label}
425
+ ref={refs}
426
+ value={value}
427
+ onChangeText={onChangeText}
428
+ onFocus={(e) => {
429
+ ctx.onFocus();
430
+ onFocus?.(e);
431
+ }}
432
+ onBlur={(e) => {
433
+ ctx.onBlur();
434
+ onBlur?.(e);
435
+ }}
436
+ placeholder={undefined}
437
+ placeholderTextColor={theme.colors.textSecondary}
438
+ keyboardAppearance={theme.mode === 'light' ? 'light' : 'dark'}
439
+ style={inputStyle}
440
+ />
441
+
442
+ <View
443
+ style={[
444
+ a.z_10,
445
+ a.absolute,
446
+ a.inset_0,
447
+ { borderRadius: 10 },
448
+ { backgroundColor: theme.colors.contrast50 },
449
+ { borderColor: 'transparent', borderWidth: 2 },
450
+ ctx.hovered ? chromeHover : {},
451
+ ctx.focused ? chromeFocus : {},
452
+ invalid ? chromeError : {},
453
+ invalid && (ctx.hovered || ctx.focused) ? chromeErrorHover : {},
454
+ ]}
455
+ />
456
+ </>
457
+ );
458
+ }
459
+
249
460
  export function TextFieldLabel({
250
461
  nativeID,
251
462
  children,