@utilitywarehouse/hearth-react-native 0.11.0 → 0.12.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 (100) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/CHANGELOG.md +10 -0
  4. package/build/components/Banner/Banner.context.d.ts +7 -0
  5. package/build/components/Banner/Banner.context.js +8 -0
  6. package/build/components/Banner/Banner.js +10 -40
  7. package/build/components/Banner/Banner.props.d.ts +3 -5
  8. package/build/components/Banner/BannerIllustration.d.ts +4 -0
  9. package/build/components/Banner/BannerIllustration.js +53 -0
  10. package/build/components/Banner/BannerImage.d.ts +4 -0
  11. package/build/components/Banner/BannerImage.js +53 -0
  12. package/build/components/Banner/index.d.ts +2 -0
  13. package/build/components/Banner/index.js +2 -0
  14. package/build/components/Card/CardAction/CardAction.props.d.ts +2 -3
  15. package/build/components/Card/CardAction/CardActionRoot.js +1 -2
  16. package/build/components/Checkbox/Checkbox.js +1 -2
  17. package/build/components/Checkbox/Checkbox.props.d.ts +3 -3
  18. package/build/components/Checkbox/CheckboxImage.d.ts +2 -1
  19. package/build/components/Checkbox/CheckboxImage.js +8 -1
  20. package/build/components/ExpandableCard/ExpandableCard.props.d.ts +1 -2
  21. package/build/components/ExpandableCard/ExpandableCardTrigger.props.d.ts +4 -5
  22. package/build/components/ExpandableCard/ExpandableCardTriggerRoot.js +1 -14
  23. package/build/components/HighlightBanner/HighlightBanner.js +2 -6
  24. package/build/components/HighlightBanner/HighlightBanner.props.d.ts +2 -3
  25. package/build/components/HighlightBanner/HighlightBannerImage.d.ts +4 -0
  26. package/build/components/HighlightBanner/HighlightBannerImage.js +18 -0
  27. package/build/components/HighlightBanner/index.d.ts +1 -0
  28. package/build/components/HighlightBanner/index.js +1 -0
  29. package/build/components/Input/Input.d.ts +5 -7
  30. package/build/components/Input/Input.js +11 -4
  31. package/build/components/Input/InputField.d.ts +4 -7
  32. package/build/components/Input/InputField.js +6 -5
  33. package/build/components/List/ListItem/ListItem.props.d.ts +2 -2
  34. package/build/components/List/ListItem/ListItemRoot.js +1 -2
  35. package/build/components/Modal/Modal.js +2 -6
  36. package/build/components/Modal/Modal.props.d.ts +3 -2
  37. package/build/components/Modal/Modal.web.js +2 -6
  38. package/build/components/Modal/ModalImage.d.ts +4 -0
  39. package/build/components/Modal/ModalImage.js +18 -0
  40. package/build/components/Modal/index.d.ts +1 -0
  41. package/build/components/Modal/index.js +1 -0
  42. package/build/components/Radio/Radio.js +1 -2
  43. package/build/components/Radio/Radio.props.d.ts +3 -3
  44. package/build/components/Radio/RadioImage.d.ts +2 -1
  45. package/build/components/Radio/RadioImage.js +8 -1
  46. package/build/utils/index.d.ts +2 -1
  47. package/build/utils/index.js +2 -1
  48. package/build/utils/isThemedImageProps.d.ts +4 -0
  49. package/build/utils/isThemedImageProps.js +4 -0
  50. package/package.json +2 -2
  51. package/src/components/Banner/Banner.context.ts +11 -0
  52. package/src/components/Banner/Banner.docs.mdx +55 -37
  53. package/src/components/Banner/Banner.props.ts +3 -5
  54. package/src/components/Banner/Banner.stories.tsx +86 -57
  55. package/src/components/Banner/Banner.tsx +24 -67
  56. package/src/components/Banner/BannerIllustration.tsx +63 -0
  57. package/src/components/Banner/BannerImage.tsx +63 -0
  58. package/src/components/Banner/index.ts +2 -0
  59. package/src/components/Card/Card.docs.mdx +4 -4
  60. package/src/components/Card/CardAction/CardAction.props.ts +2 -3
  61. package/src/components/Card/CardAction/CardAction.stories.tsx +4 -3
  62. package/src/components/Card/CardAction/CardActionRoot.tsx +4 -5
  63. package/src/components/Checkbox/Checkbox.docs.mdx +23 -4
  64. package/src/components/Checkbox/Checkbox.props.ts +3 -3
  65. package/src/components/Checkbox/Checkbox.stories.tsx +14 -8
  66. package/src/components/Checkbox/Checkbox.tsx +1 -2
  67. package/src/components/Checkbox/CheckboxImage.tsx +8 -3
  68. package/src/components/ExpandableCard/ExpandableCard.docs.mdx +2 -2
  69. package/src/components/ExpandableCard/ExpandableCard.props.ts +1 -2
  70. package/src/components/ExpandableCard/ExpandableCard.stories.tsx +3 -3
  71. package/src/components/ExpandableCard/ExpandableCardTrigger.props.ts +4 -5
  72. package/src/components/ExpandableCard/ExpandableCardTriggerRoot.tsx +2 -17
  73. package/src/components/HighlightBanner/HighlightBanner.docs.mdx +73 -42
  74. package/src/components/HighlightBanner/HighlightBanner.props.ts +2 -3
  75. package/src/components/HighlightBanner/HighlightBanner.stories.tsx +85 -60
  76. package/src/components/HighlightBanner/HighlightBanner.tsx +3 -10
  77. package/src/components/HighlightBanner/HighlightBannerImage.tsx +20 -0
  78. package/src/components/HighlightBanner/index.ts +1 -0
  79. package/src/components/Input/Input.stories.tsx +76 -3
  80. package/src/components/Input/Input.tsx +110 -98
  81. package/src/components/Input/InputField.tsx +27 -26
  82. package/src/components/List/List.docs.mdx +15 -9
  83. package/src/components/List/List.stories.tsx +2 -2
  84. package/src/components/List/ListItem/ListItem.props.ts +2 -2
  85. package/src/components/List/ListItem/ListItemRoot.tsx +2 -3
  86. package/src/components/Modal/Modal.docs.mdx +16 -4
  87. package/src/components/Modal/Modal.props.ts +3 -2
  88. package/src/components/Modal/Modal.stories.tsx +2 -5
  89. package/src/components/Modal/Modal.tsx +2 -6
  90. package/src/components/Modal/Modal.web.tsx +2 -6
  91. package/src/components/Modal/ModalImage.tsx +20 -0
  92. package/src/components/Modal/index.ts +1 -0
  93. package/src/components/PillGroup/PillGroup.stories.tsx +1 -1
  94. package/src/components/Radio/Radio.docs.mdx +21 -8
  95. package/src/components/Radio/Radio.props.ts +3 -3
  96. package/src/components/Radio/Radio.stories.tsx +15 -11
  97. package/src/components/Radio/Radio.tsx +1 -2
  98. package/src/components/Radio/RadioImage.tsx +8 -3
  99. package/src/utils/index.ts +2 -1
  100. package/src/utils/isThemedImageProps.ts +8 -0
