@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.
- package/README.md +3 -3
- package/package.json +4 -4
- package/plugin/__tests__/web.test.ts +2 -2
- package/plugin/web.js +2 -0
- package/src/Accordion/Accordion.native.tsx +3 -2
- package/src/ActivityIndicator/ActivityIndicator.native.tsx +4 -2
- package/src/ActivityIndicator/ActivityIndicator.styles.tsx +22 -27
- package/src/ActivityIndicator/ActivityIndicator.web.tsx +17 -29
- package/src/Alert/Alert.native.tsx +20 -10
- package/src/Alert/Alert.styles.tsx +173 -86
- package/src/Alert/Alert.web.tsx +34 -30
- 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.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/IconButton/IconButton.native.tsx +219 -0
- package/src/IconButton/IconButton.styles.tsx +127 -0
- package/src/IconButton/IconButton.web.tsx +198 -0
- package/src/IconButton/index.native.ts +5 -0
- package/src/IconButton/index.ts +5 -0
- package/src/IconButton/index.web.ts +5 -0
- package/src/IconButton/types.ts +84 -0
- 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/Skeleton/Skeleton.web.tsx +1 -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 +52 -17
- package/src/Switch/Switch.web.tsx +15 -16
- 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.styles.tsx +1 -0
- package/src/View/View.web.tsx +9 -2
- package/src/examples/ActivityIndicatorExamples.tsx +177 -0
- 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 +26 -26
- package/src/examples/TableExamples.tsx +1 -1
- package/src/examples/TooltipExamples.tsx +2 -2
- package/src/examples/index.ts +1 -0
- package/src/extensions/index.ts +1 -0
- package/src/extensions/types.ts +22 -3
- package/src/index.native.ts +4 -0
- package/src/index.ts +27 -2
- package/src/utils/index.ts +12 -0
- package/src/utils/refTypes.ts +50 -0
package/src/Alert/Alert.web.tsx
CHANGED
|
@@ -1,31 +1,33 @@
|
|
|
1
|
-
import { isValidElement, forwardRef } from 'react';
|
|
1
|
+
import { isValidElement, forwardRef, ElementType, useMemo } from 'react';
|
|
2
2
|
import { getWebProps } from 'react-native-unistyles/web';
|
|
3
3
|
import { alertStyles } from './Alert.styles';
|
|
4
4
|
import type { AlertProps } from './types';
|
|
5
5
|
import { IconSvg } from '../Icon/IconSvg/IconSvg.web';
|
|
6
6
|
import { isIconName } from '../Icon/icon-resolver';
|
|
7
7
|
import useMergeRefs from '../hooks/useMergeRefs';
|
|
8
|
+
import type { IdealystElement } from '../utils/refTypes';
|
|
8
9
|
|
|
9
10
|
// Default icons for each intent
|
|
10
|
-
const defaultIcons: Record<string,
|
|
11
|
-
primary:
|
|
12
|
-
success:
|
|
13
|
-
error:
|
|
14
|
-
warning:
|
|
15
|
-
info:
|
|
16
|
-
neutral:
|
|
11
|
+
const defaultIcons: Record<string, React.ComponentType<any>> = {
|
|
12
|
+
primary: (props: any) => <IconSvg {...props} name="information" aria-label="information" />,
|
|
13
|
+
success: (props: any) => <IconSvg {...props} name="check-circle" aria-label="check-circle" />,
|
|
14
|
+
error: (props: any) => <IconSvg {...props} name="alert-circle" aria-label="alert-circle" />,
|
|
15
|
+
warning: (props: any) => <IconSvg {...props} name="alert" aria-label="alert" />,
|
|
16
|
+
info: (props: any) => <IconSvg {...props} name="information" aria-label="information" />,
|
|
17
|
+
neutral: (props: any) => <IconSvg {...props} name="record-circle" aria-label="record-circle" />,
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Notification banner for displaying important messages, warnings, or status updates.
|
|
21
22
|
* Supports multiple intents, dismissibility, and custom actions.
|
|
22
23
|
*/
|
|
23
|
-
const Alert = forwardRef<
|
|
24
|
+
const Alert = forwardRef<IdealystElement, AlertProps>(({
|
|
24
25
|
title,
|
|
25
26
|
message,
|
|
26
27
|
children,
|
|
27
28
|
intent = 'neutral',
|
|
28
29
|
type = 'soft',
|
|
30
|
+
size = 'md',
|
|
29
31
|
icon,
|
|
30
32
|
showIcon = true,
|
|
31
33
|
dismissible = false,
|
|
@@ -35,37 +37,39 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(({
|
|
|
35
37
|
testID,
|
|
36
38
|
id,
|
|
37
39
|
}, ref) => {
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
+
// Apply variants for size, intent, and type
|
|
41
|
+
alertStyles.useVariants({ size, intent, type });
|
|
42
|
+
|
|
43
|
+
// Compute dynamic styles with intent, type, and size
|
|
44
|
+
const dynamicProps = { intent, type, size };
|
|
40
45
|
const containerProps = getWebProps([(alertStyles.container as any)(dynamicProps), style as any]);
|
|
41
46
|
const iconContainerProps = getWebProps([(alertStyles.iconContainer as any)(dynamicProps)]);
|
|
42
|
-
const contentProps = getWebProps([(alertStyles.content as any)(
|
|
47
|
+
const contentProps = getWebProps([(alertStyles.content as any)(dynamicProps)]);
|
|
43
48
|
const titleProps = getWebProps([(alertStyles.title as any)(dynamicProps)]);
|
|
44
49
|
const messageProps = getWebProps([(alertStyles.message as any)(dynamicProps)]);
|
|
45
|
-
const actionsProps = getWebProps([(alertStyles.actions as any)(
|
|
50
|
+
const actionsProps = getWebProps([(alertStyles.actions as any)(dynamicProps)]);
|
|
46
51
|
const closeButtonProps = getWebProps([(alertStyles.closeButton as any)(dynamicProps)]);
|
|
47
52
|
const closeIconProps = getWebProps([(alertStyles.closeIcon as any)(dynamicProps)]);
|
|
48
53
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
const Icon = useMemo(() => {
|
|
55
|
+
if (!showIcon) return null;
|
|
56
|
+
if (!icon) {
|
|
57
|
+
const Element = defaultIcons[intent];
|
|
58
|
+
if (Element) {
|
|
59
|
+
return <Element {...iconContainerProps} />;
|
|
60
|
+
}
|
|
61
|
+
return null
|
|
62
|
+
} else if (typeof icon === 'string') {
|
|
63
|
+
return <IconSvg
|
|
64
|
+
name={icon}
|
|
59
65
|
{...iconContainerProps}
|
|
60
|
-
aria-label={
|
|
66
|
+
aria-label={icon}
|
|
61
67
|
/>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return iconProp;
|
|
68
|
+
} else if (isValidElement(icon)) {
|
|
69
|
+
return icon;
|
|
65
70
|
}
|
|
66
|
-
|
|
67
71
|
return null;
|
|
68
|
-
};
|
|
72
|
+
}, [icon, showIcon, intent]);
|
|
69
73
|
|
|
70
74
|
const mergedRef = useMergeRefs(ref, containerProps.ref);
|
|
71
75
|
|
|
@@ -77,7 +81,7 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(({
|
|
|
77
81
|
data-testid={testID}
|
|
78
82
|
role="alert"
|
|
79
83
|
>
|
|
80
|
-
{
|
|
84
|
+
{Icon}
|
|
81
85
|
|
|
82
86
|
<div {...contentProps}>
|
|
83
87
|
{title && (
|
package/src/Alert/types.ts
CHANGED
|
@@ -1,29 +1,79 @@
|
|
|
1
1
|
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
-
import type { Intent } from '@idealyst/theme';
|
|
2
|
+
import type { Intent, Size } from '@idealyst/theme';
|
|
3
3
|
import { BaseProps } from '../utils/viewStyleProps';
|
|
4
4
|
|
|
5
5
|
// Component-specific type aliases for future extensibility
|
|
6
6
|
export type AlertIntentVariant = Intent;
|
|
7
|
-
export type
|
|
7
|
+
export type AlertSizeVariant = Size;
|
|
8
|
+
export type AlertType = 'filled' | 'outlined' | 'soft';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Feedback message component for displaying important information to users.
|
|
11
|
-
* Supports multiple intent colors, dismissible behavior, and custom actions.
|
|
12
|
+
* Supports multiple intent colors, sizes, dismissible behavior, and custom actions.
|
|
12
13
|
*/
|
|
13
14
|
export interface AlertProps extends BaseProps {
|
|
14
15
|
/**
|
|
15
16
|
* The title of the alert
|
|
16
17
|
*/
|
|
17
18
|
title?: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The message content of the alert
|
|
22
|
+
*/
|
|
18
23
|
message?: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Custom content to display in the alert
|
|
27
|
+
*/
|
|
19
28
|
children?: React.ReactNode;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The intent/color scheme of the alert
|
|
32
|
+
*/
|
|
20
33
|
intent?: AlertIntentVariant;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The visual style type of the alert
|
|
37
|
+
*/
|
|
21
38
|
type?: AlertType;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The size of the alert
|
|
42
|
+
*/
|
|
43
|
+
size?: AlertSizeVariant;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Custom icon to display. Pass `null` to hide icon.
|
|
47
|
+
*/
|
|
22
48
|
icon?: React.ReactNode;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Whether to show the default intent icon
|
|
52
|
+
*/
|
|
23
53
|
showIcon?: boolean;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether the alert can be dismissed
|
|
57
|
+
*/
|
|
24
58
|
dismissible?: boolean;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Called when the alert is dismissed
|
|
62
|
+
*/
|
|
25
63
|
onDismiss?: () => void;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Action buttons to display at the bottom of the alert
|
|
67
|
+
*/
|
|
26
68
|
actions?: React.ReactNode;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Additional styles
|
|
72
|
+
*/
|
|
27
73
|
style?: StyleProp<ViewStyle>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Test ID for testing
|
|
77
|
+
*/
|
|
28
78
|
testID?: string;
|
|
29
79
|
}
|
|
@@ -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
|
*/
|