@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.
- package/README.md +3 -3
- package/package.json +3 -3
- package/plugin/__tests__/web.test.ts +2 -2
- package/src/Accordion/Accordion.native.tsx +3 -2
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +3 -2
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +25 -26
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +2 -1
- package/src/Alert/Alert.native.tsx +20 -10
- package/src/Alert/Alert.styles.tsx +148 -86
- package/src/Alert/Alert.web.tsx +10 -5
- package/src/Alert/types.ts +53 -3
- package/src/Avatar/Avatar.native.tsx +3 -2
- package/src/Avatar/Avatar.web.tsx +2 -1
- package/src/Avatar/types.ts +1 -1
- package/src/Badge/Badge.native.tsx +18 -6
- package/src/Badge/Badge.styles.tsx +22 -5
- package/src/Badge/Badge.web.tsx +12 -4
- package/src/Badge/types.ts +14 -2
- package/src/Breadcrumb/Breadcrumb.native.tsx +3 -2
- package/src/Button/Button.native.tsx +16 -6
- package/src/Button/Button.styles.tsx +2 -2
- package/src/Button/Button.web.tsx +19 -15
- package/src/Button/types.ts +6 -10
- package/src/Card/Card.native.tsx +27 -3
- package/src/Card/Card.web.tsx +30 -4
- package/src/Card/types.ts +15 -0
- package/src/Checkbox/Checkbox.native.tsx +5 -4
- package/src/Checkbox/Checkbox.styles.tsx +62 -52
- package/src/Checkbox/Checkbox.web.tsx +4 -3
- package/src/Checkbox/types.ts +1 -1
- package/src/Chip/Chip.native.tsx +30 -7
- package/src/Chip/Chip.styles.tsx +142 -124
- package/src/Chip/Chip.web.tsx +28 -5
- package/src/Chip/types.ts +15 -0
- package/src/Dialog/Dialog.native.tsx +6 -6
- package/src/Dialog/Dialog.web.tsx +5 -5
- package/src/Dialog/types.ts +2 -2
- package/src/Divider/Divider.native.tsx +20 -17
- package/src/Divider/Divider.styles.tsx +51 -29
- package/src/Divider/Divider.web.tsx +5 -4
- package/src/Divider/types.ts +3 -3
- package/src/Icon/Icon.native.tsx +3 -2
- package/src/Icon/Icon.web.tsx +2 -1
- package/src/Icon/IconSvg/IconSvg.native.tsx +3 -2
- package/src/Image/Image.native.tsx +3 -2
- package/src/Input/Input.native.tsx +42 -290
- package/src/Input/Input.styles.tsx +1 -1
- package/src/Input/Input.web.tsx +37 -288
- package/src/Input/index.native.ts +9 -2
- package/src/Input/index.ts +8 -1
- package/src/Input/index.web.ts +8 -1
- package/src/Input/types.ts +1 -1
- package/src/List/List.native.tsx +3 -2
- package/src/List/ListItem.native.tsx +3 -2
- package/src/List/ListSection.native.tsx +3 -2
- package/src/Menu/Menu.native.tsx +2 -1
- package/src/Menu/Menu.styles.tsx +79 -29
- package/src/Menu/Menu.web.tsx +2 -1
- package/src/Menu/MenuItem.native.tsx +4 -3
- package/src/Menu/MenuItem.styles.tsx +81 -32
- package/src/Menu/MenuItem.web.tsx +2 -1
- package/src/Menu/docs.ts +1 -1
- package/src/Popover/Popover.native.tsx +2 -1
- package/src/Popover/Popover.web.tsx +2 -1
- package/src/Popover/types.ts +15 -4
- package/src/Pressable/Pressable.native.tsx +3 -2
- package/src/Pressable/Pressable.web.tsx +3 -5
- package/src/Progress/Progress.native.tsx +5 -4
- package/src/Progress/Progress.web.tsx +3 -3
- package/src/Progress/types.ts +3 -3
- package/src/RadioButton/RadioButton.native.tsx +4 -3
- package/src/RadioButton/RadioButton.styles.tsx +53 -33
- package/src/RadioButton/RadioGroup.native.tsx +3 -2
- package/src/SVGImage/SVGImage.native.tsx +5 -4
- package/src/SVGImage/SVGImage.styles.tsx +44 -10
- package/src/SVGImage/SVGImage.web.tsx +2 -1
- package/src/Screen/Screen.native.tsx +2 -1
- package/src/Screen/Screen.web.tsx +2 -1
- package/src/Select/Select.native.tsx +6 -5
- package/src/Select/Select.styles.tsx +1 -1
- package/src/Select/Select.web.tsx +4 -3
- package/src/Select/types.ts +1 -1
- package/src/Skeleton/Skeleton.native.tsx +2 -1
- package/src/Slider/Slider.native.tsx +9 -8
- package/src/Slider/Slider.web.tsx +10 -9
- package/src/Slider/types.ts +9 -2
- package/src/Switch/Switch.native.tsx +7 -6
- package/src/Switch/Switch.styles.tsx +35 -17
- package/src/Switch/Switch.web.tsx +8 -7
- package/src/Switch/types.ts +44 -4
- package/src/TabBar/TabBar.native.tsx +3 -2
- package/src/Text/Text.native.tsx +3 -2
- package/src/Text/Text.web.tsx +2 -1
- package/src/TextArea/TextArea.native.tsx +3 -2
- package/src/TextArea/TextArea.styles.tsx +2 -2
- package/src/TextArea/TextArea.web.tsx +2 -1
- package/src/TextInput/TextInput.native.tsx +300 -0
- package/src/TextInput/TextInput.styles.tsx +207 -0
- package/src/TextInput/TextInput.web.tsx +301 -0
- package/src/TextInput/index.native.ts +3 -0
- package/src/TextInput/index.ts +5 -0
- package/src/TextInput/index.web.ts +5 -0
- package/src/TextInput/types.ts +163 -0
- package/src/Tooltip/Tooltip.native.tsx +3 -2
- package/src/Video/Video.native.tsx +4 -3
- package/src/View/View.native.tsx +2 -1
- package/src/View/View.web.tsx +2 -1
- package/src/examples/AlertExamples.tsx +5 -5
- package/src/examples/ButtonExamples.tsx +12 -12
- package/src/examples/CardExamples.tsx +1 -1
- package/src/examples/CheckboxExamples.tsx +2 -2
- package/src/examples/ChipExamples.tsx +6 -6
- package/src/examples/DialogExamples.tsx +1 -1
- package/src/examples/DividerExamples.tsx +1 -1
- package/src/examples/InputExamples.tsx +1 -1
- package/src/examples/LinkExamples.tsx +1 -1
- package/src/examples/ListExamples.tsx +1 -1
- package/src/examples/MenuExamples.tsx +2 -2
- package/src/examples/ProgressExamples.tsx +1 -1
- package/src/examples/RadioButtonExamples.tsx +5 -5
- package/src/examples/SVGImageExamples.tsx +1 -1
- package/src/examples/SelectExamples.tsx +1 -1
- package/src/examples/SliderExamples.tsx +5 -5
- package/src/examples/SwitchExamples.tsx +2 -2
- package/src/examples/TableExamples.tsx +1 -1
- package/src/examples/TooltipExamples.tsx +2 -2
- package/src/extensions/index.ts +1 -0
- package/src/extensions/types.ts +10 -3
- package/src/index.ts +23 -2
- package/src/utils/index.ts +12 -0
- 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<
|
|
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<
|
|
13
|
+
const Avatar = forwardRef<IdealystElement, AvatarProps>(({
|
|
13
14
|
src,
|
|
14
15
|
alt,
|
|
15
16
|
fallback,
|
package/src/Avatar/types.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
27
|
-
const colorValue =
|
|
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
|
|
60
|
-
const colorValue =
|
|
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
|
package/src/Badge/Badge.web.tsx
CHANGED
|
@@ -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<
|
|
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
|
-
|
|
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]);
|
package/src/Badge/types.ts
CHANGED
|
@@ -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<
|
|
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 {
|
|
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<
|
|
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
|
-
|
|
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
|
|
73
|
+
backgroundColor: theme.$intents.primary,
|
|
74
74
|
borderColor: 'transparent',
|
|
75
75
|
},
|
|
76
76
|
outlined: {
|
|
77
77
|
backgroundColor: 'transparent',
|
|
78
|
-
borderColor: theme
|
|
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<
|
|
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
|
|
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
|
|
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 &&
|
|
69
|
+
if (!isDisabled && pressHandler) {
|
|
63
70
|
e.stopPropagation();
|
|
64
|
-
|
|
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
|
|
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
|
-
|
|
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;
|
package/src/Button/types.ts
CHANGED
|
@@ -24,19 +24,20 @@ export type ButtonGradient = 'darken' | 'lighten';
|
|
|
24
24
|
*/
|
|
25
25
|
export interface ButtonProps extends BaseProps, InteractiveAccessibilityProps {
|
|
26
26
|
/**
|
|
27
|
-
* The
|
|
27
|
+
* The content to display inside the button
|
|
28
28
|
*/
|
|
29
29
|
children?: ReactNode;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* Called when the button is pressed
|
|
33
33
|
*/
|
|
34
|
-
|
|
34
|
+
onPress?: () => void;
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
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
|
-
|
|
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
|
*/
|
package/src/Card/Card.native.tsx
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { forwardRef,
|
|
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
|
-
|
|
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 :
|
|
98
|
+
onPress: disabled ? undefined : pressHandler,
|
|
75
99
|
disabled,
|
|
76
100
|
android_ripple: { color: 'rgba(0, 0, 0, 0.1)' },
|
|
77
101
|
}),
|
package/src/Card/Card.web.tsx
CHANGED
|
@@ -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<
|
|
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
|
|
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<
|
|
9
|
+
const Checkbox = forwardRef<IdealystElement, CheckboxProps>(({
|
|
9
10
|
checked = false,
|
|
10
11
|
indeterminate = false,
|
|
11
12
|
disabled = false,
|
|
12
|
-
|
|
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
|
-
|
|
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}
|