@@ -4,6 +4,7 @@ import { Button } from '../Button';
4
4
  import { Flex } from '../Flex';
5
5
  import { Link } from '../Link';
6
6
  import HighlightBanner from './HighlightBanner';
7
+ import HighlightBannerImage from './HighlightBannerImage';
7
8
 
8
9
  const meta = {
9
10
  title: 'Stories / HighlightBanner',
@@ -49,11 +50,13 @@ const meta = {
49
50
  heading: 'Featured Content',
50
51
  headingColor: 'highlight',
51
52
  description: 'This is a description of the featured content that appears below the image.',
52
- image: {
53
- source: {
54
- uri: 'https://images.unsplash.com/photo-1506126613408-eca07ce68773?w=800&q=80',
55
- },
56
- },
53
+ image: (
54
+ <HighlightBannerImage
55
+ source={{
56
+ uri: 'https://images.unsplash.com/photo-1506126613408-eca07ce68773?w=800&q=80',
57
+ }}
58
+ />
59
+ ),
57
60
  },
58
61
  } satisfies Meta<typeof HighlightBanner>;
59
62
 
@@ -105,11 +108,13 @@ export const ColorVariants: Story = {
105
108
  {...args}
106
109
  heading="Energy Blue"
107
110
  headingColor="energy"
108
- image={{
109
- source: {
110
- uri: 'https://images.unsplash.com/photo-1473186578172-c141e6798cf4?w=800&q=80',
111
- },
112
- }}
111
+ image={
112
+ <HighlightBannerImage
113
+ source={{
114
+ uri: 'https://images.unsplash.com/photo-1473186578172-c141e6798cf4?w=800&q=80',
115
+ }}
116
+ />
117
+ }
113
118
  description="Featured content with energy blue heading"
114
119
  link={<Link onPress={() => console.log('pressed')}>Learn more</Link>}
115
120
  />
@@ -117,22 +122,26 @@ export const ColorVariants: Story = {
117
122
  {...args}
118
123
  heading="Broadband Green"
119
124
  headingColor="broadband"
120
- image={{
121
- source: {
122
- uri: 'https://images.unsplash.com/photo-1501594907352-04cda38ebc29?w=800&q=80',
123
- },
124
- }}
125
+ image={
126
+ <HighlightBannerImage
127
+ source={{
128
+ uri: 'https://images.unsplash.com/photo-1501594907352-04cda38ebc29?w=800&q=80',
129
+ }}
130
+ />
131
+ }
125
132
  description="Featured content with broadband green heading"
126
133
  />
127
134
  <HighlightBanner
128
135
  {...args}
129
136
  heading="Insurance Orange"
130
137
  headingColor="insurance"
131
- image={{
132
- source: {
133
- uri: 'https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=800&q=80',
134
- },
135
- }}
138
+ image={
139
+ <HighlightBannerImage
140
+ source={{
141
+ uri: 'https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=800&q=80',
142
+ }}
143
+ />
144
+ }
136
145
  description="Featured content with insurance orange heading"
137
146
  button={<Button onPress={() => console.log('pressed')}>Get Started</Button>}
138
147
  />
@@ -140,11 +149,13 @@ export const ColorVariants: Story = {
140
149
  {...args}
141
150
  heading="Cashback Lilac"
142
151
  headingColor="cashback"
143
- image={{
144
- source: {
145
- uri: 'https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0?w=800&q=80',
146
- },
147
- }}
152
+ image={
153
+ <HighlightBannerImage
154
+ source={{
155
+ uri: 'https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0?w=800&q=80',
156
+ }}
157
+ />
158
+ }
148
159
  description="Featured content with cashback lilac heading"
149
160
  button={<Button onPress={() => console.log('pressed')}>Get Started</Button>}
150
161
  />
@@ -152,11 +163,13 @@ export const ColorVariants: Story = {
152
163
  {...args}
153
164
  heading="Pig Pink"
154
165
  headingColor="pig"
155
- image={{
156
- source: {
157
- uri: 'https://images.unsplash.com/photo-1494526585095-c41746248156?w=800&q=80',
158
- },
159
- }}
166
+ image={
167
+ <HighlightBannerImage
168
+ source={{
169
+ uri: 'https://images.unsplash.com/photo-1494526585095-c41746248156?w=800&q=80',
170
+ }}
171
+ />
172
+ }
160
173
  description="Featured content with pig pink heading"
161
174
  link={<Link onPress={() => console.log('pressed')}>Learn more</Link>}
162
175
  />
@@ -164,22 +177,26 @@ export const ColorVariants: Story = {
164
177
  {...args}
165
178
  heading="Mobile Rose"
166
179
  headingColor="mobile"
167
- image={{
168
- source: {
169
- uri: 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=800&q=80',
170
- },
171
- }}
180
+ image={
181
+ <HighlightBannerImage
182
+ source={{
183
+ uri: 'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=800&q=80',
184
+ }}
185
+ />
186
+ }
172
187
  description="Featured content with mobile rose heading"
173
188
  />
174
189
  <HighlightBanner
175
190
  {...args}
176
191
  heading="Highlight Yellow"
177
192
  headingColor="highlight"
178
- image={{
179
- source: {
180
- uri: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?w=800&q=80',
181
- },
182
- }}
193
+ image={
194
+ <HighlightBannerImage
195
+ source={{
196
+ uri: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?w=800&q=80',
197
+ }}
198
+ />
199
+ }
183
200
  description="Featured content with highlight yellow heading"
184
201
  />
185
202
  </Flex>
@@ -226,11 +243,13 @@ export const DifferentImages: Story = {
226
243
  {...args}
227
244
  heading="Nature Landscape"
228
245
  headingColor="broadband"
229
- image={{
230
- source: {
231
- uri: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&q=80',
232
- },
233
- }}
246
+ image={
247
+ <HighlightBannerImage
248
+ source={{
249
+ uri: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800&q=80',
250
+ }}
251
+ />
252
+ }
234
253
  description="Beautiful mountain landscape with clear blue sky"
235
254
  link={<Link onPress={() => console.log('pressed')}>View gallery</Link>}
236
255
  />
@@ -240,11 +259,13 @@ export const DifferentImages: Story = {
240
259
  {...args}
241
260
  heading="Urban Architecture"
242
261
  headingColor="highlight"
243
- image={{
244
- source: {
245
- uri: 'https://images.unsplash.com/photo-1480714378408-67cf0d13bc1b?w=800&q=80',
246
- },
247
- }}
262
+ image={
263
+ <HighlightBannerImage
264
+ source={{
265
+ uri: 'https://images.unsplash.com/photo-1480714378408-67cf0d13bc1b?w=800&q=80',
266
+ }}
267
+ />
268
+ }
248
269
  description="Modern city buildings and urban design"
249
270
  button={<Button onPress={() => console.log('pressed')}>Explore Cities</Button>}
250
271
  />
@@ -254,11 +275,13 @@ export const DifferentImages: Story = {
254
275
  {...args}
255
276
  heading="Ocean Waves"
256
277
  headingColor="energy"
257
- image={{
258
- source: {
259
- uri: 'https://images.unsplash.com/photo-1505142468610-359e7d316be0?w=800&q=80',
260
- },
261
- }}
278
+ image={
279
+ <HighlightBannerImage
280
+ source={{
281
+ uri: 'https://images.unsplash.com/photo-1505142468610-359e7d316be0?w=800&q=80',
282
+ }}
283
+ />
284
+ }
262
285
  description="Stunning ocean views and coastal beauty"
263
286
  />
264
287
  </View>
@@ -275,11 +298,13 @@ export const LongContent: Story = {
275
298
  {...args}
276
299
  heading="Extended Information Card"
277
300
  headingColor="energy"
278
- image={{
279
- source: {
280
- uri: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=800&q=80',
281
- },
282
- }}
301
+ image={
302
+ <HighlightBannerImage
303
+ source={{
304
+ uri: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=800&q=80',
305
+ }}
306
+ />
307
+ }
283
308
  description="This is a much longer description that demonstrates how the component handles extended text content. It includes multiple sentences and provides more detailed information about the featured content, ensuring the layout remains clean and readable even with more text."
284
309
  button={<Button onPress={() => console.log('pressed')}>Read Full Article</Button>}
285
310
  />
@@ -1,4 +1,4 @@
1
- import { Image, View } from 'react-native';
1
+ import { View } from 'react-native';
2
2
  import { StyleSheet } from 'react-native-unistyles';
3
3
  import { BodyText } from '../BodyText';
4
4
  import { Card } from '../Card';
@@ -26,11 +26,7 @@ const HighlightBanner = ({
26
26
  {heading}
27
27
  </BodyText>
28
28
  </View>
29
- {!!image && (
30
- <View style={styles.imageContainer(imageContainerHeight)}>
31
- <Image resizeMode="cover" {...image} style={[styles.image, image?.style]} />
32
- </View>
33
- )}
29
+ {!!image && <View style={styles.imageContainer(imageContainerHeight)}>{image}</View>}
34
30
  <View style={styles.footer}>
35
31
  <BodyText size="md" textAlign="center">
36
32
  {description}
@@ -99,10 +95,7 @@ const styles = StyleSheet.create(theme => ({
99
95
  width: '100%',
100
96
  height,
101
97
  }),
102
- image: {
103
- width: '100%',
104
- height: '100%',
105
- },
98
+
106
99
  footer: {
107
100
  padding: theme.components.banner.highlight.padding,
108
101
  gap: theme.components.banner.highlight.content.gap,
@@ -0,0 +1,20 @@
1
+ import { Image, ImageProps } from 'react-native';
2
+ import { StyleSheet } from 'react-native-unistyles';
3
+ import { isThemedImageProps } from '../../utils';
4
+ import { ThemedImage, ThemedImageProps } from '../ThemedImage';
5
+
6
+ const HighlightBannerImage = (props: ImageProps | ThemedImageProps) => {
7
+ if (isThemedImageProps(props)) {
8
+ return <ThemedImage {...props} style={[styles.image, props.style]} />;
9
+ }
10
+ return <Image resizeMode="cover" {...props} style={[styles.image, props.style]} />;
11
+ };
12
+
13
+ const styles = StyleSheet.create({
14
+ image: {
15
+ width: '100%',
16
+ height: '100%',
17
+ },
18
+ });
19
+
20
+ export default HighlightBannerImage;
@@ -1,2 +1,3 @@
1
1
  export { default as HighlightBanner } from './HighlightBanner';
2
2
  export type { default as HighlightBannerProps } from './HighlightBanner.props';
3
+ export { default as HighlightBannerImage } from './HighlightBannerImage';
@@ -1,10 +1,13 @@
1
- import { Meta, StoryObj } from '@storybook/react-vite';
1
+ import { Meta, StoryObj } from '@storybook/react-native';
2
2
  import * as Icons from '@utilitywarehouse/hearth-react-native-icons';
3
3
  import { EmailMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
4
- import { useState } from 'react';
5
- import { NativeSyntheticEvent, TextInputChangeEventData } from 'react-native';
4
+ import { useRef, useState } from 'react';
5
+ import { NativeSyntheticEvent, TextInput, TextInputChangeEventData, View } from 'react-native';
6
6
  import { Input } from '.';
7
7
  import { VariantTitle } from '../../../docs/components';
8
+ import { useTheme } from '../../hooks';
9
+ import { BodyText } from '../BodyText';
10
+ import { Button } from '../Button';
8
11
  import { Flex } from '../Flex';
9
12
 
10
13
  const meta = {
@@ -73,6 +76,7 @@ export default meta;
73
76
  type Story = StoryObj<typeof meta>;
74
77
 
75
78
  export const Playground: Story = {
79
+ // @ts-expect-error - This is a playground
76
80
  render: ({ leadingIcon: leading, trailingIcon: trailing, ...args }) => {
77
81
  // @ts-expect-error - This is a playground
78
82
  const leadingIcon = leading === 'none' ? undefined : Icons[leading];
@@ -152,3 +156,72 @@ export const Variants: Story = {
152
156
  );
153
157
  },
154
158
  };
159
+
160
+ export const RefTest: Story = {
161
+ parameters: {
162
+ controls: { include: [] },
163
+ },
164
+ render: () => {
165
+ const inputRef = useRef<TextInput>(null);
166
+ const [refStatus, setRefStatus] = useState('Ref not tested yet');
167
+ const theme = useTheme();
168
+
169
+ const handleFocus = () => {
170
+ if (inputRef.current) {
171
+ inputRef.current.focus();
172
+ setRefStatus('✅ Ref works! Input focused programmatically');
173
+ } else {
174
+ setRefStatus('❌ Ref is null');
175
+ }
176
+ };
177
+
178
+ const handleBlur = () => {
179
+ if (inputRef.current) {
180
+ inputRef.current.blur();
181
+ setRefStatus('✅ Ref works! Input blurred programmatically');
182
+ } else {
183
+ setRefStatus('❌ Ref is null');
184
+ }
185
+ };
186
+
187
+ const handleClear = () => {
188
+ if (inputRef.current) {
189
+ inputRef.current.clear();
190
+ setRefStatus('✅ Ref works! Input cleared programmatically');
191
+ } else {
192
+ setRefStatus('❌ Ref is null');
193
+ }
194
+ };
195
+
196
+ return (
197
+ <Flex direction="column" space="lg">
198
+ <VariantTitle title="Ref Test">
199
+ <Input
200
+ ref={inputRef}
201
+ placeholder="Test ref functionality"
202
+ defaultValue="Try the buttons below"
203
+ />
204
+ </VariantTitle>
205
+ <VariantTitle title="Status">
206
+ <Flex direction="column" space="sm">
207
+ <Flex direction="row" space="sm">
208
+ <Button onPress={handleFocus}>Focus Input</Button>
209
+ <Button onPress={handleBlur}>Blur Input</Button>
210
+ <Button onPress={handleClear}>Clear Input</Button>
211
+ </Flex>
212
+ <View
213
+ style={{
214
+ marginTop: 8,
215
+ padding: 8,
216
+ backgroundColor: theme.color.background.secondary,
217
+ borderRadius: 4,
218
+ }}
219
+ >
220
+ <BodyText>{refStatus}</BodyText>
221
+ </View>
222
+ </Flex>
223
+ </VariantTitle>
224
+ </Flex>
225
+ );
226
+ },
227
+ };
@@ -1,5 +1,6 @@
1
1
  import { createInput } from '@gluestack-ui/input';
2
- import { ComponentType, useState } from 'react';
2
+ import { ComponentType, forwardRef, useImperativeHandle, useRef, useState } from 'react';
3
+ import { TextInput } from 'react-native';
3
4
  import type InputProps from './Input.props';
4
5
 
5
6
  import {
@@ -29,108 +30,119 @@ export const InputSlot = InputComponent.Slot;
29
30
  export const InputField = InputComponent.Input;
30
31
  export const InputIcon = InputComponent.Icon;
31
32
 
32
- const Input = ({
33
- validationStatus = 'initial',
34
- children,
35
- disabled,
36
- focused,
37
- readonly,
38
- leadingIcon,
39
- trailingIcon,
40
- type,
41
- showPasswordToggle = true,
42
- onClear,
43
- format,
44
- loading,
45
- clearable = false,
46
- required,
47
- inBottomSheet = false,
48
- ...props
49
- }: InputProps) => {
50
- const formFieldContext = useFormFieldContext();
51
- const { disabled: formFieldDisabled } = formFieldContext;
52
- const validationStatusFromContext = formFieldContext?.validationStatus ?? validationStatus;
53
- const isRequired = formFieldContext?.required ?? required;
54
- const [fieldType, setFieldType] = useState<'password' | 'text'>(
55
- type === 'password' ? 'password' : 'text'
56
- );
57
- const { color } = useTheme();
33
+ const Input = forwardRef<TextInput, InputProps>(
34
+ (
35
+ {
36
+ validationStatus = 'initial',
37
+ children,
38
+ disabled,
39
+ focused,
40
+ readonly,
41
+ leadingIcon,
42
+ trailingIcon,
43
+ type,
44
+ showPasswordToggle = true,
45
+ onClear,
46
+ format,
47
+ loading,
48
+ clearable = false,
49
+ required,
50
+ inBottomSheet = false,
51
+ ...props
52
+ },
53
+ ref
54
+ ) => {
55
+ const formFieldContext = useFormFieldContext();
56
+ const { disabled: formFieldDisabled } = formFieldContext;
57
+ const validationStatusFromContext = formFieldContext?.validationStatus ?? validationStatus;
58
+ const isRequired = formFieldContext?.required ?? required;
59
+ const [fieldType, setFieldType] = useState<'password' | 'text'>(
60
+ type === 'password' ? 'password' : 'text'
61
+ );
62
+ const { color } = useTheme();
63
+ const inputRef = useRef<TextInput>(null);
58
64
 
59
- const shouldShowPasswordToggle = type === 'password' && showPasswordToggle;
60
- const shouldShowClear = clearable && !!(props as InputWithoutChildrenProps)?.value;
65
+ // Expose TextInput methods to parent components
66
+ useImperativeHandle(ref, () => inputRef.current as TextInput, []);
61
67
 
62
- const toggleFieldType = () => {
63
- setFieldType(fieldType === 'password' ? 'text' : 'password');
64
- };
68
+ const shouldShowPasswordToggle = type === 'password' && showPasswordToggle;
69
+ const shouldShowClear = clearable && !!(props as InputWithoutChildrenProps)?.value;
65
70
 
66
- const leadingIconComponent = ((): ComponentType | undefined => {
67
- if (type === 'search') {
68
- return SearchMediumIcon;
69
- }
70
- return leadingIcon;
71
- })();
71
+ const toggleFieldType = () => {
72
+ setFieldType(fieldType === 'password' ? 'text' : 'password');
73
+ };
72
74
 
73
- const getInputMode = (() => {
74
- if (type === 'search') {
75
- return 'search';
76
- }
77
- return undefined;
78
- })();
75
+ const leadingIconComponent = ((): ComponentType | undefined => {
76
+ if (type === 'search') {
77
+ return SearchMediumIcon;
78
+ }
79
+ return leadingIcon;
80
+ })();
79
81
 
80
- return (
81
- <InputComponent
82
- {...(children ? props : {})}
83
- validationStatus={validationStatusFromContext}
84
- isInvalid={validationStatusFromContext === 'invalid'}
85
- isReadOnly={readonly}
86
- isDisabled={formFieldDisabled ?? disabled}
87
- isFocused={focused}
88
- type={type as undefined}
89
- isRequired={isRequired}
90
- >
91
- {children ? (
92
- <>{children}</>
93
- ) : (
94
- <>
95
- {!!leadingIconComponent && (
96
- <InputSlot>
97
- <InputIcon as={leadingIconComponent} />
98
- </InputSlot>
99
- )}
100
- <InputField
101
- type={fieldType}
102
- inputMode={getInputMode}
103
- inBottomSheet={inBottomSheet}
104
- {...props}
105
- />
106
- {shouldShowClear && (
107
- <InputSlot>
108
- <UnstyledIconButton onPress={onClear} icon={CloseSmallIcon} />
109
- </InputSlot>
110
- )}
111
- {loading && (
112
- <InputSlot>
113
- <Spinner size="xs" color={color.icon.primary} />
114
- </InputSlot>
115
- )}
116
- {shouldShowPasswordToggle && (
117
- <InputSlot>
118
- <UnstyledIconButton
119
- onPress={toggleFieldType}
120
- icon={fieldType === 'password' ? EyeSmallIcon : EyeOffSmallIcon}
121
- />
122
- </InputSlot>
123
- )}
124
- {!!trailingIcon && (
125
- <InputSlot>
126
- <InputIcon as={trailingIcon} />
127
- </InputSlot>
128
- )}
129
- </>
130
- )}
131
- </InputComponent>
132
- );
133
- };
82
+ const getInputMode = (() => {
83
+ if (type === 'search') {
84
+ return 'search';
85
+ }
86
+ return undefined;
87
+ })();
88
+
89
+ return (
90
+ <InputComponent
91
+ {...(children ? props : {})}
92
+ validationStatus={validationStatusFromContext}
93
+ isInvalid={validationStatusFromContext === 'invalid'}
94
+ isReadOnly={readonly}
95
+ isDisabled={formFieldDisabled ?? disabled}
96
+ isFocused={focused}
97
+ type={type as undefined}
98
+ isRequired={isRequired}
99
+ >
100
+ {children ? (
101
+ <>{children}</>
102
+ ) : (
103
+ <>
104
+ {!!leadingIconComponent && (
105
+ <InputSlot>
106
+ <InputIcon as={leadingIconComponent} />
107
+ </InputSlot>
108
+ )}
109
+ <InputField
110
+ // @ts-expect-error - ref forwarding issue
111
+ ref={inputRef}
112
+ type={fieldType}
113
+ inputMode={getInputMode}
114
+ inBottomSheet={inBottomSheet}
115
+ {...props}
116
+ />
117
+ {shouldShowClear && (
118
+ <InputSlot>
119
+ <UnstyledIconButton onPress={onClear} icon={CloseSmallIcon} />
120
+ </InputSlot>
121
+ )}
122
+ {loading && (
123
+ <InputSlot>
124
+ <Spinner size="xs" color={color.icon.primary} />
125
+ </InputSlot>
126
+ )}
127
+ {shouldShowPasswordToggle && (
128
+ <InputSlot>
129
+ <UnstyledIconButton
130
+ onPress={toggleFieldType}
131
+ icon={fieldType === 'password' ? EyeSmallIcon : EyeOffSmallIcon}
132
+ />
133
+ </InputSlot>
134
+ )}
135
+ {!!trailingIcon && (
136
+ <InputSlot>
137
+ <InputIcon as={trailingIcon} />
138
+ </InputSlot>
139
+ )}
140
+ </>
141
+ )}
142
+ </InputComponent>
143
+ );
144
+ }
145
+ );
134
146
 
135
147
  Input.displayName = 'Input';
136
148