@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
@@ -1,252 +1,182 @@
1
- import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, Size, Styles} from '@idealyst/theme';
3
- import { buildSizeVariants } from '../utils/buildSizeVariants';
4
- import {
5
- buildMarginVariants,
6
- buildMarginVerticalVariants,
7
- buildMarginHorizontalVariants,
8
- } from '../utils/buildViewStyleVariants';
9
- import { SliderIntentVariant } from './types';
10
-
11
- /**
12
- * Create size variants for track
13
- */
14
- function createTrackSizeVariants(theme: Theme) {
15
- const variants = {} as Record<Size, Styles>;
16
- for (const sizeKey in theme.sizes.slider) {
17
- const size = sizeKey as Size;
18
- variants[size] = {
19
- height: theme.sizes.slider[size].trackHeight,
20
- };
21
- }
22
- return variants;
23
- }
24
-
25
1
  /**
26
- * Get filled track color based on intent
2
+ * Slider styles using defineStyle with dynamic props.
27
3
  */
28
- function getFilledTrackColor(theme: Theme, intent: SliderIntentVariant) {
29
- return theme.intents[intent].primary;
30
- }
31
-
32
- /**
33
- * Create size variants for thumb
34
- */
35
- function createThumbSizeVariants(theme: Theme) {
36
- return buildSizeVariants(theme, 'slider', (size) => ({
37
- width: size.thumbSize,
38
- height: size.thumbSize,
39
- }));
40
- }
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+ import { defineStyle, ThemeStyleWrapper } from '@idealyst/theme';
6
+ import type { Theme as BaseTheme, Intent, Size } from '@idealyst/theme';
7
+ import { ViewStyleSize } from '../utils/viewStyleProps';
41
8
 
42
- /**
43
- * Get thumb border color based on intent
44
- */
45
- function getThumbBorderColor(theme: Theme, intent: SliderIntentVariant) {
46
- return theme.intents[intent].primary;
47
- }
9
+ // Required: Unistyles must see StyleSheet usage in original source to process this file
10
+ void StyleSheet;
48
11
 
49
- /**
50
- * Create size variants for thumb icon
51
- */
52
- function createThumbIconSizeVariants(theme: Theme) {
53
- return buildSizeVariants(theme, 'slider', (size) => ({
54
- width: size.thumbIconSize,
55
- height: size.thumbIconSize,
56
- minWidth: size.thumbIconSize,
57
- maxWidth: size.thumbIconSize,
58
- minHeight: size.thumbIconSize,
59
- maxHeight: size.thumbIconSize,
60
- }));
61
- }
12
+ // Wrap theme for $iterator support
13
+ type Theme = ThemeStyleWrapper<BaseTheme>;
62
14
 
63
- /**
64
- * Get thumb icon color based on intent
65
- */
66
- function getThumbIconColor(theme: Theme, intent: SliderIntentVariant){
67
- return theme.intents[intent].primary;
68
- }
15
+ export type SliderDynamicProps = {
16
+ size?: Size;
17
+ intent?: Intent;
18
+ disabled?: boolean;
19
+ margin?: ViewStyleSize;
20
+ marginVertical?: ViewStyleSize;
21
+ marginHorizontal?: ViewStyleSize;
22
+ };
69
23
 
70
24
  /**
71
- * Create size variants for mark
25
+ * Slider styles with intent/disabled handling.
72
26
  */
