@idealyst/components 1.2.29 → 1.2.30

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 (131) hide show
  1. package/README.md +3 -3
  2. package/package.json +3 -3
  3. package/plugin/__tests__/web.test.ts +2 -2
  4. package/src/Accordion/Accordion.native.tsx +3 -2
  5. package/src/ActivityIndicator/ActivityIndicator.native.tsx +3 -2
  6. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +25 -26
  7. package/src/ActivityIndicator/ActivityIndicator.web.tsx +2 -1
  8. package/src/Alert/Alert.native.tsx +20 -10
  9. package/src/Alert/Alert.styles.tsx +148 -86
  10. package/src/Alert/Alert.web.tsx +10 -5
  11. package/src/Alert/types.ts +53 -3
  12. package/src/Avatar/Avatar.native.tsx +3 -2
  13. package/src/Avatar/Avatar.web.tsx +2 -1
  14. package/src/Avatar/types.ts +1 -1
  15. package/src/Badge/Badge.native.tsx +18 -6
  16. package/src/Badge/Badge.styles.tsx +22 -5
  17. package/src/Badge/Badge.web.tsx +12 -4
  18. package/src/Badge/types.ts +14 -2
  19. package/src/Breadcrumb/Breadcrumb.native.tsx +3 -2
  20. package/src/Button/Button.native.tsx +16 -6
  21. package/src/Button/Button.styles.tsx +2 -2
  22. package/src/Button/Button.web.tsx +19 -15
  23. package/src/Button/types.ts +6 -10
  24. package/src/Card/Card.native.tsx +27 -3
  25. package/src/Card/Card.web.tsx +30 -4
  26. package/src/Card/types.ts +15 -0
  27. package/src/Checkbox/Checkbox.native.tsx +5 -4
  28. package/src/Checkbox/Checkbox.styles.tsx +62 -52
  29. package/src/Checkbox/Checkbox.web.tsx +4 -3
  30. package/src/Checkbox/types.ts +1 -1
  31. package/src/Chip/Chip.native.tsx +30 -7
  32. package/src/Chip/Chip.styles.tsx +142 -124
  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/Image/Image.native.tsx +3 -2
  46. package/src/Input/Input.native.tsx +42 -290
  47. package/src/Input/Input.styles.tsx +1 -1
  48. package/src/Input/Input.web.tsx +37 -288
  49. package/src/Input/index.native.ts +9 -2
  50. package/src/Input/index.ts +8 -1
  51. package/src/Input/index.web.ts +8 -1
  52. package/src/Input/types.ts +1 -1
  53. package/src/List/List.native.tsx +3 -2
  54. package/src/List/ListItem.native.tsx +3 -2
  55. package/src/List/ListSection.native.tsx +3 -2
  56. package/src/Menu/Menu.native.tsx +2 -1
  57. package/src/Menu/Menu.styles.tsx +79 -29
  58. package/src/Menu/Menu.web.tsx +2 -1
  59. package/src/Menu/MenuItem.native.tsx +4 -3
  60. package/src/Menu/MenuItem.styles.tsx +81 -32
  61. package/src/Menu/MenuItem.web.tsx +2 -1
  62. package/src/Menu/docs.ts +1 -1
  63. package/src/Popover/Popover.native.tsx +2 -1
  64. package/src/Popover/Popover.web.tsx +2 -1
  65. package/src/Popover/types.ts +15 -4
  66. package/src/Pressable/Pressable.native.tsx +3 -2
  67. package/src/Pressable/Pressable.web.tsx +3 -5
  68. package/src/Progress/Progress.native.tsx +5 -4
  69. package/src/Progress/Progress.web.tsx +3 -3
  70. package/src/Progress/types.ts +3 -3
  71. package/src/RadioButton/RadioButton.native.tsx +4 -3
  72. package/src/RadioButton/RadioButton.styles.tsx +53 -33
  73. package/src/RadioButton/RadioGroup.native.tsx +3 -2
  74. package/src/SVGImage/SVGImage.native.tsx +5 -4
  75. package/src/SVGImage/SVGImage.styles.tsx +44 -10
  76. package/src/SVGImage/SVGImage.web.tsx +2 -1
  77. package/src/Screen/Screen.native.tsx +2 -1
  78. package/src/Screen/Screen.web.tsx +2 -1
  79. package/src/Select/Select.native.tsx +6 -5
  80. package/src/Select/Select.styles.tsx +1 -1
  81. package/src/Select/Select.web.tsx +4 -3
  82. package/src/Select/types.ts +1 -1
  83. package/src/Skeleton/Skeleton.native.tsx +2 -1
  84. package/src/Slider/Slider.native.tsx +9 -8
  85. package/src/Slider/Slider.web.tsx +10 -9
  86. package/src/Slider/types.ts +9 -2
  87. package/src/Switch/Switch.native.tsx +7 -6
  88. package/src/Switch/Switch.styles.tsx +35 -17
  89. package/src/Switch/Switch.web.tsx +8 -7
  90. package/src/Switch/types.ts +44 -4
  91. package/src/TabBar/TabBar.native.tsx +3 -2
  92. package/src/Text/Text.native.tsx +3 -2
  93. package/src/Text/Text.web.tsx +2 -1
  94. package/src/TextArea/TextArea.native.tsx +3 -2
  95. package/src/TextArea/TextArea.styles.tsx +2 -2
  96. package/src/TextArea/TextArea.web.tsx +2 -1
  97. package/src/TextInput/TextInput.native.tsx +300 -0
  98. package/src/TextInput/TextInput.styles.tsx +207 -0
  99. package/src/TextInput/TextInput.web.tsx +301 -0
  100. package/src/TextInput/index.native.ts +3 -0
  101. package/src/TextInput/index.ts +5 -0
  102. package/src/TextInput/index.web.ts +5 -0
  103. package/src/TextInput/types.ts +163 -0
  104. package/src/Tooltip/Tooltip.native.tsx +3 -2
  105. package/src/Video/Video.native.tsx +4 -3
  106. package/src/View/View.native.tsx +2 -1
  107. package/src/View/View.web.tsx +2 -1
  108. package/src/examples/AlertExamples.tsx +5 -5
  109. package/src/examples/ButtonExamples.tsx +12 -12
  110. package/src/examples/CardExamples.tsx +1 -1
  111. package/src/examples/CheckboxExamples.tsx +2 -2
  112. package/src/examples/ChipExamples.tsx +6 -6
  113. package/src/examples/DialogExamples.tsx +1 -1
  114. package/src/examples/DividerExamples.tsx +1 -1
  115. package/src/examples/InputExamples.tsx +1 -1
  116. package/src/examples/LinkExamples.tsx +1 -1
  117. package/src/examples/ListExamples.tsx +1 -1
  118. package/src/examples/MenuExamples.tsx +2 -2
  119. package/src/examples/ProgressExamples.tsx +1 -1
  120. package/src/examples/RadioButtonExamples.tsx +5 -5
  121. package/src/examples/SVGImageExamples.tsx +1 -1
  122. package/src/examples/SelectExamples.tsx +1 -1
  123. package/src/examples/SliderExamples.tsx +5 -5
  124. package/src/examples/SwitchExamples.tsx +2 -2
  125. package/src/examples/TableExamples.tsx +1 -1
  126. package/src/examples/TooltipExamples.tsx +2 -2
  127. package/src/extensions/index.ts +1 -0
  128. package/src/extensions/types.ts +10 -3
  129. package/src/index.ts +23 -2
  130. package/src/utils/index.ts +12 -0
  131. package/src/utils/refTypes.ts +50 -0
