@idealyst/components 1.1.6 → 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 (104) 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/deepMerge.ts +54 -2
@@ -1,5 +1,6 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
2
  import { Theme, StylesheetStyles} from '@idealyst/theme';
3
+ import { applyExtensions } from '../extensions/applyExtension';
3
4
 
4
5
  type ImageVariants = {}
5
6
 
@@ -13,45 +14,60 @@ export type ImageStylesheet = {
13
14
  loadingIndicator: ExpandedImageStyles;
14
15
  }
15
16
 
17
+ // Style creators for extension support
18
+ function createContainerStyles(theme: Theme) {
19
+ return () => ({
20
+ position: 'relative' as const,
21
+ overflow: 'hidden' as const,
22
+ backgroundColor: theme.colors['gray.200'],
23
+ });
24
+ }
25
+
26
+ function createImageStyles() {
27
+ return () => ({
28
+ width: '100%' as const,
29
+ height: '100%' as const,
30
+ });
31
+ }
32
+
16
33
  // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
17
34
  // transform on native cannot resolve function calls to extract variant structures.
18
35
  // @ts-ignore - TS language server needs restart to pick up theme structure changes
19
36
  export const imageStyles = StyleSheet.create((theme: Theme) => {
20
- return {
21
- container: {
22
- position: 'relative',
23
- overflow: 'hidden',
24
- backgroundColor: theme.colors['gray.200'],
25
- },
26
- image: {
27
- width: '100%',
28
- height: '100%',
29
- },
30
- placeholder: {
31
- position: 'absolute',
32
- top: 0,
33
- left: 0,
34
- right: 0,
35
- bottom: 0,
36
- display: 'flex',
37
- alignItems: 'center',
38
- justifyContent: 'center',
39
- backgroundColor: theme.colors['gray.200'],
40
- },
41
- fallback: {
42
- position: 'absolute',
43
- top: 0,
44
- left: 0,
45
- right: 0,
46
- bottom: 0,
47
- display: 'flex',
48
- alignItems: 'center',
49
- justifyContent: 'center',
50
- backgroundColor: theme.colors['gray.300'],
51
- color: theme.colors['gray.600'],
52
- },
53
- loadingIndicator: {
54
- color: theme.colors['gray.600'],
55
- },
56
- };
37
+ // Apply extensions to main visual elements
38
+ const extended = applyExtensions('Image', theme, {
39
+ container: createContainerStyles(theme),
40
+ image: createImageStyles(),
41
+ });
42
+
43
+ return {
44
+ ...extended,
45
+ // Minor utility styles (not extended)
46
+ placeholder: {
47
+ position: 'absolute',
48
+ top: 0,
49
+ left: 0,
50
+ right: 0,
51
+ bottom: 0,
52
+ display: 'flex',
53
+ alignItems: 'center',
54
+ justifyContent: 'center',
55
+ backgroundColor: theme.colors['gray.200'],
56
+ },
57
+ fallback: {
58
+ position: 'absolute',
59
+ top: 0,
60
+ left: 0,
61
+ right: 0,
62
+ bottom: 0,
63
+ display: 'flex',
64
+ alignItems: 'center',
65
+ justifyContent: 'center',
66
+ backgroundColor: theme.colors['gray.300'],
67
+ color: theme.colors['gray.600'],
68
+ },
69
+ loadingIndicator: {
70
+ color: theme.colors['gray.600'],
71
+ },
72
+ };
57
73
  });
