@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
package/README.md CHANGED
@@ -198,15 +198,15 @@ The library includes a comprehensive theme system with light and dark mode suppo
198
198
  All components use a consistent intent-based color system:
199
199
 
200
200
  - `primary`: Main brand actions
201
- - `neutral`: Secondary actions
201
+ - `neutral`: Secondary actions
202
202
  - `success`: Positive actions (save, confirm)
203
- - `error`: Destructive actions (delete, cancel)
203
+ - `danger`: Destructive actions (delete, cancel)
204
204
  - `warning`: Caution actions
205
205
 
206
206
  ```tsx
207
207
  <Button variant="contained" intent="primary">Primary Action</Button>
208
208
  <Button variant="contained" intent="success">Save</Button>
209
- <Button variant="contained" intent="error">Delete</Button>
209
+ <Button variant="contained" intent="danger">Delete</Button>
210
210
  ```
211
211
 
212
212
  ### Theme Integration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/components",
3
- "version": "1.2.29",
3
+ "version": "1.2.30",
4
4
  "description": "Shared component library for React and React Native",
5
5
  "documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/components#readme",
6
6
  "readme": "README.md",
@@ -56,7 +56,7 @@
56
56
  "publish:npm": "npm publish"
57
57
  },
58
58
  "peerDependencies": {
59
- "@idealyst/theme": "^1.2.29",
59
+ "@idealyst/theme": "^1.2.30",
60
60
  "@mdi/js": ">=7.0.0",
61
61
  "@mdi/react": ">=1.0.0",
62
62
  "@react-native-vector-icons/common": ">=12.0.0",
@@ -106,7 +106,7 @@
106
106
  }
107
107
  },
