@idealyst/components 1.2.30 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/components",
3
- "version": "1.2.30",
3
+ "version": "1.2.31",
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.30",
59
+ "@idealyst/theme": "^1.2.31",
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,8 +106,8 @@
106
106
  }
107
107
  },
108
108
  "devDependencies": {
109
- "@idealyst/theme": "^1.2.30",
110
- "@idealyst/tooling": "^1.2.4",
109
+ "@idealyst/theme": "^1.2.31",
110
+ "@idealyst/tooling": "^1.2.30",
111
111
  "@mdi/react": "^1.6.1",
112
112
  "@types/react": "^19.1.0",
113
113
  "react": "^19.1.0",
package/plugin/web.js CHANGED
@@ -248,6 +248,8 @@ module.exports = function ({ types: t }, options = {}) {
248
248
  'Alert': ['icon'],
249
249
  'Chip': ['icon', 'deleteIcon'],
250
250
  'Input': ['leftIcon', 'rightIcon'],
251
+ 'TextInput': ['leftIcon', 'rightIcon'],
252
+ 'IconButton': ['icon'],
251
253
  };
252
254
 
253
255
  const iconProps = iconPropMap[componentName];
@@ -37,6 +37,7 @@ const ActivityIndicator = forwardRef<IdealystElement, ActivityIndicatorProps>(({
37
37
  activityIndicatorStyles.useVariants({
38
38
  size: sizeVariant,
39
39
  animating,
40
+ intent,
40
41
  });
41
42
 
42
43
  // Call dynamic style with intent variant
@@ -22,10 +22,10 @@ export type ActivityIndicatorDynamicProps = {
22
22
  */
23
23
  export const activityIndicatorStyles = defineStyle('ActivityIndicator', (theme: Theme) => ({
24
24
  container: (_props: ActivityIndicatorDynamicProps) => ({
25
+ display: 'flex' as const,
25
26
  alignItems: 'center' as const,
26
27
  justifyContent: 'center' as const,
27
28
  variants: {
28
- // $iterator expands for each activityIndicator size
29
29
  size: {
30
30
  width: theme.sizes.$activityIndicator.size,
31
31
  height: theme.sizes.$activityIndicator.size,
@@ -46,10 +46,6 @@ export const activityIndicatorStyles = defineStyle('ActivityIndicator', (theme:
46
46
  height: theme.sizes.$activityIndicator.size,
47
47
  borderWidth: theme.sizes.$activityIndicator.borderWidth,
48
48
  },
49
- animating: {
50
- true: {},
51
- false: {},
52
- },
53
49
  intent: {
54
50
  color: theme.$intents.primary,
55
51
  _web: {
@@ -42,8 +42,9 @@ const ActivityIndicator = forwardRef<IdealystElement, ActivityIndicatorProps>(({
42
42
 
43
43
  // Apply variants using the correct Unistyles 3.0 pattern
44
44
  activityIndicatorStyles.useVariants({
45
- size: sizeVariant,
45
+ size: size,
46
46
  animating,
47
+ intent,
47
48
  });
48
49
 
49
50
  // Don't render if not animating and hidesWhenStopped is true
@@ -51,35 +52,21 @@ const ActivityIndicator = forwardRef<IdealystElement, ActivityIndicatorProps>(({
51
52
  return null;
52
53
  }
53
54
 
54
- // Create the style array following the official documentation pattern
55
- const containerStyleArray = [
56
- (activityIndicatorStyles.container as any)({}),
57
- customSize && {
58
- width: customSize,
59
- height: customSize,
60
- },
61
- style,
62
- ];
55
+ // Dynamic props for style functions
56
+ const dynamicProps = { size: sizeVariant, animating, intent };
63
57
 
64
- const spinnerStyleArray = [
65
- (activityIndicatorStyles.spinner as any)({
66
- intent,
67
- }),
68
- customSize ? {
69
- width: customSize,
70
- height: customSize,
71
- borderWidth: Math.max(2, customSize / 10),
72
- } : {},
73
- color ? { borderTopColor: color, borderRightColor: color } : {},
74
- // Add inline CSS animation
75
- {
76
- animation: animating ? 'spin 1s linear infinite' : undefined,
77
- },
78
- ];
58
+ // Use getWebProps - same pattern as Alert
59
+ const containerProps = getWebProps([
60
+ (activityIndicatorStyles.container as any)(dynamicProps),
61
+ customSize && { width: customSize, height: customSize },
62
+ style,
63
+ ]);
79
64
 
80
- // Use getWebProps to generate className and ref for web
81
- const containerProps = getWebProps(containerStyleArray);
82
- const spinnerProps = getWebProps(spinnerStyleArray);
65
+ const spinnerProps = getWebProps([
66
+ (activityIndicatorStyles.spinner as any)(dynamicProps),
67
+ customSize && { width: customSize, height: customSize, borderWidth: Math.max(2, customSize / 10) },
68
+ color && { borderTopColor: color, borderRightColor: color },
69
+ ]);
83
70
 
84
71
  const mergedRef = useMergeRefs(ref, containerProps.ref);
85
72
 
@@ -32,8 +32,8 @@ const Alert = forwardRef<IdealystElement, AlertProps>(({
32
32
  testID,
33
33
  id,
34
34
  }, ref) => {
35
- // Apply variants for size
36
- alertStyles.useVariants({ size });
35
+ // Apply variants for size, intent, and type
36
+ alertStyles.useVariants({ size, intent, type });
37
37
 
38
38
  // Call all styles as functions to get theme-reactive styles
39
39
  const dynamicProps = { intent, type, size };
@@ -1,5 +1,8 @@
1
1
  /**
2
- * Alert styles using defineStyle with $iterator expansion for size variants.
2
+ * Alert styles using defineStyle with variant expansion.
3
+ *
4
+ * Alert has compound logic between type+intent that's handled via compoundVariants.
5
+ * The $intents iterator in compoundVariants expands for all intent values.
3
6
  */
4
7
  import { StyleSheet } from 'react-native-unistyles';
5
8
  import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
@@ -20,10 +23,10 @@ export type AlertDynamicProps = {
20
23
  };
21
24
 
22
25
  /**
23
- * Alert styles with $iterator expansion for size variants.
26
+ * Alert styles with variant expansion for size/intent/type.
24
27
  *
25
- * Intent/type combinations use dynamic functions with inlined theme accesses
26
- * so Unistyles can trace all possible theme paths.
28
+ * The intent variant is expanded via $intents iterator.
29
+ * CompoundVariants handle type+intent combinations for colors.
27
30
  */
28
31
  export const alertStyles = defineStyle('Alert', (theme: Theme) => ({
29
32
  container: (_props: AlertDynamicProps) => ({
@@ -33,26 +36,33 @@ export const alertStyles = defineStyle('Alert', (theme: Theme) => ({
33
36
  borderWidth: 1,
34
37
  borderStyle: 'solid' as const,
35
38
  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
39
  size: {
51
40
  gap: theme.sizes.$alert.gap,
52
41
  padding: theme.sizes.$alert.padding,
53
42
  borderRadius: theme.sizes.$alert.borderRadius,
54
43
  },
44
+ // Intent variant - expands to primary, success, danger, etc.
45
+ intent: {
46
+ // Base styles per intent (overridden by type+intent compoundVariants)
47
+ backgroundColor: theme.$intents.primary,
48
+ borderColor: theme.$intents.primary,
49
+ },
50
+ type: {
51
+ filled: {},
52
+ outlined: {
53
+ backgroundColor: 'transparent',
54
+ },
55
+ soft: {},
56
+ },
55
57
  },
58
+ compoundVariants: [
59
+ // filled: use intent primary for bg and border
60
+ { type: 'filled', styles: { backgroundColor: theme.$intents.primary, borderColor: theme.$intents.primary } },
61
+ // outlined: transparent bg, intent primary for border
62
+ { type: 'outlined', styles: { backgroundColor: 'transparent', borderColor: theme.$intents.primary } },
63
+ // soft: intent light for bg and border
64
+ { type: 'soft', styles: { backgroundColor: theme.$intents.light, borderColor: theme.$intents.light } },
65
+ ],
56
66
  }),
57
67
 
58
68
  iconContainer: (_props: AlertDynamicProps) => ({
@@ -63,63 +73,75 @@ export const alertStyles = defineStyle('Alert', (theme: Theme) => ({
63
73
  flexShrink: 0,
64
74
  marginTop: 2,
65
75
  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
76
  size: {
78
77
  width: theme.sizes.$alert.iconSize,
79
78
  height: theme.sizes.$alert.iconSize,
80
79
  },
80
+ intent: {
81
+ color: theme.$intents.primary,
82
+ },
83
+ type: {
84
+ filled: {},
85
+ outlined: {},
86
+ soft: {},
87
+ },
81
88
  },
89
+ compoundVariants: [
90
+ // filled: contrast color for icon
91
+ { type: 'filled', styles: { color: theme.$intents.contrast } },
92
+ // outlined/soft: primary color for icon
93
+ { type: 'outlined', styles: { color: theme.$intents.primary } },
94
+ { type: 'soft', styles: { color: theme.$intents.primary } },
95
+ ],
82
96
  }),
83
97
 
84
98
  title: (_props: AlertDynamicProps) => ({
85
99
  fontWeight: '600' as const,
86
100
  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
101
  size: {
99
102
  fontSize: theme.sizes.$alert.titleFontSize,
100
103
  lineHeight: theme.sizes.$alert.titleLineHeight,
101
104
  },
105
+ intent: {
106
+ color: theme.$intents.primary,
107
+ },
108
+ type: {
109
+ filled: {},
110
+ outlined: {},
111
+ soft: {},
112
+ },
102
113
  },
114
+ compoundVariants: [
115
+ // filled: contrast color for title
116
+ { type: 'filled', styles: { color: theme.$intents.contrast } },
117
+ // outlined/soft: primary color for title
118
+ { type: 'outlined', styles: { color: theme.$intents.primary } },
119
+ { type: 'soft', styles: { color: theme.$intents.primary } },
120
+ ],
103
121
  }),
104
122
 
105
123
  message: (_props: AlertDynamicProps) => ({
106
124
  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
125
  size: {
119
126
  fontSize: theme.sizes.$alert.messageFontSize,
120
127
  lineHeight: theme.sizes.$alert.messageLineHeight,
121
128
  },
129
+ intent: {
130
+ color: theme.colors.text.primary,
131
+ },
132
+ type: {
133
+ filled: {},
134
+ outlined: {},
135
+ soft: {},
136
+ },
122
137
  },
138
+ compoundVariants: [
139
+ // filled: contrast color for message
140
+ { type: 'filled', styles: { color: theme.$intents.contrast } },
141
+ // outlined/soft: use default text color (set in intent variant)
142
+ { type: 'outlined', styles: { color: theme.colors.text.primary } },
143
+ { type: 'soft', styles: { color: theme.colors.text.primary } },
144
+ ],
123
145
  }),
124
146
 
125
147
  content: (_props: AlertDynamicProps) => ({
@@ -128,7 +150,6 @@ export const alertStyles = defineStyle('Alert', (theme: Theme) => ({
128
150
  flexDirection: 'column' as const,
129
151
  variants: {
130
152
  size: {
131
- // Gap is half of the main gap
132
153
  gap: theme.sizes.$alert.gap,
133
154
  },
134
155
  },
@@ -177,22 +198,26 @@ export const alertStyles = defineStyle('Alert', (theme: Theme) => ({
177
198
  alignItems: 'center' as const,
178
199
  justifyContent: 'center' as const,
179
200
  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
201
  size: {
192
202
  width: theme.sizes.$alert.closeIconSize,
193
203
  height: theme.sizes.$alert.closeIconSize,
194
204
  },
205
+ intent: {
206
+ color: theme.$intents.primary,
207
+ },
208
+ type: {
209
+ filled: {},
210
+ outlined: {},
211
+ soft: {},
212
+ },
195
213
  },
214
+ compoundVariants: [
215
+ // filled: contrast color for close icon
216
+ { type: 'filled', styles: { color: theme.$intents.contrast } },
217
+ // outlined/soft: primary color for close icon
218
+ { type: 'outlined', styles: { color: theme.$intents.primary } },
219
+ { type: 'soft', styles: { color: theme.$intents.primary } },
220
+ ],
196
221
  }),
197
222
  }));
198
223
 
@@ -1,4 +1,4 @@
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';
@@ -8,13 +8,13 @@ import useMergeRefs from '../hooks/useMergeRefs';
8
8
  import type { IdealystElement } from '../utils/refTypes';
9
9
 
10
10
  // Default icons for each intent
11
- const defaultIcons: Record<string, string> = {
12
- primary: 'information',
13
- success: 'check-circle',
14
- error: 'alert-circle',
15
- warning: 'alert',
16
- info: 'information',
17
- neutral: 'record-circle',
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" />,
18
18
  };
19
19
 
20
20
  /**
@@ -37,8 +37,8 @@ const Alert = forwardRef<IdealystElement, AlertProps>(({
37
37
  testID,
38
38
  id,
39
39
  }, ref) => {
40
- // Apply variants for size
41
- alertStyles.useVariants({ size });
40
+ // Apply variants for size, intent, and type
41
+ alertStyles.useVariants({ size, intent, type });
42
42
 
43
43
  // Compute dynamic styles with intent, type, and size
44
44
  const dynamicProps = { intent, type, size };
@@ -51,26 +51,25 @@ const Alert = forwardRef<IdealystElement, AlertProps>(({
51
51
  const closeButtonProps = getWebProps([(alertStyles.closeButton as any)(dynamicProps)]);
52
52
  const closeIconProps = getWebProps([(alertStyles.closeIcon as any)(dynamicProps)]);
53
53
 
54
- const displayIcon = icon !== undefined ? icon : (showIcon ? defaultIcons[intent] : null);
55
-
56
- // Helper to render icon
57
- const renderIcon = (iconProp: typeof displayIcon) => {
58
- if (!iconProp) return null;
59
-
60
- if (isIconName(iconProp)) {
61
- return (
62
- <IconSvg
63
- name={iconProp}
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}
64
65
  {...iconContainerProps}
65
- aria-label={iconProp}
66
+ aria-label={icon}
66
67
  />
67
- );
68
- } else if (isValidElement(iconProp)) {
69
- return iconProp;
68
+ } else if (isValidElement(icon)) {
69
+ return icon;
70
70
  }
71
-
72
71
  return null;
73
- };
72
+ }, [icon, showIcon, intent]);
74
73
 
75
74
  const mergedRef = useMergeRefs(ref, containerProps.ref);
76
75
 
@@ -82,7 +81,7 @@ const Alert = forwardRef<IdealystElement, AlertProps>(({
82
81
  data-testid={testID}
83
82
  role="alert"
84
83
  >
85
- {displayIcon && renderIcon(displayIcon)}
84
+ {Icon}
86
85
 
87
86
  <div {...contentProps}>
88
87
  {title && (