@idealyst/components 1.1.6 → 1.1.8

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 (144) hide show
  1. package/package.json +8 -3
  2. package/src/Accordion/Accordion.native.tsx +22 -14
  3. package/src/Accordion/Accordion.styles.old.tsx +298 -0
  4. package/src/Accordion/Accordion.styles.tsx +139 -248
  5. package/src/Accordion/Accordion.web.tsx +12 -7
  6. package/src/ActivityIndicator/ActivityIndicator.native.tsx +3 -2
  7. package/src/ActivityIndicator/ActivityIndicator.styles.old.tsx +94 -0
  8. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +43 -62
  9. package/src/ActivityIndicator/ActivityIndicator.web.tsx +2 -2
  10. package/src/Alert/Alert.native.tsx +26 -15
  11. package/src/Alert/Alert.styles.old.tsx +209 -0
  12. package/src/Alert/Alert.styles.tsx +108 -281
  13. package/src/Alert/Alert.web.tsx +6 -10
  14. package/src/Avatar/Avatar.native.tsx +5 -2
  15. package/src/Avatar/Avatar.styles.old.tsx +99 -0
  16. package/src/Avatar/Avatar.styles.tsx +47 -62
  17. package/src/Avatar/Avatar.web.tsx +2 -2
  18. package/src/Badge/Badge.native.tsx +2 -2
  19. package/src/Badge/Badge.styles.old.tsx +157 -0
  20. package/src/Badge/Badge.styles.tsx +69 -108
  21. package/src/Badge/Badge.web.tsx +6 -6
  22. package/src/Breadcrumb/Breadcrumb.native.tsx +12 -5
  23. package/src/Breadcrumb/Breadcrumb.styles.old.tsx +231 -0
  24. package/src/Breadcrumb/Breadcrumb.styles.tsx +93 -209
  25. package/src/Breadcrumb/Breadcrumb.web.tsx +39 -27
  26. package/src/Button/Button.native.tsx +39 -14
  27. package/src/Button/Button.styles.tsx +99 -253
  28. package/src/Button/Button.web.tsx +10 -8
  29. package/src/Card/Card.native.tsx +8 -4
  30. package/src/Card/Card.styles.old.tsx +160 -0
  31. package/src/Card/Card.styles.tsx +107 -142
  32. package/src/Card/Card.web.tsx +6 -4
  33. package/src/Checkbox/Checkbox.native.tsx +14 -6
  34. package/src/Checkbox/Checkbox.styles.old.tsx +271 -0
  35. package/src/Checkbox/Checkbox.styles.tsx +109 -197
  36. package/src/Checkbox/Checkbox.web.tsx +7 -7
  37. package/src/Chip/Chip.native.tsx +5 -5
  38. package/src/Chip/Chip.styles.old.tsx +184 -0
  39. package/src/Chip/Chip.styles.tsx +34 -22
  40. package/src/Chip/Chip.web.tsx +5 -5
  41. package/src/Dialog/Dialog.native.tsx +16 -7
  42. package/src/Dialog/Dialog.styles.old.tsx +202 -0
  43. package/src/Dialog/Dialog.styles.tsx +108 -132
  44. package/src/Dialog/Dialog.web.tsx +4 -4
  45. package/src/Divider/Divider.native.tsx +29 -42
  46. package/src/Divider/Divider.styles.old.tsx +172 -0
  47. package/src/Divider/Divider.styles.tsx +116 -242
  48. package/src/Divider/Divider.web.tsx +17 -14
  49. package/src/Icon/Icon.native.tsx +12 -4
  50. package/src/Icon/Icon.styles.old.tsx +81 -0
  51. package/src/Icon/Icon.styles.tsx +52 -60
  52. package/src/Icon/Icon.web.tsx +43 -7
  53. package/src/Icon/IconSvg/IconSvg.web.tsx +2 -0
  54. package/src/Image/Image.styles.old.tsx +69 -0
  55. package/src/Image/Image.styles.tsx +45 -43
  56. package/src/Input/Input.native.tsx +140 -56
  57. package/src/Input/Input.styles.old.tsx +289 -0
  58. package/src/Input/Input.styles.tsx +177 -228
  59. package/src/Input/Input.web.tsx +5 -8
  60. package/src/Link/Link.native.tsx +4 -1
  61. package/src/List/List.native.tsx +5 -2
  62. package/src/List/List.styles.old.tsx +242 -0
  63. package/src/List/List.styles.tsx +178 -240
  64. package/src/List/ListItem.native.tsx +16 -8
  65. package/src/List/ListItem.web.tsx +26 -15
  66. package/src/Menu/Menu.native.tsx +1 -1
  67. package/src/Menu/Menu.styles.old.tsx +197 -0
  68. package/src/Menu/Menu.styles.tsx +90 -156
  69. package/src/Menu/Menu.web.tsx +2 -2
  70. package/src/Menu/MenuItem.native.tsx +9 -5
  71. package/src/Menu/MenuItem.styles.old.tsx +114 -0
  72. package/src/Menu/MenuItem.styles.tsx +71 -104
  73. package/src/Menu/MenuItem.web.tsx +23 -5
  74. package/src/Popover/Popover.native.tsx +10 -4
  75. package/src/Popover/Popover.styles.old.tsx +135 -0
  76. package/src/Popover/Popover.styles.tsx +46 -96
  77. package/src/Popover/Popover.web.tsx +1 -1
  78. package/src/Pressable/Pressable.native.tsx +3 -1
  79. package/src/Pressable/Pressable.styles.old.tsx +27 -0
  80. package/src/Pressable/Pressable.styles.tsx +35 -20
  81. package/src/Pressable/Pressable.web.tsx +1 -1
  82. package/src/Progress/Progress.native.tsx +15 -6
  83. package/src/Progress/Progress.styles.old.tsx +200 -0
  84. package/src/Progress/Progress.styles.tsx +69 -118
  85. package/src/Progress/Progress.web.tsx +10 -9
  86. package/src/RadioButton/RadioButton.native.tsx +10 -4
  87. package/src/RadioButton/RadioButton.styles.old.tsx +175 -0
  88. package/src/RadioButton/RadioButton.styles.tsx +81 -145
  89. package/src/RadioButton/RadioButton.web.tsx +4 -4
  90. package/src/SVGImage/SVGImage.styles.old.tsx +86 -0
  91. package/src/SVGImage/SVGImage.styles.tsx +35 -66
  92. package/src/Screen/Screen.native.tsx +30 -27
  93. package/src/Screen/Screen.styles.old.tsx +87 -0
  94. package/src/Screen/Screen.styles.tsx +120 -71
  95. package/src/Screen/Screen.web.tsx +2 -2
  96. package/src/Select/Select.native.tsx +44 -29
  97. package/src/Select/Select.styles.old.tsx +353 -0
  98. package/src/Select/Select.styles.tsx +244 -293
  99. package/src/Select/Select.web.tsx +5 -5
  100. package/src/Skeleton/Skeleton.styles.old.tsx +67 -0
  101. package/src/Skeleton/Skeleton.styles.tsx +31 -43
  102. package/src/Slider/Slider.native.tsx +9 -5
  103. package/src/Slider/Slider.styles.old.tsx +259 -0
  104. package/src/Slider/Slider.styles.tsx +157 -227
  105. package/src/Slider/Slider.web.tsx +5 -5
  106. package/src/Switch/Switch.native.tsx +11 -5
  107. package/src/Switch/Switch.styles.old.tsx +203 -0
  108. package/src/Switch/Switch.styles.tsx +103 -149
  109. package/src/Switch/Switch.web.tsx +8 -8
  110. package/src/TabBar/TabBar.native.tsx +24 -31
  111. package/src/TabBar/TabBar.styles.old.tsx +343 -0
  112. package/src/TabBar/TabBar.styles.tsx +204 -494
  113. package/src/TabBar/TabBar.web.tsx +21 -33
  114. package/src/Table/Table.native.tsx +18 -9
  115. package/src/Table/Table.styles.old.tsx +311 -0
  116. package/src/Table/Table.styles.tsx +151 -278
  117. package/src/Table/Table.web.tsx +1 -1
  118. package/src/Text/Text.native.tsx +1 -4
  119. package/src/Text/Text.style.demo.tsx +16 -0
  120. package/src/Text/Text.styles.old.tsx +219 -0
  121. package/src/Text/Text.styles.tsx +94 -78
  122. package/src/Text/Text.web.tsx +2 -2
  123. package/src/Text/index.ts +1 -0
  124. package/src/TextArea/TextArea.styles.old.tsx +213 -0
  125. package/src/TextArea/TextArea.styles.tsx +101 -157
  126. package/src/Tooltip/Tooltip.native.tsx +2 -2
  127. package/src/Tooltip/Tooltip.styles.old.tsx +82 -0
  128. package/src/Tooltip/Tooltip.styles.tsx +38 -53
  129. package/src/Tooltip/Tooltip.web.tsx +2 -2
  130. package/src/Video/Video.styles.old.tsx +51 -0
  131. package/src/Video/Video.styles.tsx +32 -28
  132. package/src/View/View.native.tsx +12 -12
  133. package/src/View/View.styles.old.tsx +125 -0
  134. package/src/View/View.styles.tsx +84 -103
  135. package/src/View/View.web.tsx +14 -2
  136. package/src/examples/CardExamples.tsx +0 -6
  137. package/src/extensions/applyExtension.ts +210 -0
  138. package/src/extensions/extendComponent.ts +438 -0
  139. package/src/extensions/index.ts +102 -0
  140. package/src/extensions/types.ts +497 -0
  141. package/src/globals.ts +16 -0
  142. package/src/index.native.ts +4 -0
  143. package/src/index.ts +28 -0
  144. package/src/utils/deepMerge.ts +54 -2
