@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
@@ -1,25 +1,38 @@
1
1
  import { BottomSheetTextInput } from '@gorhom/bottom-sheet';
2
+ import { forwardRef } from 'react';
2
3
  import { TextInput as RNTextInput, TextInputProps } from 'react-native';
3
4
  import { StyleSheet } from 'react-native-unistyles';
4
5
  import { useTheme } from '../../hooks';
5
6
  import { useInputContext } from './Input.context';
6
7
 
7
- const InputField = ({
8
- style,
9
- inBottomSheet = false,
10
- ...props
11
- }: TextInputProps & { inBottomSheet?: boolean }) => {
12
- const { disabled, focused = false, type } = useInputContext();
13
- styles.useVariants({ focused, type });
14
- const { color } = useTheme();
8
+ const InputField = forwardRef<RNTextInput, TextInputProps & { inBottomSheet?: boolean }>(
9
+ ({ style, inBottomSheet = false, ...props }, ref) => {
10
+ const { disabled, focused = false, type } = useInputContext();
11
+ styles.useVariants({ focused, type });
12
+ const { color } = useTheme();
13
+
14
+ if (inBottomSheet) {
15
+ return (
16
+ // @ts-expect-error - BottomSheetTextInput has incompatible event types with TextInput
17
+ <BottomSheetTextInput
18
+ ref={ref as any}
19
+ placeholderTextColor={color.text.secondary}
20
+ selectionColor={color.surface.brand.default}
21
+ cursorColor={color.surface.brand.default}
22
+ verticalAlign="middle"
23
+ aria-disabled={disabled}
24
+ {...props}
25
+ style={[styles.input, style]}
26
+ />
27
+ );
28
+ }
15
29
 
16
- if (inBottomSheet) {
17
30
  return (
18
- // @ts-expect-error - BottomSheetTextInput type issue
19
- <BottomSheetTextInput
31
+ <RNTextInput
32
+ ref={ref}
20
33
  placeholderTextColor={color.text.secondary}
21
- selectionColor={color.purple[700]}
22
- cursorColor={color.purple[700]}
34
+ selectionColor={color.surface.brand.default}
35
+ cursorColor={color.surface.brand.default}
23
36
  verticalAlign="middle"
24
37
  aria-disabled={disabled}
25
38
  {...props}
@@ -27,19 +40,7 @@ const InputField = ({
27
40
  />
28
41
  );
29
42
  }
30
-
31
- return (
32
- <RNTextInput
33
- placeholderTextColor={color.text.secondary}
34
- selectionColor={color.purple[700]}
35
- cursorColor={color.purple[700]}
36
- verticalAlign="middle"
37
- aria-disabled={disabled}
38
- {...props}
39
- style={[styles.input, style]}
40
- />
41
- );
42
- };
43
+ );
43
44
 
44
45
  InputField.displayName = 'InputField';
45
46
 
@@ -4,18 +4,20 @@ import {
4
4
  ChevronRightSmallIcon,
5
5
  ElectricityMediumIcon,
6
6
  GasMediumIcon,
7
- InfoMediumIcon as HomeMediumIcon,
8
- // HomeMediumIcon,
7
+ HomeMediumIcon,
9
8
  PaymentMediumIcon,
10
9
  UserMediumIcon,
11
10
  } from '@utilitywarehouse/hearth-react-native-icons';
12
11
  import {
13
12
  Badge,
14
13
  BadgeText,
14
+ BodyText,
15
15
  Box,
16
16
  FlatList,
17
17
  Image,
18
+ Link,
18
19
  List,
20
+ ListAction,
19
21
  ListItem,
20
22
  ListItemContent,
21
23
  ListItemHelperText,
@@ -26,9 +28,6 @@ import {
26
28
  ListItemTrailingIcon,
27
29
  SectionHeader,
28
30
  Switch,
29
- Link,
30
- ListAction,
31
- BodyText,
32
31
  } from '../../';
33
32
  import { BackToTopButton, BadgeList, UsageWrap, ViewFigmaButton } from '../../../docs/components';
34
33
  import * as Stories from './List.stories';
@@ -116,7 +115,7 @@ const MyComponent = () => (
116
115
  | variant | `'subtle' \| 'emphasis'` | | The variant style of the list item. |
117
116
  | disabled | `boolean` | `false` | Whether to disable the list item. |
118
117
  | loading | `boolean` | `false` | Whether to show the list item in loading state. |
119
- | badge | `BadgeProps` | | Props to pass to the Badge component. |
118
+ | badge | `ReactNode` | | The badge component to display in the list item. |
120
119
  | badgePosition | `'top' \| 'bottom'` | `bottom` | Position of the badge in the list item. |
121
120
  | numericValue | `string \| number` | | A numeric value to display on the right side of the item. |
122
121
 
@@ -197,7 +196,14 @@ parts like `ListItemLeadingContent`, `ListItemContent`, and `ListItemTrailingCon
197
196
  <Canvas of={Stories.WithBadge} />
198
197
 
199
198
  ```tsx
200
- import { List, ListItem, IconContainer, Flex, Box } from '@utilitywarehouse/hearth-react-native';
199
+ import {
200
+ List,
201
+ ListItem,
202
+ IconContainer,
203
+ Flex,
204
+ Box,
205
+ Badge,
206
+ } from '@utilitywarehouse/hearth-react-native';
201
207
 
202
208
  import { ElectricityMediumIcon, GasMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
203
209
 
@@ -211,7 +217,7 @@ const BadgeList = () => {
211
217
  leadingContent={
212
218
  <IconContainer icon={ElectricityMediumIcon} size="md" variant="emphasis" color="energy" />
213
219
  }
214
- badge={{ text: 'Text' }}
220
+ badge={<Badge text="Text" />}
215
221
  />
216
222
  <ListItem
217
223
  heading="Gas"
@@ -220,7 +226,7 @@ const BadgeList = () => {
220
226
  leadingContent={
221
227
  <IconContainer icon={GasMediumIcon} size="md" variant="emphasis" color="energy" />
222
228
  }
223
- badge={{ text: 'Smart Meter' }}
229
+ badge={<Badge text="Smart Meter" />}
224
230
  />
225
231
  </List>
226
232
  );
@@ -126,7 +126,7 @@ export const WithBadge: Story = {
126
126
  leadingContent={
127
127
  <IconContainer icon={ElectricityMediumIcon} size="md" variant="emphasis" color="energy" />
128
128
  }
129
- badge={{ text: 'Text' }}
129
+ badge={<Badge text="Text" />}
130
130
  />
131
131
  <ListItem
132
132
  heading="Gas"
@@ -135,7 +135,7 @@ export const WithBadge: Story = {
135
135
  leadingContent={
136
136
  <IconContainer icon={GasMediumIcon} size="md" variant="emphasis" color="energy" />
137
137
  }
138
- badge={{ text: 'Smart Meter' }}
138
+ badge={<Badge text="Smart Meter" />}
139
139
  />
140
140
  </List>
141
141
  ),
@@ -1,5 +1,5 @@
1
+ import { ReactNode } from 'react';
1
2
  import type { PressableProps, ViewProps } from 'react-native';
2
- import BadgeProps from '../../Badge/Badge.props';
3
3
 
4
4
  interface ListItemBaseProps extends Omit<PressableProps, 'children'> {
5
5
  loading?: boolean;
@@ -26,7 +26,7 @@ export interface ListItemWithoutChildren extends ListItemBaseProps {
26
26
  leadingContent?: ViewProps['children'];
27
27
  trailingContent?: ViewProps['children'];
28
28
  numericValue?: string | number;
29
- badge?: BadgeProps;
29
+ badge?: ReactNode;
30
30
  badgePosition?: 'top' | 'bottom';
31
31
  }
32
32
 
@@ -2,7 +2,6 @@ import { ChevronRightSmallIcon } from '@utilitywarehouse/hearth-react-native-ico
2
2
  import { useMemo } from 'react';
3
3
  import { Pressable, ViewStyle } from 'react-native';
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
- import { Badge } from '../../Badge';
6
5
  import { DetailText } from '../../DetailText';
7
6
  import { Skeleton } from '../../Skeleton';
8
7
  import { useListContext } from '../List.context';
@@ -106,10 +105,10 @@ const ListItemRoot = ({
106
105
  <ListItemLeadingContent>{leadingContent}</ListItemLeadingContent>
107
106
  ) : null}
108
107
  <ListItemContent>
109
- {badgePosition === 'top' && badge ? <Badge {...badge} /> : null}
108
+ {badgePosition === 'top' && badge ? badge : null}
110
109
  <ListItemText>{heading}</ListItemText>
111
110
  {helperText ? <ListItemHelperText>{helperText}</ListItemHelperText> : null}
112
- {badgePosition === 'bottom' && badge ? <Badge {...badge} /> : null}
111
+ {badgePosition === 'bottom' && badge ? badge : null}
113
112
  </ListItemContent>
114
113
  {!!numericValue && <DetailText size="lg">{numericValue}</DetailText>}
115
114
  {trailingContent ? (
@@ -108,6 +108,19 @@ The Modal component extends the `BottomSheetModal` component and accepts all of
108
108
  | `fullscreen` | `boolean` | Whether the modal should take up the full screen height | `false` |
109
109
  | `inNavModal` | `boolean` | Renders the modal correctly when used inside a navigation modal | `false` |
110
110
 
111
+ ### `ModalImage` Props
112
+
113
+ The `ModalImage` component can be used to display an image within the Modal. It accepts the following props:
114
+
115
+ | Property | Type | Description |
116
+ | --------- | --------------------- | ------------------------------------------------------------------------ |
117
+ | `source` | `ImageSourcePropType` | The source of the image to display |
118
+ | `light` | `ImageSourcePropType` | The source of the image to display in light mode (use instead of source) |
119
+ | `dark` | `ImageSourcePropType` | The source of the image to display in dark mode (use instead of source) |
120
+ | `...rest` | `ImageProps` | Additional props to pass to the underlying Image component |
121
+
122
+ For more details about the ThemedImage component used internally, refer to the [`ThemedImage` documentation](/docs/utility-components-themed-image--docs).
123
+
111
124
  ## Features
112
125
 
113
126
  ### Automatic Layout Management
@@ -269,6 +282,8 @@ Use the `image` prop to display an image-centric modal with centered content:
269
282
  <Canvas of={Stories.WithImage} />
270
283
 
271
284
  ```tsx
285
+ import { Modal, ModalImage, Button } from '@utilitywarehouse/hearth-react-native';
286
+
272
287
  const ImageModal = () => {
273
288
  const modalRef = useRef<BottomSheetModal>(null);
274
289
 
@@ -282,10 +297,7 @@ const ImageModal = () => {
282
297
  description="Thanks for joining our community"
283
298
  primaryButtonText="Get Started"
284
299
  secondaryButtonText="Maybe Later"
285
- image={{
286
- source: require('./path/to/image.png'),
287
- resizeMode: 'contain',
288
- }}
300
+ image={<ModalImage source={require('./path/to/image.png')} resizeMode="contain" />}
289
301
  onPressPrimaryButton={() => modalRef.current?.dismiss()}
290
302
  onPressSecondaryButton={() => modalRef.current?.dismiss()}
291
303
  />
@@ -1,11 +1,12 @@
1
- import { ImageProps, ViewProps } from 'react-native';
1
+ import { ReactNode } from 'react';
2
+ import { ViewProps } from 'react-native';
2
3
  import { BottomSheetProps } from '../BottomSheet';
3
4
  import { ButtonWithoutChildrenProps } from '../Button/Button.props';
4
5
  import { UnstyledIconButtonProps } from '../UnstyledIconButton';
5
6
 
6
7
  interface ModalProps extends Omit<BottomSheetProps, 'children'> {
7
8
  loading?: boolean;
8
- image?: ImageProps;
9
+ image?: ReactNode;
9
10
  showCloseButton?: boolean;
10
11
  heading?: string;
11
12
  description?: string;
@@ -1,7 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useRef } from 'react';
3
3
  import { ImageSourcePropType, Platform, View } from 'react-native';
4
- import { Modal } from '.';
4
+ import { Modal, ModalImage } from '.';
5
5
  import pigs from '../../../docs/assets/pigs.png';
6
6
  import { ViewWrap } from '../../../docs/components';
7
7
  import { BodyText } from '../BodyText';
@@ -117,10 +117,7 @@ export const WithImage = () => {
117
117
  secondaryButtonText="Cancel"
118
118
  onPressSecondaryButton={closeModal}
119
119
  index={1}
120
- image={{
121
- source: pigs as ImageSourcePropType,
122
- resizeMode: 'contain',
123
- }}
120
+ image={<ModalImage source={pigs as ImageSourcePropType} resizeMode="contain" />}
124
121
  />
125
122
  </ViewWrap>
126
123
  </View>
@@ -2,7 +2,7 @@ import { BottomSheetScrollViewMethods, SNAP_POINT_TYPE } from '@gorhom/bottom-sh
2
2
  import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
3
3
  import { CloseMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
4
4
  import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
5
- import { AccessibilityInfo, Image, Platform, View, findNodeHandle } from 'react-native';
5
+ import { AccessibilityInfo, Platform, View, findNodeHandle } from 'react-native';
6
6
  import Animated, {
7
7
  Easing,
8
8
  useAnimatedStyle,
@@ -200,7 +200,7 @@ const Modal = ({
200
200
  </View>
201
201
  {image ? (
202
202
  <View style={styles.imageContainer}>
203
- <Image style={styles.image} {...image} />
203
+ {image}
204
204
  <View style={styles.textContent}>
205
205
  {heading ? (
206
206
  <Heading size="lg" textAlign="center" accessible>
@@ -296,10 +296,6 @@ const styles = StyleSheet.create((theme, rt) => ({
296
296
  flex: 1,
297
297
  gap: theme.components.modal.content.gap,
298
298
  },
299
- image: {
300
- width: 260,
301
- height: 260,
302
- },
303
299
  imageContainer: {
304
300
  alignItems: 'center',
305
301
  flex: 1,
@@ -2,7 +2,7 @@ import { BottomSheetScrollViewMethods, SNAP_POINT_TYPE } from '@gorhom/bottom-sh
2
2
  import { BottomSheetModalMethods } from '@gorhom/bottom-sheet/lib/typescript/types';
3
3
  import { CloseMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
4
4
  import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
5
- import { AccessibilityInfo, Image, Platform, View, findNodeHandle } from 'react-native';
5
+ import { AccessibilityInfo, Platform, View, findNodeHandle } from 'react-native';
6
6
  import Animated, {
7
7
  Easing,
8
8
  useAnimatedStyle,
@@ -197,7 +197,7 @@ const Modal = ({
197
197
  </View>
198
198
  {image ? (
199
199
  <View style={styles.imageContainer}>
200
- <Image style={styles.image} {...image} />
200
+ {image}
201
201
  <View style={styles.textContent}>
202
202
  {heading ? (
203
203
  <Heading size="lg" textAlign="center" accessible>
@@ -281,10 +281,6 @@ const styles = StyleSheet.create((theme, rt) => ({
281
281
  flex: 1,
282
282
  gap: theme.components.modal.content.gap,
283
283
  },
284
- image: {
285
- width: 260,
286
- height: 260,
287
- },
288
284
  imageContainer: {
289
285
  alignItems: 'center',
290
286
  justifyContent: 'center',
@@ -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 ModalImage = (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: 260,
16
+ height: 260,
17
+ },
18
+ });
19
+
20
+ export default ModalImage;
@@ -1,2 +1,3 @@
1
1
  export { default as Modal } from './Modal';
2
2
  export type { default as ModalProps } from './Modal.props';
3
+ export { default as ModalImage } from './ModalImage';
@@ -142,7 +142,7 @@ export const Multiple: Story = {
142
142
  return (
143
143
  <Flex space="lg" direction="column" align="center" style={{ maxWidth: 400 }}>
144
144
  <PillGroup wrap={true} multiple value={selectedCategories} onChange={v => setSelectedCategories(v as string[])}>
145
- <Pill value="all" label="All" />
145
+ <Pill value="unread" label="Unread" />
146
146
  <Pill value="new" label="New" icon={HeartMediumIcon} />
147
147
  <Pill value="favourites" label="My favourites" icon={HeartMediumIcon} />
148
148
  <Pill value="read" label="Read" />
@@ -104,7 +104,7 @@ const MyComponent = () => {
104
104
  | `validText` | `string` | - | The valid text to be displayed below the radio. |
105
105
  | `showValidationIcon` | `boolean` | `true` | Whether to show the validation icon. |
106
106
  | `type` | `'default' \| 'tile'` | `default` | The type of the radio. |
107
- | `image` | `ImageProps` | - | The image to be displayed next to the label. |
107
+ | `image` | `ReactNode` | - | The image to be displayed next to the label. |
108
108
 
109
109
  ## Components
110
110
 
@@ -150,6 +150,15 @@ Contains all Group related layout style props and actions. It inherits all the p
150
150
 
151
151
  ### `RadioImage`
152
152
 
153
+ | Property | Type | Description |
154
+ | --------- | --------------------- | ------------------------------------------------------------------------ |
155
+ | `source` | `ImageSourcePropType` | The source of the image to display |
156
+ | `light` | `ImageSourcePropType` | The source of the image to display in light mode (use instead of source) |
157
+ | `dark` | `ImageSourcePropType` | The source of the image to display in dark mode (use instead of source) |
158
+ | `...rest` | `ImageProps` | Additional props to pass to the underlying Image component |
159
+
160
+ For more details about the ThemedImage component used internally, refer to the [`ThemedImage` documentation](/docs/utility-components-themed-image--docs).
161
+
153
162
  Contains all Image related layout style props and actions. It inherits all the properties of React Native's [Image component](https://reactnative.dev/docs/image).
154
163
 
155
164
  ## Variants
@@ -261,9 +270,9 @@ The `RadioImage` component is used to display an image next to the radio button.
261
270
  <Canvas of={Stories.WithImage} />
262
271
 
263
272
  ```tsx
264
- import { Radio, RadioGroup } from '@utilitywarehouse/native-ui';
273
+ import { Radio, RadioGroup, RadioImage } from '@utilitywarehouse/native-ui';
265
274
  import visaLogo from './visa-logo.png';
266
- import mastarcardLogo from './mastercard-logo.png';
275
+ import mastercardLogo from './mastercard-logo.png';
267
276
 
268
277
  const MyComponent = () => {
269
278
  const [value, setValue] = React.useState('Option 1');
@@ -273,16 +282,20 @@ const MyComponent = () => {
273
282
  value="visa"
274
283
  aria-label="Visa"
275
284
  label="Visa"
276
- image={{ source: visaLogo, style: { width: 40, height: 24, resizeMode: 'contain' } }}
285
+ image={
286
+ <RadioImage source={visaLogo} style={{ width: 40, height: 24, resizeMode: 'contain' }} />
287
+ }
277
288
  />
278
289
  <Radio
279
290
  value="mastercard"
280
291
  aria-label="Mastercard"
281
292
  label="Mastercard"
282
- image={{
283
- source: mastarcardLogo,
284
- style: { width: 40, height: 24, resizeMode: 'contain' },
285
- }}
293
+ image={
294
+ <RadioImage
295
+ source={mastercardLogo}
296
+ style={{ width: 40, height: 24, resizeMode: 'contain' }}
297
+ />
298
+ }
286
299
  />
287
300
  </RadioGroup>
288
301
  );
@@ -1,5 +1,5 @@
1
- import type { ComponentType } from 'react';
2
- import type { ImageProps, PressableProps, ViewProps } from 'react-native';
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ import type { PressableProps, ViewProps } from 'react-native';
3
3
 
4
4
  interface RadioBaseProps extends Omit<PressableProps, 'children'> {
5
5
  value: string;
@@ -28,7 +28,7 @@ interface RadioWithoutChildrenProps extends RadioBaseProps {
28
28
  invalidText?: string;
29
29
  validText?: string;
30
30
  showValidationIcon?: boolean;
31
- image?: ImageProps;
31
+ image?: ReactNode;
32
32
  }
33
33
 
34
34
  type RadioProps = RadioWithChildrenProps | RadioWithoutChildrenProps;
@@ -1,7 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { useState } from 'react';
3
3
  import { ImageSourcePropType } from 'react-native';
4
- import { Radio, RadioGroup } from '.';
4
+ import { Radio, RadioGroup, RadioImage } from '.';
5
5
  import bankLogo from '../../../docs/assets/bank-logo.png';
6
6
  import bankLogo1 from '../../../docs/assets/bank-logo1.png';
7
7
  import { VariantTitle } from '../../../docs/components';
@@ -106,11 +106,13 @@ export const WithImage: Story = {
106
106
  {...args}
107
107
  label="Visa"
108
108
  value="Option 1"
109
- image={{
110
- source: bankLogo1 as ImageSourcePropType,
111
- style: { width: 48, height: 32 },
112
- resizeMode: 'cover',
113
- }}
109
+ image={
110
+ <RadioImage
111
+ source={bankLogo1 as ImageSourcePropType}
112
+ style={{ width: 48, height: 32 }}
113
+ resizeMode="cover"
114
+ />
115
+ }
114
116
  />
115
117
  <Radio
116
118
  aria-label="Label 2"
@@ -121,11 +123,13 @@ export const WithImage: Story = {
121
123
  {...args}
122
124
  label="Mastercard"
123
125
  value="Option 2"
124
- image={{
125
- source: bankLogo as ImageSourcePropType,
126
- style: { width: 48, height: 32 },
127
- resizeMode: 'cover',
128
- }}
126
+ image={
127
+ <RadioImage
128
+ source={bankLogo as ImageSourcePropType}
129
+ style={{ width: 48, height: 32 }}
130
+ resizeMode="cover"
131
+ />
132
+ }
129
133
  />
130
134
  </RadioGroup>
131
135
  ),
@@ -9,7 +9,6 @@ import StyledRadio from './RadioRoot';
9
9
  import { useFormFieldContext } from '../FormField';
10
10
  import { Helper } from '../Helper';
11
11
  import { useRadioGroupContext } from './RadioGroup.context';
12
- import RadioImage from './RadioImage';
13
12
  import RadioTextContent from './RadioTextContent';
14
13
  import RadioTileRoot from './RadioTileRoot';
15
14
 
@@ -57,7 +56,7 @@ const Radio = ({
57
56
  <RadioIndicator>
58
57
  <RadioIcon />
59
58
  </RadioIndicator>
60
- {image ? <RadioImage {...image} /> : null}
59
+ {image ? image : null}
61
60
  <RadioTextContent>
62
61
  {!!label && <RadioLabel>{label}</RadioLabel>}
63
62
  {!!helperText && <Helper disabled={disabled} icon={helperIcon} text={helperText} />}
@@ -1,8 +1,13 @@
1
1
  import { Image, ImageProps } from 'react-native';
2
+ import { isThemedImageProps } from '../../utils';
3
+ import { ThemedImage, ThemedImageProps } from '../ThemedImage';
2
4
 
3
- const RadioImage = ({ source, style, ...props }: ImageProps) => (
4
- <Image source={source} style={style} {...props} />
5
- );
5
+ const RadioImage = ({ ...props }: ImageProps | ThemedImageProps) => {
6
+ if (isThemedImageProps(props)) {
7
+ return <ThemedImage {...props} />;
8
+ }
9
+ return <Image {...props} />;
10
+ };
6
11
 
7
12
  RadioImage.displayName = 'RadioImage';
8
13
 
@@ -1,9 +1,10 @@
1
1
  export { default as coloursAsArray, extractLightColorValues } from './coloursAsArray';
2
2
  export { default as formatThousands } from './formatThousands';
3
3
  export { default as getFlattenedColorValue } from './getFlattenedColorValue';
4
+ export { getInitials } from './getInitials';
4
5
  export { default as getStyleValue } from './getStyleValue';
5
6
  export { default as hexWithOpacity } from './hexWithOpacity';
6
- export { getInitials } from './getInitials';
7
7
  export { default as isEqual } from './isEqual';
8
+ export { default as isThemedImageProps } from './isThemedImageProps';
8
9
  export * from './styleUtils';
9
10
  export * from './themeValueHelpers';
@@ -0,0 +1,8 @@
1
+ import { ImageProps } from 'react-native';
2
+ import { ThemedImageProps } from 'src/components';
3
+
4
+ const isThemedImageProps = (props: ThemedImageProps | ImageProps): props is ThemedImageProps => {
5
+ return 'light' in props && 'dark' in props;
6
+ };
7
+
8
+ export default isThemedImageProps;