73
- function createMarkSizeVariants(theme: Theme) {
74
- return buildSizeVariants(theme, 'slider', (size) => ({
75
- height: size.markHeight,
76
- }));
77
- }
78
-
79
- const createFilledTrackStyles = (theme: Theme) => {
80
- return ({ intent }: { intent: SliderIntentVariant }) => {
81
- return {
82
- position: 'absolute',
83
- height: '100%',
84
- borderRadius: 9999,
85
- top: 0,
86
- left: 0,
87
- backgroundColor: getFilledTrackColor(theme, intent),
88
- } as const;
89
- }
90
- }
91
-
92
- const createThumbStyles = (theme: Theme) => {
93
- return ({ intent, disabled }: { intent: SliderIntentVariant, disabled: boolean }) => {
94
- return {
95
- position: 'absolute',
96
- backgroundColor: theme.colors.surface.primary,
97
- borderRadius: 9999,
98
- borderWidth: 2,
99
- borderStyle: 'solid',
100
- borderColor: getThumbBorderColor(theme, intent),
101
- top: '50%',
102
- display: 'flex',
103
- alignItems: 'center',
104
- justifyContent: 'center',
105
- shadowColor: '#000',
106
- shadowOffset: { width: 0, height: 2 },
107
- shadowOpacity: 0.2,
108
- shadowRadius: 4,
109
- elevation: 2,
110
- variants: {
111
- size: createThumbSizeVariants(theme),
112
- disabled: {
113
- true: {
114
- opacity: 0.6,
115
- _web: {
116
- cursor: 'not-allowed',
117
- },
118
- },
119
- false: {
120
- opacity: 1,
121
- _web: {
122
- cursor: 'grab',
123
- _hover: {
124
- transform: 'translate(-50%, -50%) scale(1.05)',
125
- },
126
- _active: {
127
- cursor: 'grabbing',
128
- transform: 'translate(-50%, -50%) scale(1.1)',
129
- },
130
- },
131
- },
132
- },
27
+ export const sliderStyles = defineStyle('Slider', (theme: Theme) => ({
28
+ container: (_props: SliderDynamicProps) => ({
29
+ gap: 4,
30
+ paddingVertical: 8,
31
+ variants: {
32
+ margin: {
33
+ margin: theme.sizes.$view.padding,
34
+ },
35
+ marginVertical: {
36
+ marginVertical: theme.sizes.$view.padding,
133
37
  },
134
- _web: {
135
- boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
136
- transform: 'translate(-50%, -50%)',
137
- transition: 'transform 0.15s ease, box-shadow 0.2s ease',
38
+ marginHorizontal: {
39
+ marginHorizontal: theme.sizes.$view.padding,
138
40
  },
139
- } as const;
140
- }
141
- }
142
-
143
- const createThumbIconStyles = (theme: Theme) => {
144
- return ({ intent }: { intent: SliderIntentVariant }) => {
145
- return {
146
- display: 'flex',
147
- alignItems: 'center',
148
- justifyContent: 'center',
149
- flexShrink: 0,
150
- color: getThumbIconColor(theme, intent),
151
- variants: {
152
- size: createThumbIconSizeVariants(theme),
41
+ },
42
+ }),
43
+
44
+ sliderWrapper: (_props: SliderDynamicProps) => ({
45
+ position: 'relative' as const,
46
+ paddingVertical: 4,
47
+ }),
48
+
49
+ track: ({ disabled = false }: SliderDynamicProps) => ({
50
+ backgroundColor: theme.colors.surface.tertiary,
51
+ borderRadius: 9999,
52
+ position: 'relative' as const,
53
+ opacity: disabled ? 0.5 : 1,
54
+ variants: {
55
+ size: {
56
+ height: theme.sizes.$slider.trackHeight,
153
57
  },
154
- } as const;
155
- }
156
- }
157
-
158
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
159
- // @ts-ignore - TS language server needs restart to pick up theme structure changes
160
- export const sliderStyles = StyleSheet.create((theme: Theme) => {
161
- return {
162
- container: {
163
- gap: 4,
164
- paddingVertical: 8,
165
- variants: {
166
- // Spacing variants from FormInputStyleProps
167
- margin: buildMarginVariants(theme),
168
- marginVertical: buildMarginVerticalVariants(theme),
169
- marginHorizontal: buildMarginHorizontalVariants(theme),
58
+ },
59
+ _web: {
60
+ cursor: disabled ? 'not-allowed' : 'pointer',
61
+ },
62
+ }),
63
+
64
+ filledTrack: ({ intent = 'primary' }: SliderDynamicProps) => ({
65
+ position: 'absolute' as const,
66
+ height: '100%',
67
+ borderRadius: 9999,
68
+ top: 0,
69
+ left: 0,
70
+ backgroundColor: theme.intents[intent].primary,
71
+ }),
72
+
73
+ thumb: ({ intent = 'primary', disabled = false }: SliderDynamicProps) => ({
74
+ position: 'absolute' as const,
75
+ backgroundColor: theme.colors.surface.primary,
76
+ borderRadius: 9999,
77
+ borderWidth: 2,
78
+ borderStyle: 'solid' as const,
79
+ borderColor: theme.intents[intent].primary,
80
+ top: '50%',
81
+ display: 'flex' as const,
82
+ alignItems: 'center' as const,
83
+ justifyContent: 'center' as const,
84
+ shadowColor: '#000',
85
+ shadowOffset: { width: 0, height: 2 },
86
+ shadowOpacity: 0.2,
87
+ shadowRadius: 4,
88
+ elevation: 2,
89
+ opacity: disabled ? 0.6 : 1,
90
+ variants: {
91
+ size: {
92
+ width: theme.sizes.$slider.thumbSize,
93
+ height: theme.sizes.$slider.thumbSize,
170
94
  },
171
95
  },
172
- sliderWrapper: {
173
- position: 'relative',
174
- paddingVertical: 4,
96
+ _web: {
97
+ boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
98
+ transform: 'translate(-50%, -50%)',
99
+ transition: 'transform 0.15s ease, box-shadow 0.2s ease',
100
+ cursor: disabled ? 'not-allowed' : 'grab',
101
+ _hover: disabled ? {} : { transform: 'translate(-50%, -50%) scale(1.05)' },
102
+ _active: disabled ? {} : { cursor: 'grabbing', transform: 'translate(-50%, -50%) scale(1.1)' },
175
103
  },
176
- track: {
177
- backgroundColor: theme.colors.surface.tertiary,
178
- borderRadius: 9999,
179
- position: 'relative',
180
- variants: {
181
- size: createTrackSizeVariants(theme),
182
- disabled: {
183
- true: {
184
- opacity: 0.5,
185
- _web: {
186
- cursor: 'not-allowed',
187
- },
188
- },
189
- false: {
190
- opacity: 1,
191
- _web: {
192
- cursor: 'pointer',
193
- },
194
- },
195
- },
196
- } as const,
197
- } as const,
198
- filledTrack: createFilledTrackStyles(theme),
199
- thumb: createThumbStyles(theme),
200
- thumbActive: {
201
- _web: {
202
- transform: 'translate(-50%, -50%) scale(1.1)',
104
+ }),
105
+
106
+ thumbActive: (_props: SliderDynamicProps) => ({
107
+ _web: {
108
+ transform: 'translate(-50%, -50%) scale(1.1)',
109
+ },
110
+ }),
111
+
112
+ thumbIcon: ({ intent = 'primary' }: SliderDynamicProps) => ({
113
+ display: 'flex' as const,
114
+ alignItems: 'center' as const,
115
+ justifyContent: 'center' as const,
116
+ flexShrink: 0,
117
+ color: theme.intents[intent].primary,
118
+ variants: {
119
+ size: {
120
+ width: theme.sizes.$slider.thumbIconSize,
121
+ height: theme.sizes.$slider.thumbIconSize,
122
+ minWidth: theme.sizes.$slider.thumbIconSize,
123
+ maxWidth: theme.sizes.$slider.thumbIconSize,
124
+ minHeight: theme.sizes.$slider.thumbIconSize,
125
+ maxHeight: theme.sizes.$slider.thumbIconSize,
203
126
  },
204
127
  },
205
- thumbIcon: createThumbIconStyles(theme),
206
- valueLabel: {
128
+ }),
129
+
130
+ valueLabel: (_props: SliderDynamicProps) => ({
207
131
  fontSize: 12,
208
- fontWeight: '600',
132
+ fontWeight: '600' as const,
209
133
  color: theme.colors.text.primary,
210
- textAlign: 'center',
211
- },
212
- minMaxLabels: {
213
- flexDirection: 'row',
214
- justifyContent: 'space-between',
134
+ textAlign: 'center' as const,
135
+ }),
136
+
137
+ minMaxLabels: (_props: SliderDynamicProps) => ({
138
+ flexDirection: 'row' as const,
139
+ justifyContent: 'space-between' as const,
215
140
  marginTop: 4,
216
- },
217
- minMaxLabel: {
141
+ }),
142
+
143
+ minMaxLabel: (_props: SliderDynamicProps) => ({
218
144
  fontSize: 12,
219
145
  color: theme.colors.text.secondary,
220
- },
221
- mark: {
222
- position: 'absolute',
223
- width: 2,
224
- backgroundColor: theme.colors.border.secondary,
225
- top: '50%',
226
- variants: {
227
- size: createMarkSizeVariants(theme),
228
- },
229
- _web: {
230
- transform: 'translate(-50%, -50%)',
146
+ }),
147
+
148
+ marks: (_props: SliderDynamicProps) => ({
149
+ position: 'absolute' as const,
150
+ width: '100%',
151
+ height: '100%',
152
+ top: 0,
153
+ left: 0,
154
+ }),
155
+
156
+ mark: (_props: SliderDynamicProps) => ({
157
+ position: 'absolute' as const,
158
+ width: 2,
159
+ backgroundColor: theme.colors.border.secondary,
160
+ top: '50%',
161
+ variants: {
162
+ size: {
163
+ height: theme.sizes.$slider.markHeight,
231
164
  },
232
165
  },
233
- marks: {
234
- position: 'absolute',
235
- width: '100%',
236
- height: '100%',
237
- top: 0,
238
- left: 0,
166
+ _web: {
167
+ transform: 'translate(-50%, -50%)',
239
168
  },
240
- markLabel: {
241
- position: 'absolute',
242
- fontSize: 10,
243
- color: theme.colors.text.secondary,
244
- top: '100%',
245
- marginTop: 4,
246
- _web: {
247
- transform: 'translateX(-50%)',
248
- whiteSpace: 'nowrap',
249
- },
169
+ }),
170
+
171
+ markLabel: (_props: SliderDynamicProps) => ({
172
+ position: 'absolute' as const,
173
+ fontSize: 10,
174
+ color: theme.colors.text.secondary,
175
+ top: '100%',
176
+ marginTop: 4,
177
+ _web: {
178
+ transform: 'translateX(-50%)',
179
+ whiteSpace: 'nowrap',
250
180
  },
251
- };
252
- });
181
+ }),
182
+ }));
@@ -57,10 +57,10 @@ const Slider = forwardRef<HTMLDivElement, SliderProps>(({
57
57
  marginHorizontal,
58
58
  });
59
59
 
60
- const containerProps = getWebProps([sliderStyles.container, style as any]);
60
+ const containerProps = getWebProps([(sliderStyles.container as any)({}), style as any]);
61
61
  const wrapperProps = getWebProps([sliderStyles.sliderWrapper]);
62
- const trackProps = getWebProps([sliderStyles.track]);
63
- const thumbIconProps = getWebProps([sliderStyles.thumbIcon({ intent })]);
62
+ const trackProps = getWebProps([(sliderStyles.track as any)({})]);
63
+ const thumbIconProps = getWebProps([(sliderStyles.thumbIcon as any)({ intent })]);
64
64
  const valueLabelProps = getWebProps([sliderStyles.valueLabel]);
65
65
  const minMaxLabelsProps = getWebProps([sliderStyles.minMaxLabels]);
66
66
  const minMaxLabelProps = getWebProps([sliderStyles.minMaxLabel]);
@@ -211,9 +211,9 @@ const Slider = forwardRef<HTMLDivElement, SliderProps>(({
211
211
  const percentage = ((value - min) / (max - min)) * 100;
212
212
 
213
213
  // Dynamic styles with percentage
214
- const filledTrackProps = getWebProps([sliderStyles.filledTrack({ intent }), { width: `${percentage}%` }]);
214
+ const filledTrackProps = getWebProps([(sliderStyles.filledTrack as any)({ intent }), { width: `${percentage}%` }]);
215
215
  const thumbProps = getWebProps([
216
- sliderStyles.thumb({ intent, disabled }),
216
+ (sliderStyles.thumb as any)({ intent, disabled }),
217
217
  isDragging && sliderStyles.thumbActive,
218
218
  { left: `${percentage}%` }
219
219
  ]);
@@ -118,17 +118,23 @@ const Switch = forwardRef<ComponentRef<typeof Pressable>, SwitchProps>(({
118
118
  };
119
119
  });
120
120
 
121
+ // Get dynamic styles - call as functions for theme reactivity
122
+ const switchTrackStyle = (switchStyles.switchTrack as any)({ checked, intent, disabled });
123
+ const containerStyle = (switchStyles.container as any)({});
124
+ const switchContainerStyle = (switchStyles.switchContainer as any)({});
125
+ const labelStyle = (switchStyles.label as any)({ disabled, labelPosition });
126
+
121
127
  const switchElement = (
122
128
  <Pressable
123
129
  ref={!label ? ref : undefined}
124
130
  nativeID={!label ? id : undefined}
125
131
  onPress={handlePress}
126
132
  disabled={disabled}
127
- style={switchStyles.switchContainer}
133
+ style={switchContainerStyle}
128
134
  testID={testID}
129
135
  {...nativeA11yProps}
130
136
  >
131
- <Animated.View style={switchStyles.switchTrack({ checked, intent })}>
137
+ <Animated.View style={switchTrackStyle}>
132
138
  <Animated.View
133
139
  style={[
134
140
  {
@@ -158,14 +164,14 @@ const Switch = forwardRef<ComponentRef<typeof Pressable>, SwitchProps>(({
158
164
  nativeID={id}
159
165
  onPress={handlePress}
160
166
  disabled={disabled}
161
- style={[switchStyles.container, style]}
167
+ style={[containerStyle, style]}
162
168
  >
163
169
  {labelPosition === 'left' && (
164
- <Text style={switchStyles.label}>{label}</Text>
170
+ <Text style={labelStyle}>{label}</Text>
165
171
  )}
166
172
  {switchElement}
167
173
  {labelPosition === 'right' && (
168
- <Text style={switchStyles.label}>{label}</Text>
174
+ <Text style={labelStyle}>{label}</Text>
169
175
  )}
170
176
  </Pressable>
171
177
  );
@@ -0,0 +1,203 @@
1
+ import { StyleSheet } from 'react-native-unistyles';
2
+ import { Theme, StylesheetStyles, Intent, Size } from '@idealyst/theme';
3
+ import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+ import {
5
+ buildMarginVariants,
6
+ buildMarginVerticalVariants,
7
+ buildMarginHorizontalVariants,
8
+ } from '../utils/buildViewStyleVariants';
9
+ import { SwitchIntentVariant, SwitchSizeVariant } from './types';
10
+ import { applyExtensions } from '../extensions/applyExtension';
11
+
12
+ function createTrackSizeVariants(theme: Theme) {
13
+ return buildSizeVariants(theme, 'switch', (size) => ({
14
+ width: size.trackWidth,
15
+ height: size.trackHeight,
16
+ }));
17
+ }
18
+
19
+ function getTrackBackgroundColor(theme: Theme, checked: boolean, intent: SwitchIntentVariant) {
20
+ if (checked) {
21
+ return theme.intents[intent].primary;
22
+ }
23
+ return theme.colors.border.secondary;
24
+ }
25
+
26
+ function createThumbSizeVariants(theme: Theme) {
27
+ return buildSizeVariants(theme, 'switch', (size) => ({
28
+ width: size.thumbSize,
29
+ height: size.thumbSize,
30
+ left: 2,
31
+ }));
32
+ }
33
+
34
+ function getThumbTransform(theme: Theme, size: SwitchSizeVariant, checked: boolean) {
35
+ const sizeValue = theme.sizes.switch[size];
36
+ const translateX = checked ? sizeValue.translateX : 0;
37
+ return `translateY(-50%) translateX(${translateX}px)`;
38
+ }
39
+
40
+ function createThumbIconSizeVariants(theme: Theme) {
41
+ return buildSizeVariants(theme, 'switch', (size) => ({
42
+ width: size.thumbIconSize,
43
+ height: size.thumbIconSize,
44
+ }));
45
+ }
46
+
47
+ function getThumbIconColor(theme: Theme, checked: boolean, intent: SwitchIntentVariant) {
48
+ if (checked) {
49
+ return theme.intents[intent].primary;
50
+ }
51
+ return theme.colors.border.secondary;
52
+ }
53
+
54
+ function createSwitchTrackStyles(theme: Theme) {
55
+ return ({ checked, intent }: { checked: boolean, intent: SwitchIntentVariant }) => {
56
+ return {
57
+ borderRadius: 9999,
58
+ position: 'relative',
59
+ backgroundColor: getTrackBackgroundColor(theme, checked, intent),
60
+ variants: {
61
+ size: createTrackSizeVariants(theme),
62
+ disabled: {
63
+ true: {
64
+ opacity: 0.5,
65
+ _web: {
66
+ cursor: 'not-allowed',
67
+ },
68
+ },
69
+ false: {
70
+ opacity: 1,
71
+ _web: {
72
+ cursor: 'pointer',
73
+ _hover: {
74
+ opacity: 0.9,
75
+ },
76
+ _active: {
77
+ opacity: 0.8,
78
+ },
79
+ },
80
+ },
81
+ },
82
+ } as const,
83
+ _web: {
84
+ transition: 'background-color 0.2s ease',
85
+ },
86
+ } as const;
87
+ }
88
+ }
89
+
90
+ function createSwitchThumbStyles(theme: Theme) {
91
+ return ({ size, checked }: { size: SwitchSizeVariant, checked: boolean }) => {
92
+ return {
93
+ position: 'absolute',
94
+ backgroundColor: theme.colors.surface.primary,
95
+ borderRadius: 9999,
96
+ top: '50%',
97
+ display: 'flex',
98
+ alignItems: 'center',
99
+ justifyContent: 'center',
100
+ shadowColor: '#000',
101
+ shadowOffset: { width: 0, height: 1 },
102
+ shadowOpacity: 0.2,
103
+ shadowRadius: 3,
104
+ elevation: 2,
105
+ variants: {
106
+ size: createThumbSizeVariants(theme),
107
+ },
108
+ _web: {
109
+ boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',
110
+ transition: 'transform 0.2s ease',
111
+ transform: getThumbTransform(theme, size, checked),
112
+ },
113
+ } as const;
114
+ }
115
+ }
116
+
117
+ function createThumbIconStyles(theme: Theme) {
118
+ return ({ checked, intent }: { checked: boolean, intent: SwitchIntentVariant }) => {
119
+ return {
120
+ display: 'flex',
121
+ alignItems: 'center',
122
+ justifyContent: 'center',
123
+ color: getThumbIconColor(theme, checked, intent),
124
+ variants: {
125
+ size: createThumbIconSizeVariants(theme),
126
+ },
127
+ } as const;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Create container styles
133
+ */
134
+ function createContainerStyles(theme: Theme) {
135
+ return () => ({
136
+ flexDirection: 'row' as const,
137
+ alignItems: 'center' as const,
138
+ gap: 8,
139
+ variants: {
140
+ margin: buildMarginVariants(theme),
141
+ marginVertical: buildMarginVerticalVariants(theme),
142
+ marginHorizontal: buildMarginHorizontalVariants(theme),
143
+ },
144
+ });
145
+ }
146
+
147
+ /**
148
+ * Create switch container styles
149
+ */
150
+ function createSwitchContainerStyles() {
151
+ return () => ({
152
+ justifyContent: 'center' as const,
153
+ _web: {
154
+ border: 'none',
155
+ padding: 0,
156
+ backgroundColor: 'transparent',
157
+ width: 'fit-content',
158
+ },
159
+ });
160
+ }
161
+
162
+ /**
163
+ * Create label styles
164
+ */
165
+ function createLabelStyles(theme: Theme) {
166
+ return () => ({
167
+ fontSize: 14,
168
+ color: theme.colors.text.primary,
169
+ variants: {
170
+ disabled: {
171
+ true: {
172
+ opacity: 0.5,
173
+ },
174
+ false: {
175
+ opacity: 1,
176
+ },
177
+ },
178
+ position: {
179
+ left: {
180
+ marginRight: 8,
181
+ },
182
+ right: {
183
+ marginLeft: 8,
184
+ },
185
+ },
186
+ } as const,
187
+ });
188
+ }
189
+
190
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
191
+ // transform on native cannot resolve function calls to extract variant structures.
192
+ export const switchStyles = StyleSheet.create((theme: Theme) => {
193
+ // Apply extensions to main visual elements
194
+
195
+ return applyExtensions('Switch', theme, {container: createContainerStyles(theme),
196
+ switchTrack: createSwitchTrackStyles(theme),
197
+ switchThumb: createSwitchThumbStyles(theme),
198
+ // Additional styles (merged from return)
199
+ // Minor utility styles (not extended)
200
+ switchContainer: createSwitchContainerStyles()(),
201
+ thumbIcon: createThumbIconStyles(theme),
202
+ label: createLabelStyles(theme)()});
203
+ });