@@ -0,0 +1,197 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles, CompoundVariants, Intent, Size } from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+ import { applyExtensions } from '../extensions/applyExtension';
5
+
6
+ type MenuSize = Size;
7
+ type MenuIntent = Intent;
8
+
9
+ type MenuVariants = {
10
+ size: MenuSize;
11
+ intent: MenuIntent;
12
+ disabled: boolean;
13
+ }
14
+
15
+ export type ExpandedMenuStyles = StylesheetStyles<keyof MenuVariants>;
16
+
17
+ export type MenuStylesheet = {
18
+ overlay: ExpandedMenuStyles;
19
+ menu: ExpandedMenuStyles;
20
+ separator: ExpandedMenuStyles;
21
+ item: ExpandedMenuStyles;
22
+ icon: ExpandedMenuStyles;
23
+ label: ExpandedMenuStyles;
24
+ }
25
+
26
+ /**
27
+ * Create size variants for menu item
28
+ */
29
+ function createItemSizeVariants(theme: Theme) {
30
+ return buildSizeVariants(theme, 'menu', (size) => ({
31
+ paddingVertical: size.paddingVertical,
32
+ paddingHorizontal: size.paddingHorizontal,
33
+ }));
34
+ }
35
+
36
+ /**
37
+ * Get hover styles for menu item based on intent
38
+ */
39
+ function getItemHoverStyles(theme: Theme, intent: MenuIntent) {
40
+ if (intent === 'neutral') {
41
+ return {};
42
+ }
43
+ const intentValue = theme.intents[intent];
44
+ return {
45
+ _web: {
46
+ _hover: {
47
+ backgroundColor: intentValue.light + '20',
48
+ color: intentValue.primary,
49
+ },
50
+ },
51
+ } as const;
52
+ }
53
+
54
+ /**
55
+ * Create compound variants for menu item
56
+ */
57
+ function createItemCompoundVariants(theme: Theme): CompoundVariants<keyof MenuVariants> {
58
+ return [
59
+ {
60
+ disabled: true,
61
+ styles: {
62
+ _web: {
63
+ _hover: {
64
+ backgroundColor: 'transparent',
65
+ },
66
+ },
67
+ },
68
+ },
69
+ ] as const;
70
+ }
71
+
72
+ /**
73
+ * Create size variants for icon
74
+ */
75
+ function createIconSizeVariants(theme: Theme) {
76
+ return buildSizeVariants(theme, 'menu', (size) => ({
77
+ width: size.iconSize,
78
+ height: size.iconSize,
79
+ fontSize: size.iconSize,
80
+ }));
81
+ }
82
+
83
+ /**
84
+ * Create size variants for label
85
+ */
86
+ function createLabelSizeVariants(theme: Theme) {
87
+ return buildSizeVariants(theme, 'menu', (size) => ({
88
+ fontSize: size.labelFontSize,
89
+ }));
90
+ }
91
+
92
+ // Main element style creators (for extension support)
93
+ function createOverlayStyles(theme: Theme) {
94
+ return () => ({
95
+ backgroundColor: 'transparent',
96
+ _web: {
97
+ position: 'fixed' as const,
98
+ top: 0,
99
+ left: 0,
100
+ right: 0,
101
+ bottom: 0,
102
+ zIndex: 999,
103
+ },
104
+ });
105
+ }
106
+
107
+ function createMenuStyles(theme: Theme) {
108
+ return () => ({
109
+ position: 'absolute' as const,
110
+ zIndex: 1000,
111
+ backgroundColor: theme.colors.surface.primary,
112
+ borderWidth: 1,
113
+ borderStyle: 'solid' as const,
114
+ borderColor: theme.colors.border.primary,
115
+ borderRadius: 8,
116
+ minWidth: 120,
117
+ maxWidth: 400,
118
+ padding: 4,
119
+ display: 'flex' as const,
120
+ flexDirection: 'column' as const,
121
+ _web: {
122
+ border: `1px solid ${theme.colors.border.primary}`,
123
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
124
+ width: 'fit-content',
125
+ },
126
+ });
127
+ }
128
+
129
+ function createItemStyles(theme: Theme) {
130
+ return ({ intent }: Partial<MenuVariants>) => {
131
+ const hoverStyles = getItemHoverStyles(theme, intent ?? 'neutral');
132
+ return {
133
+ flexDirection: 'row',
134
+ alignItems: 'center',
135
+ backgroundColor: 'transparent',
136
+ borderRadius: 4,
137
+ minHeight: 44,
138
+ variants: {
139
+ size: createItemSizeVariants(theme),
140
+ disabled: {
141
+ true: {
142
+ opacity: 0.5,
143
+ _web: {
144
+ cursor: 'not-allowed',
145
+ },
146
+ },
147
+ false: {},
148
+ },
149
+ },
150
+ compoundVariants: createItemCompoundVariants(theme),
151
+ _web: {
152
+ cursor: 'pointer',
153
+ border: 'none',
154
+ outline: 'none',
155
+ transition: 'background-color 0.2s ease',
156
+ textAlign: 'left',
157
+ _hover: {
158
+ backgroundColor: theme.colors.surface.secondary,
159
+ },
160
+ },
161
+ ...hoverStyles,
162
+ } as const;
163
+ };
164
+ }
165
+
166
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
167
+ export const menuStyles = StyleSheet.create((theme: Theme) => {
168
+ // Apply extensions to main visual elements
169
+
170
+ return applyExtensions('Menu', theme, {overlay: createOverlayStyles(theme),
171
+ menu: createMenuStyles(theme),
172
+ item: createItemStyles(theme),
173
+ // Additional styles (merged from return)
174
+ // Minor utility styles (not extended)
175
+ separator: {
176
+ height: 1,
177
+ backgroundColor: theme.colors.border.primary,
178
+ marginTop: 4,
179
+ marginBottom: 4,
180
+ },
181
+ icon: {
182
+ alignItems: 'center',
183
+ justifyContent: 'center',
184
+ flexShrink: 0,
185
+ marginRight: 8,
186
+ variants: {
187
+ size: createIconSizeVariants(theme),
188
+ } as const,
189
+ } as const,
190
+ label: {
191
+ flex: 1,
192
+ color: theme.colors.text.primary,
193
+ variants: {
194
+ size: createLabelSizeVariants(theme),
195
+ } as const,
196
+ } as const});
197
+ });
@@ -1,185 +1,119 @@
1
+ /**
2
+ * Menu styles using defineStyle with dynamic props.
3
+ */
1
4
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, StylesheetStyles, CompoundVariants, Intent, Size} from '@idealyst/theme';
3
- import { buildSizeVariants } from '../utils/buildSizeVariants';
5
+ import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme, Intent, Size } from '@idealyst/theme';
4
7
 