@@ -3,8 +3,9 @@ import { View, Text, Image } from 'react-native';
3
3
  import { AvatarProps } from './types';
4
4
  import { avatarStyles } from './Avatar.styles';
5
5
  import { getNativeAccessibilityProps } from '../utils/accessibility';
6
+ import type { IdealystElement } from '../utils/refTypes';
6
7
 
7
- const Avatar = forwardRef<View, AvatarProps>(({
8
+ const Avatar = forwardRef<IdealystElement, AvatarProps>(({
8
9
  src,
9
10
  alt,
10
11
  fallback,
@@ -45,7 +46,7 @@ const Avatar = forwardRef<View, AvatarProps>(({
45
46
  const fallbackStyle = (avatarStyles.fallback as any)({});
46
47
 
47
48
  return (
48
- <View ref={ref} nativeID={id} style={[avatarStyle, style]} testID={testID} {...nativeA11yProps}>
49
+ <View ref={ref as any} nativeID={id} style={[avatarStyle, style]} testID={testID} {...nativeA11yProps}>
49
50
  {src && !hasError ? (
50
51
  <Image
51
52
  source={typeof src === 'string' ? { uri: src } : src}
@@ -4,12 +4,13 @@ import { AvatarProps } from './types';
4
4
  import { avatarStyles } from './Avatar.styles';
5
5
  import useMergeRefs from '../hooks/useMergeRefs';
6
6
  import { getWebAriaProps } from '../utils/accessibility';
7
+ import type { IdealystElement } from '../utils/refTypes';
7
8
 
8
9
  /**
9
10
  * User or entity representation with image support and fallback initials.
10
11
  * Available in circle or square shapes with multiple sizes.
11
12
  */
12
- const Avatar = forwardRef<HTMLDivElement, AvatarProps>(({
13
+ const Avatar = forwardRef<IdealystElement, AvatarProps>(({
13
14
  src,
14
15
  alt,
15
16
  fallback,
@@ -5,7 +5,7 @@ import { AccessibilityProps } from '../utils/accessibility';
5
5
 
6
6
  // Component-specific type aliases for future extensibility
7
7
  export type AvatarColorVariant = Color;
8
- export type AvatarSizeVariant = 'sm' | 'md' | 'lg' | 'xl';
8
+ export type AvatarSizeVariant = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
9
9
  export type AvatarShapeVariant = 'circle' | 'square';
10
10
 
11
11
  /**
@@ -4,25 +4,37 @@ import MaterialDesignIcons from '@react-native-vector-icons/material-design-icon
4
4
  import { BadgeProps } from './types';
5
5
  import { badgeStyles } from './Badge.styles';
6
6
  import { isIconName } from '../Icon/icon-resolver';
7
+ import type { IdealystElement } from '../utils/refTypes';
7
8
 
8
- const Badge = forwardRef<View, BadgeProps>(({
9
+ /**
10
+ * Small status indicator for counts, labels, or notifications.
11
+ * Available in filled, outlined, and dot variants with customizable colors.
12
+ *
13
+ * Supports both `intent` (semantic colors) and `color` (raw palette colors).
14
+ * If both are provided, `intent` takes precedence.
15
+ */
16
+ const Badge = forwardRef<IdealystElement, BadgeProps>(({
9
17
  children,
10
18
  icon,
11
19
  size = 'md',
12
20
  type = 'filled',
13
- color = 'blue',
21
+ intent,
22
+ color,
14
23
  style,
15
24
  testID,
16
25
  id,
17
26
  }, ref) => {
27
+ // Default to 'primary' intent if neither intent nor color is provided
28
+ const effectiveColor = intent ? undefined : (color ?? 'primary');
29
+
18
30
  badgeStyles.useVariants({
19
31
  size,
20
32
  type,
21
33
  });
22
34
 
23
- // Call dynamic styles with color variant
24
- const badgeStyle = (badgeStyles.badge as any)({ color });
25
- const textStyle = (badgeStyles.text as any)({ color });
35
+ // Call dynamic styles with intent/color - intent takes precedence
36
+ const badgeStyle = (badgeStyles.badge as any)({ intent, color: effectiveColor });
37
+ const textStyle = (badgeStyles.text as any)({ intent, color: effectiveColor });
26
38
 
27
39
  // Map badge size to icon size
28
40
  const iconSize = size === 'sm' ? 12 : size === 'md' ? 14 : 16;
@@ -62,7 +74,7 @@ const Badge = forwardRef<View, BadgeProps>(({
62
74
 
63
75
  return (
64
76
  <View
65
- ref={ref}
77
+ ref={ref as any}
66
78
  nativeID={id}
67
79
  style={[badgeStyle, style]}
68
80
  testID={testID}
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * Badge styles using defineStyle with $iterator expansion.
3
+ *
4
+ * Supports both `intent` (semantic colors) and `color` (raw palette colors).
5
+ * If both are provided, `intent` takes precedence.
3
6
  */
4
7
  import { StyleSheet } from 'react-native-unistyles';
5
8
  import { defineStyle, ThemeStyleWrapper, getColorFromString } from '@idealyst/theme';
6
- import type { Theme as BaseTheme, Size, Color } from '@idealyst/theme';
9
+ import type { Theme as BaseTheme, Size, Color, Intent } from '@idealyst/theme';
7
10
 
8
11
  // Required: Unistyles must see StyleSheet usage in original source to process this file
9
12
  void StyleSheet;
@@ -16,15 +19,29 @@ type BadgeType = 'filled' | 'outlined' | 'dot';
16
19
  export type BadgeDynamicProps = {
17
20
  size?: Size;
18
21
  type?: BadgeType;
22
+ intent?: Intent;
19
23
  color?: Color;
20
24
  };
21
25
 
26
+ /**
27
+ * Helper to resolve badge color from intent or color prop.
28
+ * Intent takes precedence over color.
29
+ */
30
+ const resolveBadgeColor = (theme: Theme, intent?: Intent, color?: Color): string => {
31
+ if (intent) {
32
+ // Use intent's primary color
33
+ return (theme as unknown as BaseTheme).intents[intent].primary;
34
+ }
35
+ // Fall back to color prop or default to primary intent
36
+ return getColorFromString(theme as unknown as BaseTheme, color ?? 'primary');
37
+ };
38
+
22
39
  /**
23
40
  * Badge styles with color-based type variants.
24
41
  */
25
42
  export const badgeStyles = defineStyle('Badge', (theme: Theme) => ({
26
- badge: ({ color = 'primary', type = 'filled' }: BadgeDynamicProps) => {
27
- const colorValue = getColorFromString(theme as unknown as BaseTheme, color);
43
+ badge: ({ intent, color, type = 'filled' }: BadgeDynamicProps) => {
44
+ const colorValue = resolveBadgeColor(theme, intent, color);
28
45
 
29
46
  const typeStyles = type === 'filled'
30
47
  ? { borderWidth: 0, backgroundColor: colorValue }
@@ -56,8 +73,8 @@ export const badgeStyles = defineStyle('Badge', (theme: Theme) => ({
56
73
  } as const;
57
74
  },
58
75
 
59
- text: ({ color = 'primary', type = 'filled' }: BadgeDynamicProps) => {
60
- const colorValue = getColorFromString(theme as unknown as BaseTheme, color);
76
+ text: ({ intent, color, type = 'filled' }: BadgeDynamicProps) => {
77
+ const colorValue = resolveBadgeColor(theme, intent, color);
61
78
 
62
79
  const textColor = type === 'filled'
63
80
  ? theme.colors.text.inverse
@@ -4,18 +4,23 @@ import { BadgeProps } from './types';
4
4
  import { badgeStyles } from './Badge.styles';
5
5
  import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
6
6
  import useMergeRefs from '../hooks/useMergeRefs';
7
+ import type { IdealystElement } from '../utils/refTypes';
7
8
 
8
9
  /**
9
10
  * Small status indicator for counts, labels, or notifications.
10
11
  * Available in filled, outlined, and dot variants with customizable colors.
12
+ *
13
+ * Supports both `intent` (semantic colors) and `color` (raw palette colors).
14
+ * If both are provided, `intent` takes precedence.
11
15
  */
12
- const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props, ref) => {
16
+ const Badge = forwardRef<IdealystElement, BadgeProps>((props, ref) => {
13
17
  const {
14
18
  children,
15
19
  size = 'md',
16
20
  type: typeProp,
17
21
  variant,
18
- color = 'blue',
22
+ intent,
23
+ color,
19
24
  icon,
20
25
  style,
21
26
  testID,
@@ -25,14 +30,17 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>((props, ref) => {
25
30
  // variant is an alias for type - variant takes precedence if both are set
26
31
  const type = variant ?? typeProp ?? 'filled';
27
32
 
33
+ // Default to 'primary' intent if neither intent nor color is provided
34
+ const effectiveColor = intent ? undefined : (color ?? 'primary');
35
+
28
36
  badgeStyles.useVariants({
29
37
  size,
30
38
  type,
31
39
  });
32
40
 
33
- const badgeStyle = (badgeStyles.badge as any)({ color });
41
+ const badgeStyle = (badgeStyles.badge as any)({ intent, color: effectiveColor });
34
42
  const contentStyle = badgeStyles.content as any;
35
- const textStyle = (badgeStyles.text as any)({ color });
43
+ const textStyle = (badgeStyles.text as any)({ intent, color: effectiveColor });
36
44
 
37
45
  const badgeProps = getWebProps([badgeStyle]);
38
46
  const contentProps = getWebProps([contentStyle]);
@@ -1,11 +1,12 @@
1
1
  import type { CSSProperties, ReactNode } from 'react';
2
2
  import type { StyleProp, ViewStyle } from 'react-native';
3
3
  import type { IconName } from '../Icon/icon-types';
4
- import { Color, Size } from '@idealyst/theme';
4
+ import { Color, Intent, Size } from '@idealyst/theme';
5
5
  import { BaseProps } from '../utils/viewStyleProps';
6
6
 
7
7
  // Component-specific type aliases for future extensibility
8
8
  export type BadgeColorVariant = Color;
9
+ export type BadgeIntentVariant = Intent;
9
10
  export type BadgeSizeVariant = Size;
10
11
  export type BadgeType = 'filled' | 'outlined' | 'dot';
11
12
 
@@ -35,7 +36,18 @@ export interface BadgeProps extends BaseProps {
35
36
  variant?: BadgeType;
36
37
 
37
38
  /**
38
- * The color scheme of the badge
39
+ * The semantic color scheme of the badge.
40
+ * Use for status-based coloring (success, danger, warning, etc.).
41
+ *
42
+ * **Note:** Only one of `intent` or `color` should be set. If both are provided, `intent` takes precedence.
43
+ */
44
+ intent?: BadgeIntentVariant;
45
+
46
+ /**
47
+ * The raw palette color of the badge.
48
+ * Use for custom coloring that doesn't map to a semantic intent.
49
+ *
50
+ * **Note:** Only one of `intent` or `color` should be set. If both are provided, `intent` takes precedence.
39
51
  */
40
52
  color?: BadgeColorVariant;
41
53
 
@@ -6,6 +6,7 @@ import Icon from '../Icon';
6
6
  import type { IconName } from '../Icon/icon-types';
7
7
  import Menu from '../Menu/Menu.native';
8
8
  import type { MenuItem } from '../Menu/types';
9
+ import type { IdealystElement } from '../utils/refTypes';
9
10
 
10
11
  interface BreadcrumbItemProps {
11
12
  item: BreadcrumbItemType;
@@ -113,7 +114,7 @@ const BreadcrumbEllipsis: React.FC<BreadcrumbEllipsisProps> = ({ size, intent })
113
114
  );
114
115
  };
115
116
 
116
- const Breadcrumb = forwardRef<View, BreadcrumbProps>(({
117
+ const Breadcrumb = forwardRef<IdealystElement, BreadcrumbProps>(({
117
118
  items,
118
119
  separator = '/',
119
120
  maxItems,
@@ -169,7 +170,7 @@ const Breadcrumb = forwardRef<View, BreadcrumbProps>(({
169
170
 
170
171
  return (
171
172
  <View
172
- ref={ref}
173
+ ref={ref as any}
173
174
  nativeID={id}
174
175
  style={[containerStyle, style]}
175
176
  testID={testID}
@@ -1,16 +1,17 @@
1
- import { ComponentRef, forwardRef, useMemo } from 'react';
1
+ import { forwardRef, useMemo } from 'react';
2
2
  import { ActivityIndicator, StyleSheet as RNStyleSheet, Text, TouchableOpacity, View } from 'react-native';
3
3
  import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons';
4
4
  import Svg, { Defs, LinearGradient, Stop, Rect } from 'react-native-svg';
5
5
  import { buttonStyles } from './Button.styles';
6
6
  import { ButtonProps } from './types';
7
7
  import { getNativeInteractiveAccessibilityProps } from '../utils/accessibility';
8
+ import type { IdealystElement } from '../utils/refTypes';
8
9
 
9
- const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((props, ref) => {
10
+ const Button = forwardRef<IdealystElement, ButtonProps>((props, ref) => {
10
11
  const {
11
12
  children,
12
- title,
13
13
  onPress,
14
+ onClick,
14
15
  disabled = false,
15
16
  loading = false,
16
17
  type = 'contained',
@@ -38,6 +39,16 @@ const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((p
38
39
  // Button is effectively disabled when loading
39
40
  const isDisabled = disabled || loading;
40
41
 
42
+ // Determine the handler to use - onPress takes precedence
43
+ const pressHandler = onPress ?? onClick;
44
+
45
+ // Warn about deprecated onClick usage in development
46
+ if (__DEV__ && onClick && !onPress) {
47
+ console.warn(
48
+ 'Button: onClick prop is deprecated. Use onPress instead for cross-platform compatibility.'
49
+ );
50
+ }
51
+
41
52
  // Apply variants for size, disabled, gradient
42
53
  buttonStyles.useVariants({
43
54
  size,
@@ -67,8 +78,7 @@ const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((p
67
78
  const iconSize = iconSizeMap[size] ?? 16;
68
79
 
69
80
 
70
- // Use children if available, otherwise use title
71
- const buttonContent = children || title;
81
+ const buttonContent = children;
72
82
 
73
83
  // Determine if we need to wrap content in icon container
74
84
  const hasIcons = leftIcon || rightIcon;
@@ -159,7 +169,7 @@ const Button = forwardRef<ComponentRef<typeof TouchableOpacity>, ButtonProps>((p
159
169
  // TouchableOpacity types don't include nativeID but it's a valid RN prop
160
170
  const touchableProps = {
161
171
  ref,
162
- onPress,
172
+ onPress: pressHandler,
163
173
  disabled: isDisabled,
164
174
  testID,
165
175
  nativeID: id,
@@ -70,12 +70,12 @@ export const buttonStyles = defineStyle('Button', (theme: Theme) => ({
70
70
  // $iterator expands for each button size
71
71
  type: {
72
72
  contained: {
73
- backgroundColor: theme.intents[intent].primary,
73
+ backgroundColor: theme.$intents.primary,
74
74
  borderColor: 'transparent',
75
75
  },
76
76
  outlined: {
77
77
  backgroundColor: 'transparent',
78
- borderColor: theme.intents[intent].primary,
78
+ borderColor: theme.$intents.primary,
79
79
  },
80
80
  text: {
81
81
  backgroundColor: 'transparent',
@@ -5,20 +5,20 @@ import { buttonStyles } from './Button.styles';
5
5
  import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
6
6
  import useMergeRefs from '../hooks/useMergeRefs';
7
7
  import { getWebInteractiveAriaProps, generateAccessibilityId } from '../utils/accessibility';
8
+ import type { IdealystElement } from '../utils/refTypes';
8
9
 
9
10
  /**
10
11
  * Interactive button component with multiple visual variants, sizes, and icon support.
11
12
  * Supports contained, outlined, and text styles with customizable intent colors.
12
13
  */
13
- const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
14
+ const Button = forwardRef<IdealystElement, ButtonProps>((props, ref) => {
14
15
  const {
15
- title,
16
16
  children,
17
17
  onPress,
18
+ onClick,
18
19
  disabled = false,
19
20
  loading = false,
20
- type: typeProp,
21
- variant,
21
+ type = 'contained',
22
22
  intent = 'primary',
23
23
  size = 'md',
24
24
  gradient,
@@ -42,9 +42,6 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
42
42
  accessibilityHasPopup,
43
43
  } = props;
44
44
 
45
- // variant is an alias for type - variant takes precedence if both are set
46
- const type = variant ?? typeProp ?? 'contained';
47
-
48
45
  // Button is effectively disabled when loading
49
46
  const isDisabled = disabled || loading;
50
47
 
@@ -55,13 +52,23 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
55
52
  gradient,
56
53
  });
57
54
 
55
+ // Determine the handler to use - onPress takes precedence
56
+ const pressHandler = onPress ?? onClick;
57
+
58
+ // Warn about deprecated onClick usage in development
59
+ if (process.env.NODE_ENV !== 'production' && onClick && !onPress) {
60
+ console.warn(
61
+ 'Button: onClick prop is deprecated. Use onPress instead for cross-platform compatibility.'
62
+ );
63
+ }
64
+
58
65
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
59
66
  e.preventDefault();
60
- // Only stop propagation if we have an onPress handler
67
+ // Only stop propagation if we have a handler
61
68
  // Otherwise, let clicks bubble up to parent handlers (e.g., Menu triggers)
62
- if (!isDisabled && onPress) {
69
+ if (!isDisabled && pressHandler) {
63
70
  e.stopPropagation();
64
- onPress();
71
+ pressHandler();
65
72
  }
66
73
  };
67
74
 
@@ -71,8 +78,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
71
78
  // Generate ARIA props - especially important for icon-only buttons
72
79
  const ariaProps = useMemo(() => {
73
80
  // For icon-only buttons, accessibilityLabel is critical
74
- const buttonContent = children || title;
75
- const isIconOnly = !buttonContent && (leftIcon || rightIcon);
81
+ const isIconOnly = !children && (leftIcon || rightIcon);
76
82
  const computedLabel = accessibilityLabel ?? (isIconOnly && typeof leftIcon === 'string' ? leftIcon : undefined);
77
83
 
78
84
  return getWebInteractiveAriaProps({
@@ -92,7 +98,6 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
92
98
  }, [
93
99
  accessibilityLabel,
94
100
  children,
95
- title,
96
101
  leftIcon,
97
102
  rightIcon,
98
103
  accessibilityHint,
@@ -149,8 +154,7 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
149
154
  return null;
150
155
  };
151
156
 
152
- // Use children if available, otherwise use title
153
- const buttonContent = children || title;
157
+ const buttonContent = children;
154
158
 
155
159
  // Determine if we need to wrap content in icon container
156
160
  const hasIcons = leftIcon || rightIcon;
@@ -24,19 +24,20 @@ export type ButtonGradient = 'darken' | 'lighten';
24
24
  */
25
25
  export interface ButtonProps extends BaseProps, InteractiveAccessibilityProps {
26
26
  /**
27
- * The text or content to display inside the button
27
+ * The content to display inside the button
28
28
  */
29
29
  children?: ReactNode;
30
30
 
31
31
  /**
32
- * The text title to display inside the button (for web)
32
+ * Called when the button is pressed
33
33
  */
34
- title?: string;
34
+ onPress?: () => void;
35
35
 
36
36
  /**
37
- * Called when the button is pressed
37
+ * @deprecated Use `onPress` instead. This prop exists for web compatibility only.
38
+ * Using onClick will log a deprecation warning in development.
38
39
  */
39
- onPress?: () => void;
40
+ onClick?: () => void;
40
41
 
41
42
  /**
42
43
  * Whether the button is disabled
@@ -48,11 +49,6 @@ export interface ButtonProps extends BaseProps, InteractiveAccessibilityProps {
48
49
  */
49
50
  type?: ButtonType;
50
51
 
51
- /**
52
- * Alias for type - the visual style variant of the button
53
- */
54
- variant?: ButtonType;
55
-
56
52
  /**
57
53
  * The intent/color scheme of the button
58
54
  */
@@ -1,16 +1,21 @@
1
- import { forwardRef, ComponentRef, useMemo } from 'react';
1
+ import { forwardRef, useMemo, useEffect, useRef } from 'react';
2
2
  import { View, Pressable } from 'react-native';
3
3
  import { CardProps } from './types';
4
4
  import { cardStyles } from './Card.styles';
5
5
  import { getNativeInteractiveAccessibilityProps } from '../utils/accessibility';
6
+ import type { IdealystElement } from '../utils/refTypes';
6
7
 
7
- const Card = forwardRef<ComponentRef<typeof View> | ComponentRef<typeof Pressable>, CardProps>(({
8
+ // Track if we've logged the onClick deprecation warning (log once per session)
9
+ let hasLoggedOnClickWarning = false;
10
+
11
+ const Card = forwardRef<IdealystElement, CardProps>(({
8
12
  children,
9
13
  type = 'elevated',
10
14
  radius = 'md',
11
15
  intent: _intent = 'neutral',
12
16
  clickable = false,
13
17
  onPress,
18
+ onClick,
14
19
  disabled = false,
15
20
  // Spacing variants from ContainerStyleProps
16
21
  gap,
@@ -31,6 +36,22 @@ const Card = forwardRef<ComponentRef<typeof View> | ComponentRef<typeof Pressabl
31
36
  accessibilityRole,
32
37
  accessibilityPressed,
33
38
  }, ref) => {
39
+ const hasWarnedRef = useRef(false);
40
+
41
+ // Warn about onClick usage (deprecated)
42
+ useEffect(() => {
43
+ if (onClick && !hasWarnedRef.current && !hasLoggedOnClickWarning) {
44
+ hasWarnedRef.current = true;
45
+ hasLoggedOnClickWarning = true;
46
+ console.warn(
47
+ '[Card] onClick is deprecated. Use onPress instead.\n' +
48
+ 'Card is a cross-platform component that follows React Native conventions.\n' +
49
+ 'onClick will be removed in a future version.\n\n' +
50
+ 'Migration: Replace onClick={handler} with onPress={handler}'
51
+ );
52
+ }
53
+ }, [onClick]);
54
+
34
55
  // Generate native accessibility props
35
56
  const nativeA11yProps = useMemo(() => {
36
57
  return getNativeInteractiveAccessibilityProps({
@@ -64,6 +85,9 @@ const Card = forwardRef<ComponentRef<typeof View> | ComponentRef<typeof Pressabl
64
85
  // Use appropriate component based on clickable state
65
86
  const Component = clickable ? Pressable : View;
66
87
 
88
+ // Prefer onPress, fall back to deprecated onClick
89
+ const pressHandler = onPress ?? onClick;
90
+
67
91
  const componentProps = {
68
92
  ref,
69
93
  nativeID: id,
@@ -71,7 +95,7 @@ const Card = forwardRef<ComponentRef<typeof View> | ComponentRef<typeof Pressabl
71
95
  testID,
72
96
  ...nativeA11yProps,
73
97
  ...(clickable && {
74
- onPress: disabled ? undefined : onPress,
98
+ onPress: disabled ? undefined : pressHandler,
75
99
  disabled,
76
100
  android_ripple: { color: 'rgba(0, 0, 0, 0.1)' },
77
101
  }),
@@ -1,15 +1,19 @@
1
- import { forwardRef, useMemo } from 'react';
1
+ import { forwardRef, useMemo, useEffect, useRef } from 'react';
2
2
  import { getWebProps } from 'react-native-unistyles/web';
3
3
  import { CardProps } from './types';
4
4
  import { cardStyles } from './Card.styles';
5
5
  import useMergeRefs from '../hooks/useMergeRefs';
6
6
  import { getWebInteractiveAriaProps } from '../utils/accessibility';
7
+ import type { IdealystElement } from '../utils/refTypes';
8
+
9
+ // Track if we've logged the onClick deprecation warning (log once per session)
10
+ let hasLoggedOnClickWarning = false;
7
11
 
8
12
  /**
9
13
  * Container component for grouping related content with elevation and styling options.
10
14
  * Supports elevated, outlined, and filled variants with optional click interaction.
11
15
  */
12
- const Card = forwardRef<HTMLDivElement | HTMLButtonElement, CardProps>(({
16
+ const Card = forwardRef<IdealystElement, CardProps>(({
13
17
  children,
14
18
  type: typeProp,
15
19
  variant,
@@ -17,6 +21,7 @@ const Card = forwardRef<HTMLDivElement | HTMLButtonElement, CardProps>(({
17
21
  intent: _intent,
18
22
  clickable = false,
19
23
  onPress,
24
+ onClick,
20
25
  disabled = false,
21
26
  // Spacing variants from ContainerStyleProps
22
27
  gap,
@@ -37,6 +42,22 @@ const Card = forwardRef<HTMLDivElement | HTMLButtonElement, CardProps>(({
37
42
  accessibilityRole,
38
43
  accessibilityPressed,
39
44
  }, ref) => {
45
+ const hasWarnedRef = useRef(false);
46
+
47
+ // Warn about onClick usage (deprecated)
48
+ useEffect(() => {
49
+ if (onClick && !hasWarnedRef.current && !hasLoggedOnClickWarning) {
50
+ hasWarnedRef.current = true;
51
+ hasLoggedOnClickWarning = true;
52
+ console.warn(
53
+ '[Card] onClick is deprecated. Use onPress instead.\n' +
54
+ 'Card is a cross-platform component that follows React Native conventions.\n' +
55
+ 'onClick will be removed in a future version.\n\n' +
56
+ 'Migration: Replace onClick={handler} with onPress={handler}'
57
+ );
58
+ }
59
+ }, [onClick]);
60
+
40
61
  // variant is an alias for type - variant takes precedence if both are set
41
62
  const type = variant ?? typeProp ?? 'elevated';
42
63
  // Generate ARIA props
@@ -50,9 +71,14 @@ const Card = forwardRef<HTMLDivElement | HTMLButtonElement, CardProps>(({
50
71
  accessibilityPressed,
51
72
  });
52
73
  }, [accessibilityLabel, accessibilityHint, accessibilityDisabled, disabled, accessibilityHidden, accessibilityRole, clickable, accessibilityPressed]);
74
+
53
75
  const handleClick = () => {
54
- if (!disabled && clickable && onPress) {
55
- onPress();
76
+ if (!disabled && clickable) {
77
+ // Prefer onPress, fall back to deprecated onClick
78
+ const handler = onPress ?? onClick;
79
+ if (handler) {
80
+ handler();
81
+ }
56
82
  }
57
83
  };
58
84
 
package/src/Card/types.ts CHANGED
@@ -49,6 +49,21 @@ export interface CardProps extends ContainerStyleProps, InteractiveAccessibility
49
49
  */
50
50
  onPress?: () => void;
51
51
 
52
+ /**
53
+ * @deprecated Use `onPress` instead. This is a cross-platform component - use React Native conventions.
54
+ *
55
+ * Using `onClick` will trigger a console warning in development.
56
+ * This prop exists only for migration convenience and will be removed in a future version.
57
+ *
58
+ * @example
59
+ * // ❌ Don't use onClick
60
+ * <Card clickable onClick={() => {}} />
61
+ *
62
+ * // ✅ Use onPress instead
63
+ * <Card clickable onPress={() => {}} />
64
+ */
65
+ onClick?: () => void;
66
+
52
67
  /**
53
68
  * Whether the card is disabled
54
69
  */
@@ -4,12 +4,13 @@ import MaterialDesignIcons from '@react-native-vector-icons/material-design-icon
4
4
  import { CheckboxProps } from './types';
5
5
  import { checkboxStyles } from './Checkbox.styles';
6
6
  import { getNativeSelectionAccessibilityProps } from '../utils/accessibility';
7
+ import type { IdealystElement } from '../utils/refTypes';
7
8
 
8
- const Checkbox = forwardRef<View, CheckboxProps>(({
9
+ const Checkbox = forwardRef<IdealystElement, CheckboxProps>(({
9
10
  checked = false,
10
11
  indeterminate = false,
11
12
  disabled = false,
12
- onCheckedChange,
13
+ onChange,
13
14
  size = 'md',
14
15
  intent = 'primary',
15
16
  variant = 'default',
@@ -46,7 +47,7 @@ const Checkbox = forwardRef<View, CheckboxProps>(({
46
47
 
47
48
  const newChecked = !internalChecked;
48
49
  setInternalChecked(newChecked);
49
- onCheckedChange?.(newChecked);
50
+ onChange?.(newChecked);
50
51
  };
51
52
 
52
53
  // Generate native accessibility props
@@ -108,7 +109,7 @@ const Checkbox = forwardRef<View, CheckboxProps>(({
108
109
  const helperTextStyle = (checkboxStyles.helperText as any)({ error: !!error });
109
110
 
110
111
  return (
111
- <View ref={ref} nativeID={id} style={[wrapperStyle, style]}>
112
+ <View ref={ref as any} nativeID={id} style={[wrapperStyle, style]}>
112
113
  <Pressable
113
114
  onPress={handlePress}
114
115
  disabled={disabled}