@@ -82,18 +82,17 @@ const Input = React.forwardRef<TextInput, InputProps>(({
82
82
  setIsPasswordVisible(!isPasswordVisible);
83
83
  };
84
84
 
85
- // Apply variants to the stylesheet
85
+ // Apply variants to the stylesheet (for size and spacing)
86
86
  inputStyles.useVariants({
87
87
  size,
88
- type,
89
- focused: isFocused,
90
- hasError,
91
- disabled,
92
88
  margin,
93
89
  marginVertical,
94
90
  marginHorizontal,
95
91
  });
96
92
 
93
+ // Compute dynamic container styles
94
+ const containerStyle = (inputStyles.container as any)({ type, focused: isFocused, hasError, disabled });
95
+
97
96
  // Generate native accessibility props
98
97
  const nativeA11yProps = useMemo(() => {
99
98
  // Derive invalid state from hasError or explicit accessibilityInvalid
@@ -161,7 +160,7 @@ const Input = React.forwardRef<TextInput, InputProps>(({
161
160
  };
162
161
 
163
162
  return (
164
- <View style={[inputStyles.container, style]} testID={testID} nativeID={id}>
163
+ <View style={[containerStyle, style]} testID={testID} nativeID={id}>
165
164
  {/* Left Icon */}
166
165
  {leftIcon && (
167
166
  <View style={inputStyles.leftIconContainer}>
@@ -182,7 +181,7 @@ const Input = React.forwardRef<TextInput, InputProps>(({
182
181
  autoCapitalize={autoCapitalize}
183
182
  onFocus={handleFocus}
184
183
  onBlur={handleBlur}
185
- style={inputStyles.input}
184
+ style={(inputStyles.input as any)({})}
186
185
  placeholderTextColor="#999999"
187
186
  {...nativeA11yProps}
188
187
  />
@@ -1,5 +1,5 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, Size, CompoundVariants} from '@idealyst/theme';
2
+ import { Theme, Size } from '@idealyst/theme';
3
3
  import { buildSizeVariants } from '../utils/buildSizeVariants';
4
4
  import {
5
5
  buildMarginVariants,
@@ -7,6 +7,7 @@ import {
7
7
  buildMarginHorizontalVariants,
8
8
  } from '../utils/buildViewStyleVariants';
9
9
  import { InputSize, InputType } from './types';
10
+ import { applyExtensions } from '../extensions/applyExtension';
10
11
 
11
12
 
12
13
  export type InputVariants = {
@@ -17,182 +18,157 @@ export type InputVariants = {
17
18
  disabled: boolean;
18
19
  }
19
20
 
21
+ type InputDynamicProps = {
22
+ type?: InputType;
23
+ focused?: boolean;
24
+ hasError?: boolean;
25
+ disabled?: boolean;
26
+ };
27
+
20
28
  /**
21
- * Create type variants for container
29
+ * Get container border/background styles based on type, focused, hasError, disabled
22
30
  */
23
- function createContainerTypeVariants(theme: Theme) {
31
+ function getContainerDynamicStyles(theme: Theme, props: InputDynamicProps) {
32
+ const { type = 'outlined', focused = false, hasError = false, disabled = false } = props;
33
+ const focusColor = theme.intents.primary.primary;
34
+ const errorColor = theme.intents.error.primary;
35
+
36
+ // Base styles by type
37
+ let backgroundColor = 'transparent';
38
+ let borderWidth = 1;
39
+ let borderColor = theme.colors.border.primary;
40
+ let borderStyle = 'solid' as const;
41
+
42
+ if (type === 'filled') {
43
+ backgroundColor = theme.colors.surface.secondary;
44
+ borderWidth = 0;
45
+ } else if (type === 'bare') {
46
+ backgroundColor = 'transparent';
47
+ borderWidth = 0;
48
+ }
49
+
50
+ // Error state takes precedence
51
+ if (hasError) {
52
+ borderColor = errorColor;
53
+ borderWidth = 1;
54
+ }
55
+
56
+ // Focus state (error still takes precedence for color)
57
+ if (focused && !hasError) {
58
+ borderColor = focusColor;
59
+ borderWidth = 1;
60
+ }
61
+
62
+ // Disabled state
63
+ if (disabled) {
64
+ backgroundColor = theme.colors.surface.secondary;
65
+ }
66
+
24
67
  return {
25
- outlined: {
26
- backgroundColor: 'transparent',
27
- borderWidth: 1,
28
- borderColor: theme.colors.border.primary,
29
- borderStyle: 'solid' as const,
30
- _web: {
31
- border: `1px solid ${theme.colors.border.primary}`,
32
- },
33
- },
34
- filled: {
35
- backgroundColor: theme.colors.surface.secondary,
36
- borderWidth: 0,
37
- _web: {
38
- border: 'none',
39
- },
40
- },
41
- bare: {
42
- backgroundColor: 'transparent',
43
- borderWidth: 0,
44
- _web: {
45
- border: 'none',
46
- },
47
- },
48
- } as const;
68
+ backgroundColor,
69
+ borderWidth,
70
+ borderColor,
71
+ borderStyle,
72
+ };
49
73
  }
50
74
 
51
75
  /**
52
- * Create compound variants for focused + type + hasError combinations
76
+ * Create dynamic container styles
53
77
  */
54
- function createFocusedCompoundVariants(theme: Theme) {
55
- const compoundVariants = [] as CompoundVariants<keyof InputVariants>;
56
- const focusColor = theme.intents.primary.primary;
57
- const errorColor = theme.intents.error.primary;
78
+ function createContainerStyles(theme: Theme) {
79
+ return (props: InputDynamicProps) => {
80
+ const { type = 'outlined', focused = false, hasError = false, disabled = false } = props;
81
+ const dynamicStyles = getContainerDynamicStyles(theme, props);
82
+ const focusColor = theme.intents.primary.primary;
83
+ const errorColor = theme.intents.error.primary;
58
84
 
59
- // Error state takes precedence
60
- compoundVariants.push({
61
- focused: true,
62
- hasError: true,
63
- styles: {
64
- borderColor: errorColor,
65
- _web: {
66
- border: `1px solid ${errorColor}`,
67
- boxShadow: `0 0 0 2px ${errorColor}20`,
68
- },
69
- },
70
- });
85
+ // Web-specific border and shadow
86
+ let webBorder = `1px solid ${dynamicStyles.borderColor}`;
87
+ let webBoxShadow = 'none';
71
88
 
72
- // Default type + focused (no error)
73
- compoundVariants.push({
74
- type: 'default',
75
- focused: true,
76
- hasError: false,
77
- styles: {
78
- borderColor: focusColor,
79
- _web: {
80
- border: `1px solid ${focusColor}`,
81
- boxShadow: `0 0 0 2px ${focusColor}20`,
82
- },
83
- },
84
- });
89
+ if (type === 'filled' || type === 'bare') {
90
+ webBorder = 'none';
91
+ }
85
92
 
86
- // Outlined type + focused (no error)
87
- compoundVariants.push({
88
- type: 'outlined',
89
- focused: true,
90
- hasError: false,
91
- styles: {
92
- borderColor: focusColor,
93
- _web: {
94
- border: `1px solid ${focusColor}`,
95
- boxShadow: `0 0 0 2px ${focusColor}20`,
96
- },
97
- },
98
- });
93
+ if (hasError) {
94
+ webBorder = `1px solid ${errorColor}`;
95
+ if (focused) {
96
+ webBoxShadow = `0 0 0 2px ${errorColor}20`;
97
+ }
98
+ } else if (focused) {
99
+ webBorder = `1px solid ${focusColor}`;
100
+ webBoxShadow = `0 0 0 2px ${focusColor}20`;
101
+ }
99
102
 
100
- // Filled type + focused (no error)
101
- compoundVariants.push({
102
- type: 'filled',
103
- focused: true,
104
- hasError: false,
105
- styles: {
103
+ return {
104
+ display: 'flex',
105
+ flexDirection: 'row',
106
+ alignItems: 'center',
107
+ width: '100%',
108
+ minWidth: 0,
109
+ borderRadius: 8,
110
+ ...dynamicStyles,
111
+ opacity: disabled ? 0.6 : 1,
112
+ variants: {
113
+ size: buildSizeVariants(theme, 'input', (size) => ({
114
+ height: size.height,
115
+ paddingHorizontal: size.paddingHorizontal,
116
+ })),
117
+ // Spacing variants from FormInputStyleProps
118
+ margin: buildMarginVariants(theme),
119
+ marginVertical: buildMarginVerticalVariants(theme),
120
+ marginHorizontal: buildMarginHorizontalVariants(theme),
121
+ },
106
122
  _web: {
107
- boxShadow: `0 0 0 2px ${focusColor}20`,
123
+ boxSizing: 'border-box',
124
+ transition: 'border-color 0.2s ease, box-shadow 0.2s ease',
125
+ border: webBorder,
126
+ boxShadow: webBoxShadow,
127
+ cursor: disabled ? 'not-allowed' : 'text',
108
128
  },
109
- },
110
- });
111
-
112
- return compoundVariants;
129
+ } as const;
130
+ };
113
131
  }
114
132
 
115
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
116
- // transform on native cannot resolve function calls to extract variant structures.
117
- export const inputStyles = StyleSheet.create((theme: Theme) => {
118
- return {
119
- container: {
120
- display: 'flex',
121
- flexDirection: 'row',
122
- alignItems: 'center',
123
- width: '100%',
124
- minWidth: 0,
125
- borderRadius: 8,
126
- variants: {
127
- size: buildSizeVariants(theme, 'input', (size) => ({
128
- height: size.height,
129
- paddingHorizontal: size.paddingHorizontal,
130
- })),
131
- type: createContainerTypeVariants(theme),
132
- focused: {
133
- true: {},
134
- false: {},
135
- },
136
- hasError: {
137
- true: {
138
- borderColor: theme.intents.error.primary,
139
- _web: {
140
- border: `1px solid ${theme.intents.error.primary}`,
141
- },
142
- },
143
- false: {},
144
- },
145
- disabled: {
146
- true: {
147
- opacity: 0.6,
148
- backgroundColor: theme.colors.surface.secondary,
149
- _web: {
150
- cursor: 'not-allowed',
151
- },
152
- },
153
- false: {
154
- _web: {
155
- cursor: 'text',
156
- _hover: {
157
- borderColor: theme.intents.primary.primary,
158
- },
159
- },
160
- },
161
- },
162
- // Spacing variants from FormInputStyleProps
163
- margin: buildMarginVariants(theme),
164
- marginVertical: buildMarginVerticalVariants(theme),
165
- marginHorizontal: buildMarginHorizontalVariants(theme),
166
- },
167
- compoundVariants: createFocusedCompoundVariants(theme),
168
- _web: {
169
- boxSizing: 'border-box',
170
- transition: 'border-color 0.2s ease, box-shadow 0.2s ease',
171
- },
172
- },
173
- leftIconContainer: {
174
- display: 'flex',
175
- alignItems: 'center',
176
- justifyContent: 'center',
133
+ /**
134
+ * Create left icon container styles
135
+ */
136
+ function createLeftIconContainerStyles(theme: Theme) {
137
+ return () => ({
138
+ display: 'flex' as const,
139
+ alignItems: 'center' as const,
140
+ justifyContent: 'center' as const,
177
141
  flexShrink: 0,
178
142
  variants: {
179
143
  size: buildSizeVariants(theme, 'input', (size) => ({
180
144
  marginRight: size.iconMargin,
181
145
  })),
182
146
  },
183
- },
184
- rightIconContainer: {
185
- display: 'flex',
186
- alignItems: 'center',
187
- justifyContent: 'center',
147
+ });
148
+ }
149
+
150
+ /**
151
+ * Create right icon container styles
152
+ */
153
+ function createRightIconContainerStyles(theme: Theme) {
154
+ return () => ({
155
+ display: 'flex' as const,
156
+ alignItems: 'center' as const,
157
+ justifyContent: 'center' as const,
188
158
  flexShrink: 0,
189
159
  variants: {
190
160
  size: buildSizeVariants(theme, 'input', (size) => ({
191
161
  marginLeft: size.iconMargin,
192
162
  })),
193
163
  },
194
- },
195
- leftIcon: {
164
+ });
165
+ }
166
+
167
+ /**
168
+ * Create left icon styles
169
+ */
170
+ function createLeftIconStyles(theme: Theme) {
171
+ return () => ({
196
172
  color: theme.colors.text.secondary,
197
173
  variants: {
198
174
  size: buildSizeVariants(theme, 'input', (size) => ({
@@ -201,25 +177,37 @@ export const inputStyles = StyleSheet.create((theme: Theme) => {
201
177
  height: size.iconSize,
202
178
  })),
203
179
  },
204
- },
205
- rightIcon: {
206
- display: 'flex',
207
- alignItems: 'center',
208
- justifyContent: 'center',
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Create right icon styles
185
+ */
186
+ function createRightIconStyles(theme: Theme) {
187
+ return () => ({
188
+ display: 'flex' as const,
189
+ alignItems: 'center' as const,
190
+ justifyContent: 'center' as const,
209
191
  flexShrink: 0,
210
192
  color: theme.colors.text.secondary,
211
193
  variants: {
212
194
  size: buildSizeVariants(theme, 'input', (size) => ({
213
- fontSize: size.iconSize,
214
- width: size.iconSize,
215
- height: size.iconSize,
216
- })),
195
+ fontSize: size.iconSize,
196
+ width: size.iconSize,
197
+ height: size.iconSize,
198
+ })),
217
199
  },
218
- },
219
- passwordToggle: {
220
- display: 'flex',
221
- alignItems: 'center',
222
- justifyContent: 'center',
200
+ });
201
+ }
202
+
203
+ /**
204
+ * Create password toggle styles
205
+ */
206
+ function createPasswordToggleStyles(theme: Theme) {
207
+ return () => ({
208
+ display: 'flex' as const,
209
+ alignItems: 'center' as const,
210
+ justifyContent: 'center' as const,
223
211
  flexShrink: 0,
224
212
  padding: 0,
225
213
  variants: {
@@ -238,11 +226,17 @@ export const inputStyles = StyleSheet.create((theme: Theme) => {
238
226
  opacity: 0.5,
239
227
  },
240
228
  },
241
- },
242
- passwordToggleIcon: {
243
- display: 'flex',
244
- alignItems: 'center',
245
- justifyContent: 'center',
229
+ });
230
+ }
231
+
232
+ /**
233
+ * Create password toggle icon styles
234
+ */
235
+ function createPasswordToggleIconStyles(theme: Theme) {
236
+ return () => ({
237
+ display: 'flex' as const,
238
+ alignItems: 'center' as const,
239
+ justifyContent: 'center' as const,
246
240
  flexShrink: 0,
247
241
  color: theme.colors.text.secondary,
248
242
  variants: {
@@ -252,13 +246,19 @@ export const inputStyles = StyleSheet.create((theme: Theme) => {
252
246
  height: size.iconSize,
253
247
  })),
254
248
  },
255
- },
256
- input: {
249
+ });
250
+ }
251
+
252
+ /**
253
+ * Create input styles
254
+ */
255
+ function createInputStyles(theme: Theme) {
256
+ return () => ({
257
257
  flex: 1,
258
258
  minWidth: 0,
259
259
  backgroundColor: 'transparent',
260
260
  color: theme.colors.text.primary,
261
- fontWeight: '400',
261
+ fontWeight: '400' as const,
262
262
  variants: {
263
263
  size: buildSizeVariants(theme, 'input', (size) => ({
264
264
  fontSize: size.fontSize,
@@ -269,6 +269,26 @@ export const inputStyles = StyleSheet.create((theme: Theme) => {
269
269
  outline: 'none',
270
270
  fontFamily: 'inherit',
271
271
  },
272
- },
272
+ });
273
+ }
274
+
275
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
276
+ // transform on native cannot resolve function calls to extract variant structures.
277
+ export const inputStyles = StyleSheet.create((theme: Theme) => {
278
+ // Apply extensions to main visual elements
279
+ const extended = applyExtensions('Input', theme, {
280
+ container: createContainerStyles(theme),
281
+ input: createInputStyles(theme),
282
+ });
283
+
284
+ return {
285
+ ...extended,
286
+ // Minor utility styles (not extended)
287
+ leftIconContainer: createLeftIconContainerStyles(theme)(),
288
+ rightIconContainer: createRightIconContainerStyles(theme)(),
289
+ leftIcon: createLeftIconStyles(theme)(),
290
+ rightIcon: createRightIconStyles(theme)(),
291
+ passwordToggle: createPasswordToggleStyles(theme)(),
292
+ passwordToggleIcon: createPasswordToggleIconStyles(theme)(),
273
293
  };
274
- });
294
+ });
@@ -111,20 +111,17 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({
111
111
  setIsPasswordVisible(!isPasswordVisible);
112
112
  };
113
113
 
114
- // Apply variants for container
114
+ // Apply variants (for size and spacing)
115
115
  inputStyles.useVariants({
116
116
  size,
117
- type,
118
- focused: isFocused,
119
- hasError,
120
- disabled,
121
117
  margin,
122
118
  marginVertical,
123
119
  marginHorizontal,
124
120
  });
125
121
 
126
- // Get web props for all styled elements
127
- const {ref: containerStyleRef, ...containerProps} = getWebProps([inputStyles.container, style]);
122
+ // Get web props for all styled elements (container uses dynamic function)
123
+ const dynamicContainerStyle = (inputStyles.container as any)({ type, focused: isFocused, hasError, disabled });
124
+ const {ref: containerStyleRef, ...containerProps} = getWebProps([dynamicContainerStyle, style]);
128
125
  const leftIconContainerProps = getWebProps([inputStyles.leftIconContainer]);
129
126
  const rightIconContainerProps = getWebProps([inputStyles.rightIconContainer]);
130
127
  const leftIconProps = getWebProps([inputStyles.leftIcon]);
@@ -133,7 +130,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(({
133
130
  const passwordToggleIconProps = getWebProps([inputStyles.passwordToggleIcon]);
134
131
 
135
132
  // Get input props
136
- const inputWebProps = getWebProps([inputStyles.input]);
133
+ const inputWebProps = getWebProps([(inputStyles.input as any)({})]);
137
134
 
138
135
  // Generate accessibility props
139
136
  const ariaProps = useMemo(() => {
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { useNavigator } from '@idealyst/navigation';
3
3
  import { Pressable } from '../Pressable';
4
4
  import type { LinkProps } from './types';
5
+ import { Text } from 'react-native';
5
6
 
6
7
  const Link: React.FC<LinkProps> = ({
7
8
  to,
@@ -22,6 +23,8 @@ const Link: React.FC<LinkProps> = ({
22
23
  onPress?.();
23
24
  navigator.navigate({ path: to, vars });
24
25
  };
26
+
27
+ console.log("Rendering Link to:", to, "children:", children);
25
28
 
26
29
  return (
27
30
  <Pressable
@@ -33,7 +36,7 @@ const Link: React.FC<LinkProps> = ({
33
36
  accessibilityLabel={accessibilityLabel}
34
37
  accessibilityRole="link"
35
38
  >
36
- {children}
39
+ {typeof children === 'string' ? <Text>{children}</Text> : children}
37
40
  </Pressable>
38
41
  );
39
42
  };