5
- type MenuSize = Size;
6
- type MenuIntent = Intent;
8
+ // Required: Unistyles must see StyleSheet usage in original source to process this file
9
+ void StyleSheet;
7
10
 
8
- type MenuVariants = {
9
- size: MenuSize;
10
- intent: MenuIntent;
11
- disabled: boolean;
12
- }
11
+ // Wrap theme for $iterator support
12
+ type Theme = ThemeStyleWrapper<BaseTheme>;
13
13
 
14
- export type ExpandedMenuStyles = StylesheetStyles<keyof MenuVariants>;
15
-
16
- export type MenuStylesheet = {
17
- overlay: ExpandedMenuStyles;
18
- menu: ExpandedMenuStyles;
19
- separator: ExpandedMenuStyles;
20
- item: ExpandedMenuStyles;
21
- icon: ExpandedMenuStyles;
22
- label: ExpandedMenuStyles;
23
- }
14
+ export type MenuDynamicProps = {
15
+ size?: Size;
16
+ intent?: Intent;
17
+ disabled?: boolean;
18
+ };
24
19
 
25
20
  /**
26
- * Create size variants for menu item
21
+ * Menu styles with intent/disabled handling.
27
22
  */
28
- function createItemSizeVariants(theme: Theme) {
29
- return buildSizeVariants(theme, 'menu', (size) => ({
30
- paddingVertical: size.paddingVertical,
31
- paddingHorizontal: size.paddingHorizontal,
32
- }));
33
- }
34
-
35
- /**
36
- * Get hover styles for menu item based on intent
37
- */
38
- function getItemHoverStyles(theme: Theme, intent: MenuIntent) {
39
- if (intent === 'neutral') {
40
- return {};
41
- }
42
- const intentValue = theme.intents[intent];
43
- return {
23
+ export const menuStyles = defineStyle('Menu', (theme: Theme) => ({
24
+ overlay: (_props: MenuDynamicProps) => ({
25
+ backgroundColor: 'transparent' as const,
44
26
  _web: {
45
- _hover: {
46
- backgroundColor: intentValue.light + '20',
47
- color: intentValue.primary,
48
- },
27
+ position: 'fixed' as const,
28
+ top: 0,
29
+ left: 0,
30
+ right: 0,
31
+ bottom: 0,
32
+ zIndex: 999,
49
33
  },
50
- } as const;
51
- }
34
+ }),
52
35
 
53
- /**
54
- * Create compound variants for menu item
55
- */
56
- function createItemCompoundVariants(theme: Theme): CompoundVariants<keyof MenuVariants> {
57
- return [
58
- {
59
- disabled: true,
60
- styles: {
61
- _web: {
62
- _hover: {
63
- backgroundColor: 'transparent',
64
- },
65
- },
66
- },
36
+ menu: (_props: MenuDynamicProps) => ({
37
+ position: 'absolute' as const,
38
+ zIndex: 1000,
39
+ backgroundColor: theme.colors.surface.primary,
40
+ borderWidth: 1,
41
+ borderStyle: 'solid' as const,
42
+ borderColor: theme.colors.border.primary,
43
+ borderRadius: 8,
44
+ minWidth: 120,
45
+ maxWidth: 400,
46
+ padding: 4,
47
+ display: 'flex' as const,
48
+ flexDirection: 'column' as const,
49
+ _web: {
50
+ border: `1px solid ${theme.colors.border.primary}`,
51
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
52
+ width: 'fit-content',
67
53
  },
68
- ] as const;
69
- }
70
-
71
- /**
72
- * Create size variants for icon
73
- */
74
- function createIconSizeVariants(theme: Theme) {
75
- return buildSizeVariants(theme, 'menu', (size) => ({
76
- width: size.iconSize,
77
- height: size.iconSize,
78
- fontSize: size.iconSize,
79
- }));
80
- }
54
+ }),
81
55
 
82
- /**
83
- * Create size variants for label
84
- */
85
- function createLabelSizeVariants(theme: Theme) {
86
- return buildSizeVariants(theme, 'menu', (size) => ({
87
- fontSize: size.labelFontSize,
88
- }));
89
- }
56
+ item: ({ intent = 'neutral', disabled = false }: MenuDynamicProps) => {
57
+ const intentValue = theme.intents[intent];
58
+ const hoverStyles = intent !== 'neutral' ? {
59
+ backgroundColor: intentValue.light + '20',
60
+ color: intentValue.primary,
61
+ } : {
62
+ backgroundColor: theme.colors.surface.secondary,
63
+ };
90
64
 
91
- const createItemStyles = (theme: Theme) => {
92
- return ({ intent }: MenuVariants) => {
93
- const hoverStyles = getItemHoverStyles(theme, intent);
94
65
  return {
95
- flexDirection: 'row',
96
- alignItems: 'center',
97
- backgroundColor: 'transparent',
66
+ flexDirection: 'row' as const,
67
+ alignItems: 'center' as const,
68
+ backgroundColor: 'transparent' as const,
98
69
  borderRadius: 4,
99
70
  minHeight: 44,
71
+ opacity: disabled ? 0.5 : 1,
100
72
  variants: {
101
- size: createItemSizeVariants(theme),
102
- disabled: {
103
- true: {
104
- opacity: 0.5,
105
- _web: {
106
- cursor: 'not-allowed',
107
- },
108
- },
109
- false: {},
73
+ size: {
74
+ paddingVertical: theme.sizes.$menu.paddingVertical,
75
+ paddingHorizontal: theme.sizes.$menu.paddingHorizontal,
110
76
  },
111
77
  },
112
- compoundVariants: createItemCompoundVariants(theme),
113
78
  _web: {
114
- cursor: 'pointer',
79
+ cursor: disabled ? 'not-allowed' : 'pointer',
115
80
  border: 'none',
116
81
  outline: 'none',
117
82
  transition: 'background-color 0.2s ease',
118
83
  textAlign: 'left',
119
- _hover: {
120
- backgroundColor: theme.colors.surface.secondary,
121
- },
84
+ _hover: disabled ? { backgroundColor: 'transparent' } : hoverStyles,
122
85
  },
123
- ...hoverStyles,
124
86
  } as const;
125
- }
126
- }
87
+ },
127
88
 
128
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
129
- export const menuStyles = StyleSheet.create((theme: Theme) => {
130
- return {
131
- overlay: {
132
- backgroundColor: 'transparent',
133
- _web: {
134
- position: 'fixed' as const,
135
- top: 0,
136
- left: 0,
137
- right: 0,
138
- bottom: 0,
139
- zIndex: 999,
140
- }
141
- } as const,
142
- menu: {
143
- position: 'absolute',
144
- zIndex: 1000,
145
- backgroundColor: theme.colors.surface.primary,
146
- borderWidth: 1,
147
- borderStyle: 'solid',
148
- borderColor: theme.colors.border.primary,
149
- borderRadius: 8,
150
- minWidth: 120,
151
- maxWidth: 400,
152
- padding: 4,
153
- display: 'flex',
154
- flexDirection: 'column',
155
- _web: {
156
- border: `1px solid ${theme.colors.border.primary}`,
157
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
158
- width: 'fit-content',
89
+ separator: (_props: MenuDynamicProps) => ({
90
+ height: 1,
91
+ backgroundColor: theme.colors.border.primary,
92
+ marginTop: 4,
93
+ marginBottom: 4,
94
+ }),
95
+
96
+ icon: (_props: MenuDynamicProps) => ({
97
+ alignItems: 'center' as const,
98
+ justifyContent: 'center' as const,
99
+ flexShrink: 0,
100
+ marginRight: 8,
101
+ variants: {
102
+ size: {
103
+ width: theme.sizes.$menu.iconSize,
104
+ height: theme.sizes.$menu.iconSize,
105
+ fontSize: theme.sizes.$menu.iconSize,
159
106
  },
160
- } as const,
161
- separator: {
162
- height: 1,
163
- backgroundColor: theme.colors.border.primary,
164
- marginTop: 4,
165
- marginBottom: 4,
166
107
  },
167
- item: createItemStyles(theme),
168
- icon: {
169
- alignItems: 'center',
170
- justifyContent: 'center',
171
- flexShrink: 0,
172
- marginRight: 8,
173
- variants: {
174
- size: createIconSizeVariants(theme),
175
- } as const,
176
- } as const,
177
- label: {
178
- flex: 1,
179
- color: theme.colors.text.primary,
180
- variants: {
181
- size: createLabelSizeVariants(theme),
182
- } as const,
183
- } as const,
184
- } as const;
185
- });
108
+ }),
109
+
110
+ label: (_props: MenuDynamicProps) => ({
111
+ flex: 1,
112
+ color: theme.colors.text.primary,
113
+ variants: {
114
+ size: {
115
+ fontSize: theme.sizes.$menu.labelFontSize,
116
+ },
117
+ },
118
+ }),
119
+ }));
@@ -113,8 +113,8 @@ const Menu = forwardRef<HTMLDivElement, MenuProps>(({
113
113
  size,
114
114
  });
115
115
 
116
- const overlayProps = getWebProps([menuStyles.overlay]);
117
- const menuProps = getWebProps([menuStyles.menu, style as any]);
116
+ const overlayProps = getWebProps([(menuStyles.overlay as any)({})]);
117
+ const menuProps = getWebProps([(menuStyles.menu as any)({}), style as any]);
118
118
  const separatorProps = getWebProps([menuStyles.separator]);
119
119
 
120
120
  const handleTriggerClick = () => {
@@ -13,13 +13,17 @@ interface MenuItemProps {
13
13
  }
14
14
 
15
15
  const MenuItem = forwardRef<ComponentRef<typeof Pressable>, MenuItemProps>(({ item, onPress, size = 'md', testID }, ref) => {
16
- // Initialize styles with useVariants
16
+ // Initialize styles with useVariants (for size and disabled)
17
17
  menuItemStyles.useVariants({
18
18
  size,
19
19
  disabled: Boolean(item.disabled),
20
- intent: item.intent || 'neutral',
21
20
  });
22
21
 
22
+ // Call styles as functions to get theme-reactive styles
23
+ const itemStyle = (menuItemStyles.item as any)({ intent: item.intent || 'neutral' });
24
+ const iconStyle = (menuItemStyles.icon as any)({});
25
+ const labelStyle = (menuItemStyles.label as any)({});
26
+
23
27
  const renderIcon = () => {
24
28
  if (!item.icon) return null;
25
29
 
@@ -27,7 +31,7 @@ const MenuItem = forwardRef<ComponentRef<typeof Pressable>, MenuItemProps>(({ it
27
31
  return (
28
32
  <MaterialDesignIcons
29
33
  name={item.icon as any}
30
- style={menuItemStyles.icon}
34
+ style={iconStyle}
31
35
  />
32
36
  );
33
37
  } else if (isValidElement(item.icon)) {
@@ -39,7 +43,7 @@ const MenuItem = forwardRef<ComponentRef<typeof Pressable>, MenuItemProps>(({ it
39
43
  return (
40
44
  <Pressable
41
45
  ref={ref}
42
- style={menuItemStyles.item}
46
+ style={itemStyle}
43
47
  onPress={() => onPress(item)}
44
48
  disabled={item.disabled}
45
49
  accessibilityRole="menuitem"
@@ -54,7 +58,7 @@ const MenuItem = forwardRef<ComponentRef<typeof Pressable>, MenuItemProps>(({ it
54
58
  {renderIcon()}
55
59
  </View>
56
60
  )}
57
- <Text style={menuItemStyles.label}>
61
+ <Text style={labelStyle}>
58
62
  {item.label}
59
63
  </Text>
60
64
  </Pressable>
@@ -0,0 +1,114 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, Intent } from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+ import { applyExtensions } from '../extensions/applyExtension';
5
+
6
+ type MenuItemDynamicProps = {
7
+ intent?: Intent;
8
+ };
9
+
10
+ /**
11
+ * Get hover styles for menu item based on intent
12
+ */
13
+ function getItemHoverStyles(theme: Theme, intent: Intent) {
14
+ if (intent === 'neutral') {
15
+ return {
16
+ _web: {
17
+ _hover: {
18
+ backgroundColor: theme.colors.surface.secondary,
19
+ },
20
+ },
21
+ } as const;
22
+ }
23
+ const intentValue = theme.intents[intent];
24
+ return {
25
+ _web: {
26
+ _hover: {
27
+ backgroundColor: intentValue.light,
28
+ color: intentValue.primary,
29
+ },
30
+ },
31
+ } as const;
32
+ }
33
+
34
+ /**
35
+ * Create dynamic item styles
36
+ */
37
+ function createItemStyles(theme: Theme) {
38
+ return ({ intent = 'neutral' }: MenuItemDynamicProps) => {
39
+ const hoverStyles = getItemHoverStyles(theme, intent);
40
+ return {
41
+ flexDirection: 'row',
42
+ alignItems: 'center',
43
+ backgroundColor: 'transparent',
44
+ borderRadius: 4,
45
+ minHeight: 44,
46
+ variants: {
47
+ size: buildSizeVariants(theme, 'menu', (size) => ({
48
+ paddingVertical: size.paddingVertical,
49
+ paddingHorizontal: size.paddingHorizontal,
50
+ })),
51
+ disabled: {
52
+ true: {
53
+ opacity: 0.5,
54
+ _web: {
55
+ cursor: 'not-allowed',
56
+ _hover: {
57
+ backgroundColor: 'transparent',
58
+ },
59
+ },
60
+ },
61
+ false: {},
62
+ },
63
+ },
64
+ _web: {
65
+ display: 'flex',
66
+ width: '100%',
67
+ cursor: 'pointer',
68
+ border: 'none',
69
+ borderWidth: 0,
70
+ outline: 'none',
71
+ transition: 'background-color 0.2s ease',
72
+ textAlign: 'left',
73
+ _hover: {
74
+ backgroundColor: theme.colors.surface.secondary,
75
+ },
76
+ },
77
+ ...hoverStyles,
78
+ } as const;
79
+ };
80
+ }
81
+
82
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
83
+ export const menuItemStyles = StyleSheet.create((theme: Theme) => {
84
+ // Apply extensions to main visual elements
85
+
86
+ return applyExtensions('MenuItem', theme, {item: createItemStyles(theme),
87
+ // Additional styles (merged from return)
88
+ // Minor utility styles (not extended)
89
+ icon: {
90
+ alignItems: 'center',
91
+ justifyContent: 'center',
92
+ flexShrink: 0,
93
+ marginRight: 12,
94
+ variants: {
95
+ size: buildSizeVariants(theme, 'menu', (size) => ({
96
+ width: size.iconSize,
97
+ height: size.iconSize,
98
+ fontSize: size.iconSize,
99
+ }))
100
+ },
101
+ _web: {
102
+ display: 'flex',
103
+ },
104
+ },
105
+ label: {
106
+ flex: 1,
107
+ color: theme.colors.text.primary,
108
+ variants: {
109
+ size: buildSizeVariants(theme, 'menu', (size) => ({
110
+ fontSize: size.labelFontSize,
111
+ })),
112
+ },
113
+ }});
114
+ });