108
108
  "devDependencies": {
109
- "@idealyst/theme": "^1.2.29",
109
+ "@idealyst/theme": "^1.2.30",
110
110
  "@idealyst/tooling": "^1.2.4",
111
111
  "@mdi/react": "^1.6.1",
112
112
  "@types/react": "^19.1.0",
@@ -468,12 +468,12 @@ describe('MDI Icon Registry Babel Plugin', () => {
468
468
 
469
469
  it('does not detect icon-like strings in other props', () => {
470
470
  const code = `
471
- <Button title="home" aria-label="eye">Click me</Button>
471
+ <Button data-name="home" aria-label="eye">Click me</Button>
472
472
  `;
473
473
  const result = transform(code);
474
474
  const icons = getDetectedIcons(result?.code);
475
475
 
476
- // title and aria-label are not icon props
476
+ // data-name and aria-label are not icon props
477
477
  expect(icons).not.toContain('home');
478
478
  expect(icons).not.toContain('eye');
479
479
  });
@@ -6,6 +6,7 @@ import { accordionStyles } from './Accordion.styles';
6
6
  import Text from '../Text';
7
7
  import type { AccordionProps, AccordionItem as AccordionItemType } from './types';
8
8
  import { getNativeAccessibilityProps } from '../utils/accessibility';
9
+ import type { IdealystElement } from '../utils/refTypes';
9
10
 
10
11
  interface AccordionItemProps {
11
12
  item: AccordionItemType;
@@ -135,7 +136,7 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
135
136
  );
136
137
  };
137
138
 
138
- const Accordion = forwardRef<View, AccordionProps>(({
139
+ const Accordion = forwardRef<IdealystElement, AccordionProps>(({
139
140
  items,
140
141
  allowMultiple = false,
141
142
  defaultExpanded = [],
@@ -204,7 +205,7 @@ const Accordion = forwardRef<View, AccordionProps>(({
204
205
  const containerStyle = (accordionStyles.container as any)({});
205
206
 
206
207
  return (
207
- <View ref={ref} nativeID={id} style={[containerStyle, style]} testID={testID} {...nativeA11yProps}>
208
+ <View ref={ref as any} nativeID={id} style={[containerStyle, style]} testID={testID} {...nativeA11yProps}>
208
209
  {items.map((item, index) => (
209
210
  <AccordionItem
210
211
  key={item.id}
@@ -3,8 +3,9 @@ import { ActivityIndicator as RNActivityIndicator, View } from 'react-native';
3
3
  import { ActivityIndicatorProps } from './types';
4
4
  import { activityIndicatorStyles } from './ActivityIndicator.styles';
5
5
  import { getNativeLiveRegionAccessibilityProps } from '../utils/accessibility';
6
+ import type { IdealystElement } from '../utils/refTypes';
6
7
 
7
- const ActivityIndicator = forwardRef<View, ActivityIndicatorProps>(({
8
+ const ActivityIndicator = forwardRef<IdealystElement, ActivityIndicatorProps>(({
8
9
  animating = true,
9
10
  size = 'md',
10
11
  intent = 'primary',
@@ -55,7 +56,7 @@ const ActivityIndicator = forwardRef<View, ActivityIndicatorProps>(({
55
56
  },
56
57
  style
57
58
  ]}
58
- ref={ref}
59
+ ref={ref as any}
59
60
  nativeID={id}
60
61
  testID={testID}
61
62
  {...nativeA11yProps}
@@ -37,32 +37,31 @@ export const activityIndicatorStyles = defineStyle('ActivityIndicator', (theme:
37
37
  },
38
38
  }),
39
39
 
40
- spinner: ({ intent = 'primary' }: ActivityIndicatorDynamicProps) => {
41
- // Inline color access for Unistyles to trace
42
- const color = theme.intents[intent].primary;
43
-
44
- return {
45
- borderRadius: 9999,
46
- borderStyle: 'solid' as const,
47
- color,
48
- variants: {
49
- size: {
50
- width: theme.sizes.$activityIndicator.size,
51
- height: theme.sizes.$activityIndicator.size,
52
- borderWidth: theme.sizes.$activityIndicator.borderWidth,
53
- },
54
- animating: {
55
- true: {},
56
- false: {},
57
- },
40
+ spinner: (_props: ActivityIndicatorDynamicProps) => ({
41
+ borderRadius: 9999,
42
+ borderStyle: 'solid' as const,
43
+ variants: {
44
+ size: {
45
+ width: theme.sizes.$activityIndicator.size,
46
+ height: theme.sizes.$activityIndicator.size,
47
+ borderWidth: theme.sizes.$activityIndicator.borderWidth,
58
48
  },
59
- _web: {
60
- borderColor: 'transparent',
61
- borderTopColor: color,
62
- borderRightColor: color,
63
- animation: 'spin 1s linear infinite',
64
- boxSizing: 'border-box',
49
+ animating: {
50
+ true: {},
51
+ false: {},
52
+ },
53
+ intent: {
54
+ color: theme.$intents.primary,
55
+ _web: {
56
+ borderColor: 'transparent',
57
+ borderTopColor: theme.$intents.primary,
58
+ borderRightColor: theme.$intents.primary,
59
+ },
65
60
  },
66
- } as const;
67
- },
61
+ },
62
+ _web: {
63
+ animation: 'spin 1s linear infinite',
64
+ boxSizing: 'border-box',
65
+ },
66
+ }),
68
67
  }));
@@ -4,12 +4,13 @@ import { ActivityIndicatorProps } from './types';
4
4
  import { activityIndicatorStyles } from './ActivityIndicator.styles';
5
5
  import useMergeRefs from '../hooks/useMergeRefs';
6
6
  import { getWebLiveRegionAriaProps } from '../utils/accessibility';
7
+ import type { IdealystElement } from '../utils/refTypes';
7
8
 
8
9
  /**
9
10
  * Spinning loading indicator for async operations and content loading.
10
11
  * Supports intent-based coloring and automatic hiding when stopped.
11
12
  */
12
- const ActivityIndicator = forwardRef<HTMLDivElement, ActivityIndicatorProps>(({
13
+ const ActivityIndicator = forwardRef<IdealystElement, ActivityIndicatorProps>(({
13
14
  animating = true,
14
15
  size = 'md',
15
16
  intent = 'primary',
@@ -1,9 +1,10 @@
1
- import { isValidElement, forwardRef, ComponentRef } from 'react';
1
+ import { isValidElement, forwardRef } from 'react';
2
2
  import { View, Text, TouchableOpacity } from 'react-native';
3
3
  import MaterialDesignIcons from '@react-native-vector-icons/material-design-icons';
4
- import { alertStyles } from './Alert.styles';
4
+ import { alertStyles, alertSizeConfig } from './Alert.styles';
5
5
  import { isIconName } from '../Icon/icon-resolver';
6
6
  import type { AlertProps } from './types';
7
+ import type { IdealystElement } from '../utils/refTypes';
7
8
 
8
9
  // Default icon names for each intent
9
10
  const defaultIcons: Record<string, string> = {
@@ -15,12 +16,13 @@ const defaultIcons: Record<string, string> = {
15
16
  neutral: 'circle',
16
17
  };
17
18
 
18
- const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
19
+ const Alert = forwardRef<IdealystElement, AlertProps>(({
19
20
  title,
20
21
  message,
21
22
  children,
22
23
  intent = 'neutral',
23
24
  type = 'soft',
25
+ size = 'md',
24
26
  icon,
25
27
  showIcon = true,
26
28
  dismissible = false,
@@ -30,17 +32,25 @@ const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
30
32
  testID,
31
33
  id,
32
34
  }, ref) => {
35
+ // Apply variants for size
36
+ alertStyles.useVariants({ size });
37
+
33
38
  // Call all styles as functions to get theme-reactive styles
34
- const dynamicProps = { intent, type };
39
+ const dynamicProps = { intent, type, size };
35
40
  const containerStyle = (alertStyles.container as any)(dynamicProps);
36
41
  const iconContainerStyle = (alertStyles.iconContainer as any)(dynamicProps);
37
42
  const titleStyle = (alertStyles.title as any)(dynamicProps);
38
43
  const messageStyle = (alertStyles.message as any)(dynamicProps);
39
- const contentStyle = (alertStyles.content as any)({});
40
- const actionsStyle = (alertStyles.actions as any)({});
41
- const closeButtonStyle = (alertStyles.closeButton as any)({});
44
+ const contentStyle = (alertStyles.content as any)(dynamicProps);
45
+ const actionsStyle = (alertStyles.actions as any)(dynamicProps);
46
+ const closeButtonStyle = (alertStyles.closeButton as any)(dynamicProps);
42
47
  const closeIconStyle = (alertStyles.closeIcon as any)(dynamicProps);
43
48
 
49
+ // Get size-specific icon dimensions
50
+ const sizeConfig = alertSizeConfig[size];
51
+ const iconSize = sizeConfig.iconSize;
52
+ const closeIconSize = sizeConfig.closeIconSize;
53
+
44
54
  const displayIcon = icon !== undefined ? icon : (showIcon ? defaultIcons[intent] : null);
45
55
 
46
56
  // Helper to render icon
@@ -51,7 +61,7 @@ const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
51
61
  return (
52
62
  <MaterialDesignIcons
53
63
  name={displayIcon}
54
- size={20}
64
+ size={iconSize}
55
65
  style={iconContainerStyle}
56
66
  />
57
67
  );
@@ -63,7 +73,7 @@ const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
63
73
 
64
74
  return (
65
75
  <View
66
- ref={ref}
76
+ ref={ref as any}
67
77
  nativeID={id}
68
78
  style={[containerStyle, style]}
69
79
  testID={testID}
@@ -112,7 +122,7 @@ const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
112
122
  >
113
123
  <MaterialDesignIcons
114
124
  name="close"
115
- size={16}
125
+ size={closeIconSize}
116
126
  style={closeIconStyle}
117
127
  />
118
128
  </TouchableOpacity>
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Alert styles using defineStyle with dynamic intent/type handling.
2
+ * Alert styles using defineStyle with $iterator expansion for size variants.
3
3
  */
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
5
  import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
- import type { Theme as BaseTheme, Intent } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme, Intent, Size } from '@idealyst/theme';
7
7
 
8
8
  // Required: Unistyles must see StyleSheet usage in original source to process this file
9
9
  void StyleSheet;
@@ -16,98 +16,136 @@ type AlertType = 'filled' | 'outlined' | 'soft';
16
16
  export type AlertDynamicProps = {
17
17
  intent?: Intent;
18
18
  type?: AlertType;
19
+ size?: Size;
19
20
  };
20
21
 
21
22
  /**
22
- * Alert styles with intent/type combination handling.
23
+ * Alert styles with $iterator expansion for size variants.
24
+ *
25
+ * Intent/type combinations use dynamic functions with inlined theme accesses
26
+ * so Unistyles can trace all possible theme paths.
23
27
  */
24
28
  export const alertStyles = defineStyle('Alert', (theme: Theme) => ({
25
- container: ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
26
- const intentValue = theme.intents[intent];
27
-
28
- // Background color based on type
29
- const backgroundColor = type === 'filled'
30
- ? intentValue.primary
31
- : type === 'soft'
32
- ? intentValue.light
33
- : 'transparent';
34
-
35
- // Border color based on type
36
- const borderColor = (type === 'filled' || type === 'outlined')
37
- ? intentValue.primary
38
- : intentValue.light;
39
-
40
- return {
41
- display: 'flex' as const,
42
- flexDirection: 'row' as const,
43
- alignItems: 'flex-start' as const,
44
- gap: 8,
45
- padding: 16,
46
- borderRadius: 8,
47
- borderWidth: 1,
48
- borderStyle: 'solid' as const,
49
- backgroundColor,
50
- borderColor,
51
- } as const;
52
- },
53
-
54
- iconContainer: ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
55
- const intentValue = theme.intents[intent];
56
- const color = type === 'filled' ? intentValue.contrast : intentValue.primary;
57
-
58
- return {
59
- display: 'flex' as const,
60
- alignItems: 'center' as const,
61
- justifyContent: 'center' as const,
62
- alignSelf: 'flex-start' as const,
63
- flexShrink: 0,
64
- width: 24,
65
- height: 24,
66
- marginTop: 2,
67
- color,
68
- } as const;
69
- },
70
-
71
- title: ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
72
- const intentValue = theme.intents[intent];
73
- const color = type === 'filled' ? intentValue.contrast : intentValue.primary;
29
+ container: (_props: AlertDynamicProps) => ({
30
+ display: 'flex' as const,
31
+ flexDirection: 'row' as const,
32
+ alignItems: 'flex-start' as const,
33
+ borderWidth: 1,
34
+ borderStyle: 'solid' as const,
35
+ variants: {
36
+ type: {
37
+ filled: {
38
+ backgroundColor: theme.$intents.primary,
39
+ borderColor: theme.$intents.primary,
40
+ },
41
+ outlined: {
42
+ backgroundColor: 'transparent',
43
+ borderColor: theme.$intents.primary,
44
+ },
45
+ soft: {
46
+ backgroundColor: theme.$intents.light,
47
+ borderColor: theme.$intents.light,
48
+ },
49
+ },
50
+ size: {
51
+ gap: theme.sizes.$alert.gap,
52
+ padding: theme.sizes.$alert.padding,
53
+ borderRadius: theme.sizes.$alert.borderRadius,
54
+ },
55
+ },
56
+ }),
74
57
 
75
- return {
76
- fontSize: 16,
77
- lineHeight: 24,
78
- fontWeight: '600' as const,
79
- color,
80
- } as const;
81
- },
58
+ iconContainer: (_props: AlertDynamicProps) => ({
59
+ display: 'flex' as const,
60
+ alignItems: 'center' as const,
61
+ justifyContent: 'center' as const,
62
+ alignSelf: 'flex-start' as const,
63
+ flexShrink: 0,
64
+ marginTop: 2,
65
+ variants: {
66
+ type: {
67
+ filled: {
68
+ color: theme.$intents.contrast,
69
+ },
70
+ outlined: {
71
+ color: theme.$intents.primary,
72
+ },
73
+ soft: {
74
+ color: theme.$intents.primary,
75
+ },
76
+ },
77
+ size: {
78
+ width: theme.sizes.$alert.iconSize,
79
+ height: theme.sizes.$alert.iconSize,
80
+ },
81
+ },
82
+ }),
82
83
 
83
- message: ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
84
- const intentValue = theme.intents[intent];
85
- const color = type === 'filled' ? intentValue.contrast : theme.colors.text.primary;
84
+ title: (_props: AlertDynamicProps) => ({
85
+ fontWeight: '600' as const,
86
+ variants: {
87
+ type: {
88
+ filled: {
89
+ color: theme.$intents.contrast,
90
+ },
91
+ outlined: {
92
+ color: theme.$intents.primary,
93
+ },
94
+ soft: {
95
+ color: theme.$intents.primary,
96
+ },
97
+ },
98
+ size: {
99
+ fontSize: theme.sizes.$alert.titleFontSize,
100
+ lineHeight: theme.sizes.$alert.titleLineHeight,
101
+ },
102
+ },
103
+ }),
86
104
 
87
- return {
88
- fontSize: 14,
89
- lineHeight: 20,
90
- color,
91
- } as const;
92
- },
105
+ message: (_props: AlertDynamicProps) => ({
106
+ variants: {
107
+ type: {
108
+ filled: {
109
+ color: theme.$intents.contrast,
110
+ },
111
+ outlined: {
112
+ color: theme.colors.text.primary,
113
+ },
114
+ soft: {
115
+ color: theme.colors.text.primary,
116
+ },
117
+ },
118
+ size: {
119
+ fontSize: theme.sizes.$alert.messageFontSize,
120
+ lineHeight: theme.sizes.$alert.messageLineHeight,
121
+ },
122
+ },
123
+ }),
93
124
 
94
125
  content: (_props: AlertDynamicProps) => ({
95
126
  flex: 1,
96
127
  display: 'flex' as const,
97
128
  flexDirection: 'column' as const,
98
- gap: 4,
129
+ variants: {
130
+ size: {
131
+ // Gap is half of the main gap
132
+ gap: theme.sizes.$alert.gap,
133
+ },
134
+ },
99
135
  }),
100
136
 
101
137
  actions: (_props: AlertDynamicProps) => ({
102
- marginTop: 4,
103
138
  display: 'flex' as const,
104
139
  flexDirection: 'row' as const,
105
- gap: 8,
140
+ variants: {
141
+ size: {
142
+ marginTop: theme.sizes.$alert.gap,
143
+ gap: theme.sizes.$alert.gap,
144
+ },
145
+ },
106
146
  }),
107
147
 
108
148
  closeButton: (_props: AlertDynamicProps) => ({
109
- padding: 4,
110
- borderRadius: 4,
111
149
  display: 'flex' as const,
112
150
  alignItems: 'center' as const,
113
151
  justifyContent: 'center' as const,
@@ -126,19 +164,43 @@ export const alertStyles = defineStyle('Alert', (theme: Theme) => ({
126
164
  backgroundColor: 'rgba(0, 0, 0, 0.1)',
127
165
  },
128
166
  },
167
+ variants: {
168
+ size: {
169
+ padding: theme.sizes.$alert.padding,
170
+ borderRadius: theme.sizes.$alert.borderRadius,
171
+ },
172
+ },
129
173
  }),
130
174
 
131
- closeIcon: ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
132
- const intentValue = theme.intents[intent];
133
- const color = type === 'filled' ? intentValue.contrast : intentValue.primary;
134
-
135
- return {
136
- display: 'flex' as const,
137
- alignItems: 'center' as const,
138
- justifyContent: 'center' as const,
139
- width: 16,
140
- height: 16,
141
- color,
142
- } as const;
143
- },
175
+ closeIcon: (_props: AlertDynamicProps) => ({
176
+ display: 'flex' as const,
177
+ alignItems: 'center' as const,
178
+ justifyContent: 'center' as const,
179
+ variants: {
180
+ type: {
181
+ filled: {
182
+ color: theme.$intents.contrast,
183
+ },
184
+ outlined: {
185
+ color: theme.$intents.primary,
186
+ },
187
+ soft: {
188
+ color: theme.$intents.primary,
189
+ },
190
+ },
191
+ size: {
192
+ width: theme.sizes.$alert.closeIconSize,
193
+ height: theme.sizes.$alert.closeIconSize,
194
+ },
195
+ },
196
+ }),
144
197
  }));
198
+
199
+ // Export theme sizes for use in components (for icon sizing in native)
200
+ export const alertSizeConfig = {
201
+ xs: { padding: 8, gap: 6, borderRadius: 4, titleFontSize: 12, titleLineHeight: 16, messageFontSize: 11, messageLineHeight: 14, iconSize: 16, closeIconSize: 12 },
202
+ sm: { padding: 12, gap: 8, borderRadius: 6, titleFontSize: 14, titleLineHeight: 20, messageFontSize: 12, messageLineHeight: 16, iconSize: 20, closeIconSize: 14 },
203
+ md: { padding: 16, gap: 10, borderRadius: 8, titleFontSize: 16, titleLineHeight: 24, messageFontSize: 14, messageLineHeight: 20, iconSize: 24, closeIconSize: 16 },
204
+ lg: { padding: 20, gap: 12, borderRadius: 10, titleFontSize: 18, titleLineHeight: 28, messageFontSize: 16, messageLineHeight: 24, iconSize: 28, closeIconSize: 18 },
205
+ xl: { padding: 24, gap: 14, borderRadius: 12, titleFontSize: 20, titleLineHeight: 32, messageFontSize: 18, messageLineHeight: 28, iconSize: 32, closeIconSize: 20 },
206
+ };
@@ -5,6 +5,7 @@ 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
11
  const defaultIcons: Record<string, string> = {
@@ -20,12 +21,13 @@ const defaultIcons: Record<string, string> = {
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<HTMLDivElement, AlertProps>(({
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,14 +37,17 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(({
35
37
  testID,
36
38
  id,
37
39
  }, ref) => {
38
- // Compute dynamic styles with intent and type
39
- const dynamicProps = { intent, type };
40
+ // Apply variants for size
41
+ alertStyles.useVariants({ size });
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
 
@@ -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 AlertType= 'filled' | 'outlined' | 'soft';
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
  }