@idealyst/components 1.2.29 → 1.2.31

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 (143) hide show
  1. package/README.md +3 -3
  2. package/package.json +4 -4
  3. package/plugin/__tests__/web.test.ts +2 -2
  4. package/plugin/web.js +2 -0
  5. package/src/Accordion/Accordion.native.tsx +3 -2
  6. package/src/ActivityIndicator/ActivityIndicator.native.tsx +4 -2
  7. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +22 -27
  8. package/src/ActivityIndicator/ActivityIndicator.web.tsx +17 -29
  9. package/src/Alert/Alert.native.tsx +20 -10
  10. package/src/Alert/Alert.styles.tsx +173 -86
  11. package/src/Alert/Alert.web.tsx +34 -30
  12. package/src/Alert/types.ts +53 -3
  13. package/src/Avatar/Avatar.native.tsx +3 -2
  14. package/src/Avatar/Avatar.web.tsx +2 -1
  15. package/src/Avatar/types.ts +1 -1
  16. package/src/Badge/Badge.native.tsx +18 -6
  17. package/src/Badge/Badge.styles.tsx +22 -5
  18. package/src/Badge/Badge.web.tsx +12 -4
  19. package/src/Badge/types.ts +14 -2
  20. package/src/Breadcrumb/Breadcrumb.native.tsx +3 -2
  21. package/src/Button/Button.native.tsx +16 -6
  22. package/src/Button/Button.styles.tsx +2 -2
  23. package/src/Button/Button.web.tsx +19 -15
  24. package/src/Button/types.ts +6 -10
  25. package/src/Card/Card.native.tsx +27 -3
  26. package/src/Card/Card.web.tsx +30 -4
  27. package/src/Card/types.ts +15 -0
  28. package/src/Checkbox/Checkbox.native.tsx +5 -4
  29. package/src/Checkbox/Checkbox.styles.tsx +62 -52
  30. package/src/Checkbox/Checkbox.web.tsx +4 -3
  31. package/src/Checkbox/types.ts +1 -1
  32. package/src/Chip/Chip.native.tsx +30 -7
  33. package/src/Chip/Chip.web.tsx +28 -5
  34. package/src/Chip/types.ts +15 -0
  35. package/src/Dialog/Dialog.native.tsx +6 -6
  36. package/src/Dialog/Dialog.web.tsx +5 -5
  37. package/src/Dialog/types.ts +2 -2
  38. package/src/Divider/Divider.native.tsx +20 -17
  39. package/src/Divider/Divider.styles.tsx +51 -29
  40. package/src/Divider/Divider.web.tsx +5 -4
  41. package/src/Divider/types.ts +3 -3
  42. package/src/Icon/Icon.native.tsx +3 -2
  43. package/src/Icon/Icon.web.tsx +2 -1
  44. package/src/Icon/IconSvg/IconSvg.native.tsx +3 -2
  45. package/src/IconButton/IconButton.native.tsx +219 -0
  46. package/src/IconButton/IconButton.styles.tsx +127 -0
  47. package/src/IconButton/IconButton.web.tsx +198 -0
  48. package/src/IconButton/index.native.ts +5 -0
  49. package/src/IconButton/index.ts +5 -0
  50. package/src/IconButton/index.web.ts +5 -0
  51. package/src/IconButton/types.ts +84 -0
  52. package/src/Image/Image.native.tsx +3 -2
  53. package/src/Input/Input.native.tsx +42 -290
  54. package/src/Input/Input.styles.tsx +1 -1
  55. package/src/Input/Input.web.tsx +37 -288
  56. package/src/Input/index.native.ts +9 -2
  57. package/src/Input/index.ts +8 -1
  58. package/src/Input/index.web.ts +8 -1
  59. package/src/Input/types.ts +1 -1
  60. package/src/List/List.native.tsx +3 -2
  61. package/src/List/ListItem.native.tsx +3 -2
  62. package/src/List/ListSection.native.tsx +3 -2
  63. package/src/Menu/Menu.native.tsx +2 -1
  64. package/src/Menu/Menu.styles.tsx +79 -29
  65. package/src/Menu/Menu.web.tsx +2 -1
  66. package/src/Menu/MenuItem.native.tsx +4 -3
  67. package/src/Menu/MenuItem.styles.tsx +81 -32
  68. package/src/Menu/MenuItem.web.tsx +2 -1
  69. package/src/Menu/docs.ts +1 -1
  70. package/src/Popover/Popover.native.tsx +2 -1
  71. package/src/Popover/Popover.web.tsx +2 -1
  72. package/src/Popover/types.ts +15 -4
  73. package/src/Pressable/Pressable.native.tsx +3 -2
  74. package/src/Pressable/Pressable.web.tsx +3 -5
  75. package/src/Progress/Progress.native.tsx +5 -4
  76. package/src/Progress/Progress.web.tsx +3 -3
  77. package/src/Progress/types.ts +3 -3
  78. package/src/RadioButton/RadioButton.native.tsx +4 -3
  79. package/src/RadioButton/RadioButton.styles.tsx +53 -33
  80. package/src/RadioButton/RadioGroup.native.tsx +3 -2
  81. package/src/SVGImage/SVGImage.native.tsx +5 -4
  82. package/src/SVGImage/SVGImage.styles.tsx +44 -10
  83. package/src/SVGImage/SVGImage.web.tsx +2 -1
  84. package/src/Screen/Screen.native.tsx +2 -1
  85. package/src/Screen/Screen.web.tsx +2 -1
  86. package/src/Select/Select.native.tsx +6 -5
  87. package/src/Select/Select.styles.tsx +1 -1
  88. package/src/Select/Select.web.tsx +4 -3
  89. package/src/Select/types.ts +1 -1
  90. package/src/Skeleton/Skeleton.native.tsx +2 -1
  91. package/src/Skeleton/Skeleton.web.tsx +1 -1
  92. package/src/Slider/Slider.native.tsx +9 -8
  93. package/src/Slider/Slider.web.tsx +10 -9
  94. package/src/Slider/types.ts +9 -2
  95. package/src/Switch/Switch.native.tsx +7 -6
  96. package/src/Switch/Switch.styles.tsx +52 -17
  97. package/src/Switch/Switch.web.tsx +15 -16
  98. package/src/Switch/types.ts +44 -4
  99. package/src/TabBar/TabBar.native.tsx +3 -2
  100. package/src/Text/Text.native.tsx +3 -2
  101. package/src/Text/Text.web.tsx +2 -1
  102. package/src/TextArea/TextArea.native.tsx +3 -2
  103. package/src/TextArea/TextArea.styles.tsx +2 -2
  104. package/src/TextArea/TextArea.web.tsx +2 -1
  105. package/src/TextInput/TextInput.native.tsx +300 -0
  106. package/src/TextInput/TextInput.styles.tsx +207 -0
  107. package/src/TextInput/TextInput.web.tsx +301 -0
  108. package/src/TextInput/index.native.ts +3 -0
  109. package/src/TextInput/index.ts +5 -0
  110. package/src/TextInput/index.web.ts +5 -0
  111. package/src/TextInput/types.ts +163 -0
  112. package/src/Tooltip/Tooltip.native.tsx +3 -2
  113. package/src/Video/Video.native.tsx +4 -3
  114. package/src/View/View.native.tsx +2 -1
  115. package/src/View/View.styles.tsx +1 -0
  116. package/src/View/View.web.tsx +9 -2
  117. package/src/examples/ActivityIndicatorExamples.tsx +177 -0
  118. package/src/examples/AlertExamples.tsx +5 -5
  119. package/src/examples/ButtonExamples.tsx +12 -12
  120. package/src/examples/CardExamples.tsx +1 -1
  121. package/src/examples/CheckboxExamples.tsx +2 -2
  122. package/src/examples/ChipExamples.tsx +6 -6
  123. package/src/examples/DialogExamples.tsx +1 -1
  124. package/src/examples/DividerExamples.tsx +1 -1
  125. package/src/examples/InputExamples.tsx +1 -1
  126. package/src/examples/LinkExamples.tsx +1 -1
  127. package/src/examples/ListExamples.tsx +1 -1
  128. package/src/examples/MenuExamples.tsx +2 -2
  129. package/src/examples/ProgressExamples.tsx +1 -1
  130. package/src/examples/RadioButtonExamples.tsx +5 -5
  131. package/src/examples/SVGImageExamples.tsx +1 -1
  132. package/src/examples/SelectExamples.tsx +1 -1
  133. package/src/examples/SliderExamples.tsx +5 -5
  134. package/src/examples/SwitchExamples.tsx +26 -26
  135. package/src/examples/TableExamples.tsx +1 -1
  136. package/src/examples/TooltipExamples.tsx +2 -2
  137. package/src/examples/index.ts +1 -0
  138. package/src/extensions/index.ts +1 -0
  139. package/src/extensions/types.ts +22 -3
  140. package/src/index.native.ts +4 -0
  141. package/src/index.ts +27 -2
  142. package/src/utils/index.ts +12 -0
  143. package/src/utils/refTypes.ts +50 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * IconButton styles using defineStyle with $iterator expansion.
