@idealyst/components 1.1.5 → 1.1.7

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 (105) hide show
  1. package/package.json +8 -3
  2. package/src/Accordion/Accordion.native.tsx +15 -9
  3. package/src/Accordion/Accordion.styles.tsx +193 -168
  4. package/src/Accordion/Accordion.web.tsx +12 -7
  5. package/src/ActivityIndicator/ActivityIndicator.native.tsx +3 -2
  6. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +22 -11
  7. package/src/ActivityIndicator/ActivityIndicator.web.tsx +2 -2
  8. package/src/Alert/Alert.native.tsx +11 -10
  9. package/src/Alert/Alert.styles.tsx +162 -253
  10. package/src/Alert/Alert.web.tsx +6 -10
  11. package/src/Avatar/Avatar.native.tsx +5 -2
  12. package/src/Avatar/Avatar.styles.tsx +48 -18
  13. package/src/Avatar/Avatar.web.tsx +2 -2
  14. package/src/Badge/Badge.native.tsx +2 -2
  15. package/src/Badge/Badge.styles.tsx +37 -16
  16. package/src/Badge/Badge.web.tsx +6 -6
  17. package/src/Breadcrumb/Breadcrumb.native.tsx +12 -5
  18. package/src/Breadcrumb/Breadcrumb.styles.tsx +59 -58
  19. package/src/Breadcrumb/Breadcrumb.web.tsx +13 -6
  20. package/src/Button/Button.native.tsx +39 -14
  21. package/src/Button/Button.styles.tsx +106 -208
  22. package/src/Button/Button.web.tsx +10 -8
  23. package/src/Card/Card.native.tsx +14 -6
  24. package/src/Card/Card.styles.tsx +64 -62
  25. package/src/Card/Card.web.tsx +5 -4
  26. package/src/Checkbox/Checkbox.native.tsx +7 -3
  27. package/src/Checkbox/Checkbox.styles.tsx +49 -25
  28. package/src/Checkbox/Checkbox.web.tsx +3 -3
  29. package/src/Chip/Chip.native.tsx +5 -5
  30. package/src/Chip/Chip.styles.tsx +71 -21
  31. package/src/Chip/Chip.web.tsx +5 -5
  32. package/src/Dialog/Dialog.native.tsx +10 -4
  33. package/src/Dialog/Dialog.styles.tsx +130 -90
  34. package/src/Dialog/Dialog.web.tsx +4 -4
  35. package/src/Divider/Divider.native.tsx +29 -42
  36. package/src/Divider/Divider.styles.tsx +138 -242
  37. package/src/Divider/Divider.web.tsx +17 -14
  38. package/src/Icon/Icon.native.tsx +11 -3
  39. package/src/Icon/Icon.styles.tsx +10 -4
  40. package/src/Image/Image.styles.tsx +53 -37
  41. package/src/Input/Input.native.tsx +6 -7
  42. package/src/Input/Input.styles.tsx +194 -174
  43. package/src/Input/Input.web.tsx +5 -8
  44. package/src/Link/Link.native.tsx +4 -1
  45. package/src/List/List.styles.tsx +79 -105
  46. package/src/List/ListItem.native.tsx +5 -3
  47. package/src/List/ListItem.web.tsx +4 -3
  48. package/src/Menu/Menu.native.tsx +1 -1
  49. package/src/Menu/Menu.styles.tsx +53 -37
  50. package/src/Menu/Menu.web.tsx +2 -2
  51. package/src/Menu/MenuItem.native.tsx +5 -3
  52. package/src/Menu/MenuItem.styles.tsx +68 -69
  53. package/src/Menu/MenuItem.web.tsx +16 -3
  54. package/src/Popover/Popover.native.tsx +1 -1
  55. package/src/Popover/Popover.styles.tsx +40 -29
  56. package/src/Popover/Popover.web.tsx +1 -1
  57. package/src/Pressable/Pressable.native.tsx +3 -1
  58. package/src/Pressable/Pressable.styles.tsx +20 -13
  59. package/src/Pressable/Pressable.web.tsx +1 -1
  60. package/src/Progress/Progress.native.tsx +15 -6
  61. package/src/Progress/Progress.styles.tsx +125 -85
  62. package/src/Progress/Progress.web.tsx +10 -9
  63. package/src/RadioButton/RadioButton.native.tsx +8 -3
  64. package/src/RadioButton/RadioButton.styles.tsx +44 -37
  65. package/src/RadioButton/RadioButton.web.tsx +3 -3
  66. package/src/SVGImage/SVGImage.styles.tsx +28 -16
  67. package/src/Screen/Screen.native.tsx +23 -13
  68. package/src/Screen/Screen.styles.tsx +57 -46
  69. package/src/Screen/Screen.web.tsx +1 -1
  70. package/src/Select/Select.native.tsx +11 -5
  71. package/src/Select/Select.styles.tsx +72 -52
  72. package/src/Select/Select.web.tsx +5 -5
  73. package/src/Skeleton/Skeleton.styles.tsx +26 -14
  74. package/src/Slider/Slider.native.tsx +9 -5
  75. package/src/Slider/Slider.styles.tsx +59 -48
  76. package/src/Slider/Slider.web.tsx +5 -5
  77. package/src/Switch/Switch.native.tsx +6 -2
  78. package/src/Switch/Switch.styles.tsx +46 -19
  79. package/src/Switch/Switch.web.tsx +4 -4
  80. package/src/TabBar/TabBar.native.tsx +23 -31
  81. package/src/TabBar/TabBar.styles.tsx +215 -371
  82. package/src/TabBar/TabBar.web.tsx +21 -33
  83. package/src/Table/Table.native.tsx +1 -1
  84. package/src/Table/Table.styles.tsx +11 -4
  85. package/src/Table/Table.web.tsx +1 -1
  86. package/src/Text/Text.native.tsx +3 -4
  87. package/src/Text/Text.styles.tsx +7 -1
  88. package/src/Text/Text.web.tsx +1 -1
  89. package/src/TextArea/TextArea.styles.tsx +90 -58
  90. package/src/Tooltip/Tooltip.native.tsx +2 -2
  91. package/src/Tooltip/Tooltip.styles.tsx +21 -12
  92. package/src/Tooltip/Tooltip.web.tsx +2 -2
  93. package/src/Video/Video.styles.tsx +39 -23
  94. package/src/View/View.native.tsx +4 -2
  95. package/src/View/View.styles.tsx +33 -22
  96. package/src/View/View.web.tsx +13 -2
  97. package/src/extensions/applyExtension.ts +210 -0
  98. package/src/extensions/extendComponent.ts +377 -0
  99. package/src/extensions/index.ts +102 -0
  100. package/src/extensions/types.ts +497 -0
  101. package/src/globals.ts +16 -0
  102. package/src/index.native.ts +4 -0
  103. package/src/index.ts +28 -0
  104. package/src/utils/accessibility/index.ts +3 -3
  105. package/src/utils/deepMerge.ts +54 -2