3
+ *
4
+ * Dynamic style functions are used for intent/type combinations since
5
+ * the color depends on both values (compound logic).
6
+ */
7
+ import { StyleSheet } from 'react-native-unistyles';
8
+ import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
9
+ import type { Theme as BaseTheme, Intent, Size } from '@idealyst/theme';
10
+ import { IconButtonGradient } from './types';
11
+
12
+ // Required: Unistyles must see StyleSheet usage in original source to process this file
13
+ void StyleSheet;
14
+
15
+ // Wrap theme for $iterator support
16
+ type Theme = ThemeStyleWrapper<BaseTheme>;
17
+
18
+ type IconButtonSize = Size;
19
+ type IconButtonType = 'contained' | 'outlined' | 'text';
20
+
21
+ export type IconButtonVariants = {
22
+ size: IconButtonSize;
23
+ intent: Intent;
24
+ type: IconButtonType;
25
+ disabled: boolean;
26
+ gradient?: IconButtonGradient;
27
+ }
28
+
29
+ /**
30
+ * All dynamic props passed to icon button style functions.
31
+ */
32
+ export type IconButtonDynamicProps = {
33
+ intent?: Intent;
34
+ type?: IconButtonType;
35
+ size?: Size;
36
+ disabled?: boolean;
37
+ gradient?: IconButtonGradient;
38
+ };
39
+
40
+ /**
41
+ * IconButton styles with $iterator expansion for size variants.
42
+ * Circular button that only contains an icon.
43
+ */
44
+ export const iconButtonStyles = defineStyle('IconButton', (theme: Theme) => ({
45
+ button: ({ intent = 'primary', type = 'contained' }: IconButtonDynamicProps) => ({
46
+ boxSizing: 'border-box',
47
+ alignItems: 'center',
48
+ justifyContent: 'center',
49
+ borderRadius: 9999, // Fully circular
50
+ // Inline theme accesses so Unistyles can trace them
51
+ backgroundColor: type === 'contained'
52
+ ? theme.intents[intent].primary
53
+ : type === 'outlined'
54
+ ? theme.colors.surface.primary
55
+ : 'transparent',
56
+ borderColor: type === 'outlined'
57
+ ? theme.intents[intent].primary
58
+ : 'transparent',
59
+ borderWidth: type === 'outlined' ? 1 : 0,
60
+ borderStyle: type === 'outlined' ? 'solid' as const : undefined,
61
+ _web: {
62
+ display: 'flex',
63
+ transition: 'all 0.1s ease',
64
+ },
65
+ variants: {
66
+ type: {
67
+ contained: {
68
+ backgroundColor: theme.$intents.primary,
69
+ borderColor: 'transparent',
70
+ },
71
+ outlined: {
72
+ backgroundColor: 'transparent',
73
+ borderColor: theme.$intents.primary,
74
+ },
75
+ text: {
76
+ backgroundColor: 'transparent',
77
+ borderColor: 'transparent',
78
+ borderWidth: 0,
79
+ }
80
+ },
81
+ // Size variants - circular so width equals height
82
+ size: {
83
+ width: theme.sizes.$iconButton.size,
84
+ height: theme.sizes.$iconButton.size,
85
+ minWidth: theme.sizes.$iconButton.size,
86
+ minHeight: theme.sizes.$iconButton.size,
87
+ },
88
+ disabled: {
89
+ true: { opacity: 0.6 },
90
+ false: { opacity: 1, _web: { cursor: 'pointer', _hover: { opacity: 0.90 }, _active: { opacity: 0.75 } } },
91
+ },
92
+ gradient: {
93
+ darken: { _web: { backgroundImage: 'linear-gradient(135deg, transparent 0%, rgba(0, 0, 0, 0.15) 100%)' } },
94
+ lighten: { _web: { backgroundImage: 'linear-gradient(135deg, transparent 0%, rgba(255, 255, 255, 0.2) 100%)' } },
95
+ },
96
+ },
97
+ }),
98
+ icon: ({ intent = 'primary', type = 'contained' }: IconButtonDynamicProps) => ({
99
+ display: 'flex',
100
+ alignItems: 'center',
101
+ justifyContent: 'center',
102
+ color: type === 'contained'
103
+ ? theme.intents[intent].contrast
104
+ : theme.intents[intent].primary,
105
+ variants: {
106
+ size: {
107
+ width: theme.sizes.$iconButton.iconSize,
108
+ height: theme.sizes.$iconButton.iconSize,
109
+ },
110
+ },
111
+ }),
112
+ spinner: ({ intent = 'primary', type = 'contained' }: IconButtonDynamicProps) => ({
113
+ display: 'flex',
114
+ alignItems: 'center',
115
+ justifyContent: 'center',
116
+ // Match the icon color based on button type
117
+ color: type === 'contained'
118
+ ? theme.intents[intent].contrast
119
+ : theme.intents[intent].primary,
120
+ variants: {
121
+ size: {
122
+ width: theme.sizes.$iconButton.iconSize,
123
+ height: theme.sizes.$iconButton.iconSize,
124
+ },
125
+ },
126
+ }),
127
+ }));
@@ -0,0 +1,198 @@
1
+ import React, { isValidElement, forwardRef, useMemo } from 'react';
2
+ import { getWebProps } from 'react-native-unistyles/web';
3
+ import { IconButtonProps } from './types';
4
+ import { iconButtonStyles } from './IconButton.styles';
5
+ import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
6
+ import useMergeRefs from '../hooks/useMergeRefs';
7
+ import { getWebInteractiveAriaProps, generateAccessibilityId } from '../utils/accessibility';
8
+ import type { IdealystElement } from '../utils/refTypes';
9
+
10
+ /**
11
+ * Circular icon button component with multiple visual variants and sizes.
12
+ * Supports contained, outlined, and text styles with customizable intent colors.
13
+ */
14
+ const IconButton = forwardRef<IdealystElement, IconButtonProps>((props, ref) => {
15
+ const {
16
+ icon,
17
+ onPress,
18
+ onClick,
19
+ disabled = false,
20
+ loading = false,
21
+ type = 'contained',
22
+ intent = 'primary',
23
+ size = 'md',
24
+ gradient,
25
+ style,
26
+ testID,
27
+ id,
28
+ // Accessibility props
29
+ accessibilityLabel,
30
+ accessibilityHint,
31
+ accessibilityDisabled,
32
+ accessibilityHidden,
33
+ accessibilityRole,
34
+ accessibilityLabelledBy,
35
+ accessibilityDescribedBy,
36
+ accessibilityControls,
37
+ accessibilityExpanded,
38
+ accessibilityPressed,
39
+ accessibilityOwns,
40
+ accessibilityHasPopup,
41
+ } = props;
42
+
43
+ // Button is effectively disabled when loading
44
+ const isDisabled = disabled || loading;
45
+
46
+ // Apply variants for size, disabled, gradient
47
+ iconButtonStyles.useVariants({
48
+ size,
49
+ disabled: isDisabled,
50
+ gradient,
51
+ });
52
+
53
+ // Determine the handler to use - onPress takes precedence
54
+ const pressHandler = onPress ?? onClick;
55
+
56
+ // Warn about deprecated onClick usage in development
57
+ if (process.env.NODE_ENV !== 'production' && onClick && !onPress) {
58
+ console.warn(
59
+ 'IconButton: onClick prop is deprecated. Use onPress instead for cross-platform compatibility.'
60
+ );
61
+ }
62
+
63
+ const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
64
+ e.preventDefault();
65
+ if (!isDisabled && pressHandler) {
66
+ e.stopPropagation();
67
+ pressHandler();
68
+ }
69
+ };
70
+
71
+ // Generate unique ID for accessibility
72
+ const buttonId = useMemo(() => id || generateAccessibilityId('icon-button'), [id]);
73
+
74
+ // Generate ARIA props - accessibilityLabel is critical for icon-only buttons
75
+ const ariaProps = useMemo(() => {
76
+ const computedLabel = accessibilityLabel ?? (typeof icon === 'string' ? icon : undefined);
77
+
78
+ return getWebInteractiveAriaProps({
79
+ accessibilityLabel: computedLabel,
80
+ accessibilityHint,
81
+ accessibilityDisabled: accessibilityDisabled ?? isDisabled,
82
+ accessibilityHidden,
83
+ accessibilityRole: accessibilityRole ?? 'button',
84
+ accessibilityLabelledBy,
85
+ accessibilityDescribedBy,
86
+ accessibilityControls,
87
+ accessibilityExpanded,
88
+ accessibilityPressed,
89
+ accessibilityOwns,
90
+ accessibilityHasPopup,
91
+ });
92
+ }, [
93
+ accessibilityLabel,
94
+ icon,
95
+ accessibilityHint,
96
+ accessibilityDisabled,
97
+ isDisabled,
98
+ accessibilityHidden,
99
+ accessibilityRole,
100
+ accessibilityLabelledBy,
101
+ accessibilityDescribedBy,
102
+ accessibilityControls,
103
+ accessibilityExpanded,
104
+ accessibilityPressed,
105
+ accessibilityOwns,
106
+ accessibilityHasPopup,
107
+ ]);
108
+
109
+ // Compute dynamic styles with all props for full flexibility
110
+ const dynamicProps = { intent, type, size, disabled: isDisabled, gradient };
111
+ const buttonStyleArray = [
112
+ (iconButtonStyles.button as any)(dynamicProps),
113
+ style as any,
114
+ ];
115
+
116
+ // Use getWebProps to generate className and ref for web
117
+ const webProps = getWebProps(buttonStyleArray);
118
+
119
+ // Icon styles with dynamic function
120
+ const iconStyleArray = [(iconButtonStyles.icon as any)(dynamicProps)];
121
+ const iconProps = getWebProps(iconStyleArray);
122
+
123
+ // Spinner styles that match the icon color
124
+ const spinnerStyleArray = [(iconButtonStyles.spinner as any)(dynamicProps)];
125
+ const spinnerProps = getWebProps(spinnerStyleArray);
126
+
127
+ // Helper to render icon
128
+ const renderIcon = () => {
129
+ if (typeof icon === 'string') {
130
+ return (
131
+ <IconSvg
132
+ name={icon}
133
+ {...iconProps}
134
+ aria-label={icon}
135
+ />
136
+ );
137
+ } else if (isValidElement(icon)) {
138
+ return icon;
139
+ }
140
+ return null;
141
+ };
142
+
143
+ // Render spinner with inline CSS animation (absolutely centered)
144
+ const renderSpinner = () => (
145
+ <>
146
+ <style>
147
+ {`
148
+ @keyframes icon-button-spin {
149
+ from { transform: rotate(0deg); }
150
+ to { transform: rotate(360deg); }
151
+ }
152
+ `}
153
+ </style>
154
+ <span
155
+ {...spinnerProps}
156
+ style={{
157
+ position: 'absolute',
158
+ display: 'inline-block',
159
+ width: '1em',
160
+ height: '1em',
161
+ border: '2px solid currentColor',
162
+ borderTopColor: 'transparent',
163
+ borderRadius: '50%',
164
+ animation: 'icon-button-spin 0.8s linear infinite',
165
+ }}
166
+ role="status"
167
+ aria-label="Loading"
168
+ />
169
+ </>
170
+ );
171
+
172
+ // Merge unistyles web ref with forwarded ref
173
+ const mergedRef = useMergeRefs(ref, webProps.ref);
174
+
175
+ // Content opacity - hide when loading but keep for sizing
176
+ const contentStyle = loading ? { opacity: 0 } : undefined;
177
+
178
+ return (
179
+ <button
180
+ {...webProps}
181
+ {...ariaProps}
182
+ ref={mergedRef}
183
+ id={buttonId}
184
+ onClick={handleClick}
185
+ disabled={isDisabled}
186
+ data-testid={testID}
187
+ aria-busy={loading ? 'true' : undefined}
188
+ style={{ position: 'relative' }}
189
+ >
190
+ {loading && renderSpinner()}
191
+ <span style={contentStyle}>{renderIcon()}</span>
192
+ </button>
193
+ );
194
+ });
195
+
196
+ IconButton.displayName = 'IconButton';
197
+
198
+ export default IconButton;
@@ -0,0 +1,5 @@
1
+ import IconButtonComponent from './IconButton.native';
2
+
3
+ export default IconButtonComponent;
4
+ export { IconButtonComponent as IconButton };
5
+ export * from './types';
@@ -0,0 +1,5 @@
1
+ import IconButtonComponent from './IconButton.web';
2
+
3
+ export default IconButtonComponent;
4
+ export { IconButtonComponent as IconButton };
5
+ export * from './types';
@@ -0,0 +1,5 @@
1
+ import IconButtonComponent from './IconButton.web';
2
+
3
+ export default IconButtonComponent;
4
+ export { IconButtonComponent as IconButton };
5
+ export * from './types';
@@ -0,0 +1,84 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import type { IconName } from '../Icon/icon-types';
4
+ import { Intent, Size } from '@idealyst/theme';
5
+ import { BaseProps } from '../utils/viewStyleProps';
6
+ import { InteractiveAccessibilityProps } from '../utils/accessibility';
7
+
8
+ // Component-specific type aliases for future extensibility
9
+ export type IconButtonType = 'contained' | 'outlined' | 'text';
10
+ export type IconButtonIntentVariant = Intent;
11
+ export type IconButtonSizeVariant = Size;
12
+
13
+ /**
14
+ * Gradient overlay options for icon buttons.
15
+ * Applies a transparent gradient over the intent background color.
16
+ * - 'darken': Transparent to semi-transparent black (darkens one corner)
17
+ * - 'lighten': Transparent to semi-transparent white (lightens one corner)
18
+ */
19
+ export type IconButtonGradient = 'darken' | 'lighten';
20
+
21
+ /**
22
+ * Circular icon button component with multiple visual variants, sizes, and a single icon.
23
+ * Supports contained, outlined, and text styles with customizable intent colors.
24
+ */
25
+ export interface IconButtonProps extends BaseProps, InteractiveAccessibilityProps {
26
+ /**
27
+ * The icon to display. Can be an icon name string or a custom ReactNode.
28
+ */
29
+ icon: IconName | ReactNode;
30
+
31
+ /**
32
+ * Called when the button is pressed
33
+ */
34
+ onPress?: () => void;
35
+
36
+ /**
37
+ * @deprecated Use `onPress` instead. This prop exists for web compatibility only.
38
+ * Using onClick will log a deprecation warning in development.
39
+ */
40
+ onClick?: () => void;
41
+
42
+ /**
43
+ * Whether the button is disabled
44
+ */
45
+ disabled?: boolean;
46
+
47
+ /**
48
+ * The visual style type of the button
49
+ */
50
+ type?: IconButtonType;
51
+
52
+ /**
53
+ * The intent/color scheme of the button
54
+ */
55
+ intent?: IconButtonIntentVariant;
56
+
57
+ /**
58
+ * The size of the button
59
+ */
60
+ size?: IconButtonSizeVariant;
61
+
62
+ /**
63
+ * Apply a gradient background enhancement.
64
+ * Only applies to 'contained' button type.
65
+ */
66
+ gradient?: IconButtonGradient;
67
+
68
+ /**
69
+ * Whether the button is in a loading state.
70
+ * When true, shows a spinner and disables interaction.
71
+ * The spinner color matches the icon color.
72
+ */
73
+ loading?: boolean;
74
+
75
+ /**
76
+ * Additional styles (platform-specific)
77
+ */
78
+ style?: StyleProp<ViewStyle>;
79
+
80
+ /**
81
+ * Test ID for testing
82
+ */
83
+ testID?: string;
84
+ }
@@ -3,8 +3,9 @@ import { Image as RNImage, View } from 'react-native';
3
3
  import { imageStyles } from './Image.styles';
4
4
  import type { ImageProps } from './types';
5
5
  import ActivityIndicator from '../ActivityIndicator';
6
+ import type { IdealystElement } from '../utils/refTypes';
6
7
 
7
- const Image = forwardRef<View, ImageProps>(({
8
+ const Image = forwardRef<IdealystElement, ImageProps>(({
8
9
  source,
9
10
  alt,
10
11
  width,
@@ -57,7 +58,7 @@ const Image = forwardRef<View, ImageProps>(({
57
58
  ];
58
59
 
59
60
  return (
60
- <View ref={ref} nativeID={id} style={containerStyle as any} testID={testID} accessibilityRole="image" accessibilityLabel={accessibilityLabel || alt}>
61
+ <View ref={ref as any} nativeID={id} style={containerStyle as any} testID={testID} accessibilityRole="image" accessibilityLabel={accessibilityLabel || alt}>
61
62
  <RNImage
62
63
  source={imageSource as any}
63
64
  style={[imageStyles.image, { borderRadius }]}