@@ -48,7 +48,7 @@ const ActivityIndicator = forwardRef<HTMLDivElement, ActivityIndicatorProps>(({
48
48
 
49
49
  // Create the style array following the official documentation pattern
50
50
  const containerStyleArray = [
51
- activityIndicatorStyles.container,
51
+ (activityIndicatorStyles.container as any)({}),
52
52
  customSize && {
53
53
  width: customSize,
54
54
  height: customSize,
@@ -57,7 +57,7 @@ const ActivityIndicator = forwardRef<HTMLDivElement, ActivityIndicatorProps>(({
57
57
  ];
58
58
 
59
59
  const spinnerStyleArray = [
60
- activityIndicatorStyles.spinner({
60
+ (activityIndicatorStyles.spinner as any)({
61
61
  intent,
62
62
  }),
63
63
  customSize ? {
@@ -30,11 +30,12 @@ const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
30
30
  testID,
31
31
  id,
32
32
  }, ref) => {
33
- // Apply variants to stylesheet
34
- alertStyles.useVariants({
35
- type,
36
- intent,
37
- });
33
+ // Compute dynamic styles with intent and type
34
+ const dynamicProps = { intent, type };
35
+ const containerStyle = (alertStyles.container as any)(dynamicProps);
36
+ const iconContainerStyle = (alertStyles.iconContainer as any)(dynamicProps);
37
+ const titleStyle = (alertStyles.title as any)(dynamicProps);
38
+ const messageStyle = (alertStyles.message as any)(dynamicProps);
38
39
 
39
40
  const displayIcon = icon !== undefined ? icon : (showIcon ? defaultIcons[intent] : null);
40
41
 
@@ -47,7 +48,7 @@ const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
47
48
  <MaterialCommunityIcons
48
49
  name={displayIcon}
49
50
  size={20}
50
- style={alertStyles.iconContainer}
51
+ style={iconContainerStyle}
51
52
  />
52
53
  );
53
54
  } else if (isValidElement(displayIcon)) {
@@ -60,25 +61,25 @@ const Alert = forwardRef<ComponentRef<typeof View>, AlertProps>(({
60
61
  <View
61
62
  ref={ref}
62
63
  nativeID={id}
63
- style={[alertStyles.container, style]}
64
+ style={[containerStyle, style]}
64
65
  testID={testID}
65
66
  accessibilityRole="alert"
66
67
  >
67
68
  {displayIcon && (
68
- <View style={alertStyles.iconContainer}>
69
+ <View style={iconContainerStyle}>
69
70
  {renderIcon()}
70
71
  </View>
71
72
  )}
72
73
 
73
74
  <View style={alertStyles.content}>
74
75
  {title && (
75
- <Text style={alertStyles.title}>
76
+ <Text style={titleStyle}>
76
77
  {title}
77
78
  </Text>
78
79
  )}
79
80
 
80
81
  {message && (
81
- <Text style={alertStyles.message}>
82
+ <Text style={messageStyle}>
82
83
  {message}
83
84
  </Text>
84
85
  )}
@@ -1,236 +1,80 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, Intent, CompoundVariants} from '@idealyst/theme';
2
+ import { Theme, Intent } from '@idealyst/theme';
3
+ import { applyExtensions } from '../extensions/applyExtension';
3
4
 
4
5
  type AlertType = 'filled' | 'outlined' | 'soft';
5
- type AlertIntent = Intent | 'info'; // Alert includes 'info' which maps to primary
6
+ type AlertIntent = Intent;
6
7
 
7
8
  export type AlertVariants = {
8
9
  type: AlertType;
9
10
  intent: AlertIntent;
10
11
  }
11
12
 
13
+ type AlertDynamicProps = {
14
+ intent?: AlertIntent;
15
+ type?: AlertType;
16
+ };
12
17
 
13
18
  /**
14
- * Create type variants (structure only, colors handled by compound variants)
19
+ * Get the intent value, mapping 'info' to 'primary' for backwards compatibility
15
20
  */
16
- const TypeVariants = {
17
- filled: {
18
- borderWidth: 1,
19
- borderStyle: 'solid' as const,
20
- },
21
- outlined: {
22
- backgroundColor: 'transparent',
23
- borderWidth: 1,
24
- borderStyle: 'solid' as const,
25
- },
26
- soft: {
27
- borderWidth: 1,
28
- borderStyle: 'solid' as const,
29
- }
30
- } as const;
21
+ function getIntentValue(theme: Theme, intent: AlertIntent) {
22
+ return theme.intents[intent];
23
+ }
31
24
 
32
25
  /**
33
- * Create compound variants for container (type + intent combinations)
26
+ * Get container background color based on intent and type
34
27
  */
35
- function createContainerCompoundVariants(theme: Theme) {
36
- const compoundVariants: CompoundVariants<keyof AlertVariants> = [];
37
-
38
- // Process standard intents from theme
39
- for (const intent in theme.intents) {
40
- const intentValue = theme.intents[intent as Intent];
41
-
42
- // Filled + intent
43
- compoundVariants.push({
44
- intent,
45
- type: 'filled',
46
- styles: {
47
- backgroundColor: intentValue.primary,
48
- borderColor: intentValue.primary,
49
- },
50
- });
51
-
52
- // Outlined + intent
53
- compoundVariants.push({
54
- intent,
55
- type: 'outlined',
56
- styles: {
57
- borderColor: intentValue.primary,
58
- },
59
- });
60
-
61
- // Soft + intent
62
- compoundVariants.push({
63
- intent,
64
- type: 'soft',
65
- styles: {
66
- backgroundColor: intentValue.light,
67
- borderColor: intentValue.light,
68
- },
69
- });
28
+ function getContainerBackgroundColor(theme: Theme, intent: AlertIntent, type: AlertType): string {
29
+ const intentValue = getIntentValue(theme, intent);
30
+ if (type === 'filled') {
31
+ return intentValue.primary;
70
32
  }
71
-
72
- // Add 'info' intent (maps to primary)
73
- const primaryIntent = theme.intents.primary;
74
- compoundVariants.push({
75
- intent: 'info',
76
- type: 'filled',
77
- styles: {
78
- backgroundColor: primaryIntent.primary,
79
- borderColor: primaryIntent.primary,
80
- },
81
- });
82
- compoundVariants.push({
83
- intent: 'info',
84
- type: 'outlined',
85
- styles: {
86
- borderColor: primaryIntent.primary,
87
- },
88
- });
89
- compoundVariants.push({
90
- intent: 'info',
91
- type: 'soft',
92
- styles: {
93
- backgroundColor: primaryIntent.light,
94
- borderColor: primaryIntent.light,
95
- },
96
- });
97
-
98
- return compoundVariants;
33
+ if (type === 'soft') {
34
+ return intentValue.light;
35
+ }
36
+ return 'transparent'; // outlined
99
37
  }
100
38
 
101
39
  /**
102
- * Create compound variants for icon/title colors (type + intent combinations)
40
+ * Get container border color based on intent and type
103
41
  */
104
- function createIconTitleCompoundVariants(theme: Theme) {
105
- const compoundVariants: CompoundVariants<keyof AlertVariants> = [];
106
-
107
- // Process standard intents from theme
108
- for (const intent in theme.intents) {
109
- const intentValue = theme.intents[intent as Intent];
110
-
111
- // Filled type: use contrast color
112
- compoundVariants.push({
113
- intent,
114
- type: 'filled',
115
- styles: {
116
- color: intentValue.contrast,
117
- },
118
- });
119
-
120
- // Outlined type: use primary color
121
- compoundVariants.push({
122
- intent,
123
- type: 'outlined',
124
- styles: {
125
- color: intentValue.primary,
126
- },
127
- });
128
-
129
- // Soft type: use primary color
130
- compoundVariants.push({
131
- intent,
132
- type: 'soft',
133
- styles: {
134
- color: intentValue.primary,
135
- },
136
- });
42
+ function getContainerBorderColor(theme: Theme, intent: AlertIntent, type: AlertType): string {
43
+ const intentValue = getIntentValue(theme, intent);
44
+ if (type === 'filled' || type === 'outlined') {
45
+ return intentValue.primary;
137
46
  }
138
-
139
- // Add 'info' intent (maps to primary)
140
- const primaryIntent = theme.intents.primary;
141
- compoundVariants.push({
142
- intent: 'info',
143
- type: 'filled',
144
- styles: {
145
- color: primaryIntent.contrast,
146
- },
147
- });
148
- compoundVariants.push({
149
- intent: 'info',
150
- type: 'outlined',
151
- styles: {
152
- color: primaryIntent.primary,
153
- },
154
- });
155
- compoundVariants.push({
156
- intent: 'info',
157
- type: 'soft',
158
- styles: {
159
- color: primaryIntent.primary,
160
- },
161
- });
162
-
163
- return compoundVariants;
47
+ return intentValue.light; // soft
164
48
  }
165
49
 
166
50
  /**
167
- * Create compound variants for message colors (type + intent combinations)
51
+ * Get icon/title color based on intent and type
168
52
  */
169
- function createMessageCompoundVariants(theme: Theme): CompoundVariants<keyof AlertVariants> {
170
- const compoundVariants: CompoundVariants<keyof AlertVariants> = [];
171
-
172
- // Process standard intents from theme
173
- for (const intent in theme.intents) {
174
- const intentValue = theme.intents[intent as Intent];
175
-
176
- // Filled type: use contrast color
177
- compoundVariants.push({
178
- intent,
179
- type: 'filled',
180
- styles: {
181
- color: intentValue.contrast,
182
- },
183
- });
184
-
185
- // Outlined type: use primary text color
186
- compoundVariants.push({
187
- intent,
188
- type: 'outlined',
189
- styles: {
190
- color: theme.colors.text.primary,
191
- },
192
- });
193
-
194
- // Soft type: use primary text color
195
- compoundVariants.push({
196
- intent,
197
- type: 'soft',
198
- styles: {
199
- color: theme.colors.text.primary,
200
- },
201
- });
53
+ function getIconTitleColor(theme: Theme, intent: AlertIntent, type: AlertType): string {
54
+ const intentValue = getIntentValue(theme, intent);
55
+ if (type === 'filled') {
56
+ return intentValue.contrast;
202
57
  }
58
+ return intentValue.primary; // outlined, soft
59
+ }
203
60
 
204
- // Add 'info' intent (maps to primary)
205
- const primaryIntent = theme.intents.primary;
206
- compoundVariants.push({
207
- intent: 'info',
208
- type: 'filled',
209
- styles: {
210
- color: primaryIntent.contrast,
211
- },
212
- });
213
- compoundVariants.push({
214
- intent: 'info',
215
- type: 'outlined',
216
- styles: {
217
- color: theme.colors.text.primary,
218
- },
219
- });
220
- compoundVariants.push({
221
- intent: 'info',
222
- type: 'soft',
223
- styles: {
224
- color: theme.colors.text.primary,
225
- },
226
- });
227
-
228
- return compoundVariants;
61
+ /**
62
+ * Get message color based on intent and type
63
+ */
64
+ function getMessageColor(theme: Theme, intent: AlertIntent, type: AlertType): string {
65
+ const intentValue = getIntentValue(theme, intent);
66
+ if (type === 'filled') {
67
+ return intentValue.contrast;
68
+ }
69
+ return theme.colors.text.primary; // outlined, soft
229
70
  }
230
71
 
231
- export const alertStyles = StyleSheet.create((theme: Theme) => {
232
- return {
233
- container: {
72
+ /**
73
+ * Create dynamic container styles
74
+ */
75
+ function createContainerStyles(theme: Theme) {
76
+ return ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
77
+ return {
234
78
  display: 'flex',
235
79
  flexDirection: 'row',
236
80
  alignItems: 'flex-start',
@@ -239,66 +83,131 @@ export const alertStyles = StyleSheet.create((theme: Theme) => {
239
83
  borderRadius: 8,
240
84
  borderWidth: 1,
241
85
  borderStyle: 'solid' as const,
242
- variants: {
243
- type: TypeVariants,
244
- } as const,
245
- compoundVariants: createContainerCompoundVariants(theme),
246
- } as const,
247
- iconContainer: {
86
+ backgroundColor: getContainerBackgroundColor(theme, intent, type),
87
+ borderColor: getContainerBorderColor(theme, intent, type),
88
+ } as const;
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Create dynamic icon container styles
94
+ */
95
+ function createIconContainerStyles(theme: Theme) {
96
+ return ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
97
+ return {
248
98
  display: 'flex',
249
99
  alignItems: 'center',
250
100
  justifyContent: 'center',
251
101
  flexShrink: 0,
252
102
  width: 24,
253
103
  height: 24,
254
- compoundVariants: createIconTitleCompoundVariants(theme),
255
- } as const,
256
- content: {
257
- flex: 1,
258
- display: 'flex',
259
- flexDirection: 'column',
260
- gap: 4,
261
- },
262
- title: {
104
+ color: getIconTitleColor(theme, intent, type),
105
+ } as const;
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Create dynamic title styles
111
+ */
112
+ function createTitleStyles(theme: Theme) {
113
+ return ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
114
+ return {
263
115
  fontSize: 16,
264
116
  lineHeight: 24,
265
117
  fontWeight: '600',
266
- compoundVariants: createIconTitleCompoundVariants(theme),
267
- },
268
- message: {
118
+ color: getIconTitleColor(theme, intent, type),
119
+ } as const;
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Create dynamic message styles
125
+ */
126
+ function createMessageStyles(theme: Theme) {
127
+ return ({ intent = 'neutral', type = 'soft' }: AlertDynamicProps) => {
128
+ return {
269
129
  fontSize: 14,
270
130
  lineHeight: 20,
271
- compoundVariants: createMessageCompoundVariants(theme),
272
- },
273
- actions: {
274
- marginTop: 4,
275
- display: 'flex',
276
- flexDirection: 'row',
277
- gap: 8,
278
- },
279
- closeButton: {
280
- padding: 4,
281
- backgroundColor: 'transparent',
282
- borderRadius: 4,
283
- display: 'flex',
284
- alignItems: 'center',
285
- justifyContent: 'center',
286
- flexShrink: 0,
287
- _web: {
288
- border: 'none',
289
- cursor: 'pointer',
290
- outline: 'none',
291
- _hover: {
292
- backgroundColor: 'rgba(0, 0, 0, 0.1)',
293
- },
131
+ color: getMessageColor(theme, intent, type),
132
+ } as const;
133
+ };
134
+ }
135
+
136
+ /**
137
+ * Create content styles
138
+ */
139
+ function createContentStyles() {
140
+ return () => ({
141
+ flex: 1,
142
+ display: 'flex' as const,
143
+ flexDirection: 'column' as const,
144
+ gap: 4,
145
+ });
146
+ }
147
+
148
+ /**
149
+ * Create actions styles
150
+ */
151
+ function createActionsStyles() {
152
+ return () => ({
153
+ marginTop: 4,
154
+ display: 'flex' as const,
155
+ flexDirection: 'row' as const,
156
+ gap: 8,
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Create close button styles
162
+ */
163
+ function createCloseButtonStyles() {
164
+ return () => ({
165
+ padding: 4,
166
+ backgroundColor: 'transparent',
167
+ borderRadius: 4,
168
+ display: 'flex' as const,
169
+ alignItems: 'center' as const,
170
+ justifyContent: 'center' as const,
171
+ flexShrink: 0,
172
+ _web: {
173
+ border: 'none',
174
+ cursor: 'pointer',
175
+ outline: 'none',
176
+ _hover: {
177
+ backgroundColor: 'rgba(0, 0, 0, 0.1)',
294
178
  },
295
179
  },
296
- closeIcon: {
297
- display: 'flex',
298
- alignItems: 'center',
299
- justifyContent: 'center',
300
- width: 16,
301
- height: 16,
302
- },
303
- } as const;
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Create close icon styles
185
+ */
186
+ function createCloseIconStyles() {
187
+ return () => ({
188
+ display: 'flex' as const,
189
+ alignItems: 'center' as const,
190
+ justifyContent: 'center' as const,
191
+ width: 16,
192
+ height: 16,
193
+ });
194
+ }
195
+
196
+ export const alertStyles = StyleSheet.create((theme: Theme) => {
197
+ // Apply extensions to main visual elements
198
+ const extended = applyExtensions('Alert', theme, {
199
+ container: createContainerStyles(theme),
200
+ iconContainer: createIconContainerStyles(theme),
201
+ title: createTitleStyles(theme),
202
+ message: createMessageStyles(theme),
203
+ });
204
+
205
+ return {
206
+ ...extended,
207
+ // Minor utility styles (not extended)
208
+ content: createContentStyles()(),
209
+ actions: createActionsStyles()(),
210
+ closeButton: createCloseButtonStyles()(),
211
+ closeIcon: createCloseIconStyles()(),
212
+ };
304
213
  });
@@ -31,17 +31,13 @@ const Alert = forwardRef<HTMLDivElement, AlertProps>(({
31
31
  testID,
32
32
  id,
33
33
  }, ref) => {
34
- // Apply variants to stylesheet
35
- alertStyles.useVariants({
36
- type,
37
- intent,
38
- });
39
-
40
- const containerProps = getWebProps([alertStyles.container, style as any]);
41
- const iconContainerProps = getWebProps([alertStyles.iconContainer]);
34
+ // Compute dynamic styles with intent and type
35
+ const dynamicProps = { intent, type };
36
+ const containerProps = getWebProps([(alertStyles.container as any)(dynamicProps), style as any]);
37
+ const iconContainerProps = getWebProps([(alertStyles.iconContainer as any)(dynamicProps)]);
42
38
  const contentProps = getWebProps([alertStyles.content]);
43
- const titleProps = getWebProps([alertStyles.title]);
44
- const messageProps = getWebProps([alertStyles.message]);
39
+ const titleProps = getWebProps([(alertStyles.title as any)(dynamicProps)]);
40
+ const messageProps = getWebProps([(alertStyles.message as any)(dynamicProps)]);
45
41
  const actionsProps = getWebProps([alertStyles.actions]);
46
42
  const closeButtonProps = getWebProps([alertStyles.closeButton]);
47
43
  const closeIconProps = getWebProps([alertStyles.closeIcon]);
@@ -41,8 +41,11 @@ const Avatar = forwardRef<View, AvatarProps>(({
41
41
  setHasError(true);
42
42
  };
43
43
 
44
+ const avatarStyle = (avatarStyles.avatar as any)({});
45
+ const fallbackStyle = (avatarStyles.fallback as any)({});
46
+
44
47
  return (
45
- <View ref={ref} nativeID={id} style={[avatarStyles.avatar, style]} testID={testID} {...nativeA11yProps}>
48
+ <View ref={ref} nativeID={id} style={[avatarStyle, style]} testID={testID} {...nativeA11yProps}>
46
49
  {src && !hasError ? (
47
50
  <Image
48
51
  source={typeof src === 'string' ? { uri: src } : src}
@@ -51,7 +54,7 @@ const Avatar = forwardRef<View, AvatarProps>(({
51
54
  accessibilityLabel={alt}
52
55
  />
53
56
  ) : (
54
- <Text style={avatarStyles.fallback}>
57
+ <Text style={fallbackStyle}>
55
58
  {fallback}
56
59
  </Text>
57
60
  )}
@@ -1,6 +1,7 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, StylesheetStyles, Size} from '@idealyst/theme';
2
+ import { Theme, StylesheetStyles, Size } from '@idealyst/theme';
3
3
  import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+ import { applyExtensions } from '../extensions/applyExtension';
4
5
 
5
6
  type AvatarSize = Size;
6
7
  type AvatarShape = 'circle' | 'square';
@@ -42,32 +43,61 @@ function createFallbackSizeVariants(theme: Theme) {
42
43
  }));
43
44
  }
44
45
 
45
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
46
- // transform on native cannot resolve function calls to extract variant structures.
47
- // @ts-ignore - TS language server needs restart to pick up theme structure changes
48
- export const avatarStyles = StyleSheet.create((theme: Theme) => {
49
- return {
50
- avatar: {
51
- display: 'flex',
52
- alignItems: 'center',
53
- justifyContent: 'center',
46
+ /**
47
+ * Create container styles
48
+ */
49
+ function createContainerStyles(theme: Theme) {
50
+ return () => ({
51
+ display: 'flex' as const,
52
+ alignItems: 'center' as const,
53
+ justifyContent: 'center' as const,
54
54
  backgroundColor: theme.colors.surface.secondary,
55
- overflow: 'hidden',
55
+ overflow: 'hidden' as const,
56
56
  variants: {
57
57
  size: createAvatarSizeVariants(theme),
58
58
  shape: createAvatarShapeVariants(theme),
59
59
  },
60
- },
61
- image: {
62
- width: '100%',
63
- height: '100%',
64
- },
65
- fallback: {
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Create image styles
65
+ */
66
+ function createImageStyles() {
67
+ return () => ({
68
+ width: '100%' as const,
69
+ height: '100%' as const,
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Create fallback styles
75
+ */
76
+ function createFallbackStyles(theme: Theme) {
77
+ return () => ({
66
78
  color: theme.colors.text.primary,
67
- fontWeight: '600',
79
+ fontWeight: '600' as const,
68
80
  variants: {
69
81
  size: createFallbackSizeVariants(theme),
70
82
  },
83
+ });
84
+ }
85
+
86
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
87
+ // transform on native cannot resolve function calls to extract variant structures.
88
+ export const avatarStyles = StyleSheet.create((theme: Theme) => {
89
+ // Apply extensions to main visual elements
90
+ const extended = applyExtensions('Avatar', theme, {
91
+ avatar: createContainerStyles(theme),
92
+ fallback: createFallbackStyles(theme),
93
+ });
94
+
95
+ return {
96
+ ...extended,
97
+ // Minor utility styles (not extended)
98
+ image: {
99
+ width: '100%',
100
+ height: '100%',
71
101
  },
72
102
  };
73
103
  });