@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
@@ -87,9 +87,9 @@ const RadioButton: React.FC<RadioButtonProps> = ({
87
87
  marginHorizontal,
88
88
  });
89
89
 
90
- const containerProps = getWebProps([radioButtonStyles.container, style]);
91
- const radioProps = getWebProps([radioButtonStyles.radio({ intent })]);
92
- const dotProps = getWebProps([radioButtonStyles.radioDot({ intent })]);
90
+ const containerProps = getWebProps([(radioButtonStyles.container as any)({}), style]);
91
+ const radioProps = getWebProps([(radioButtonStyles.radio as any)({ intent })]);
92
+ const dotProps = getWebProps([(radioButtonStyles.radioDot as any)({ intent })]);
93
93
  const labelProps = getWebProps([radioButtonStyles.label]);
94
94
 
95
95
  return (
@@ -1,5 +1,6 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
2
  import { Theme, StylesheetStyles, Intent} from '@idealyst/theme';
3
+ import { applyExtensions } from '../extensions/applyExtension';
3
4
 
4
5
  type SVGImageIntent = Intent;
5
6
 
@@ -47,14 +48,11 @@ function createContainerNativeIntentVariants(theme: Theme) {
47
48
  return variants;
48
49
  }
49
50
 
50
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
51
- // transform on native cannot resolve function calls to extract variant structures.
52
- // @ts-ignore - TS language server needs restart to pick up theme structure changes
53
- export const svgImageStyles = StyleSheet.create((theme: Theme) => {
54
- return {
55
- container: {
56
- alignItems: 'center',
57
- justifyContent: 'center',
51
+ // Style creators for extension support
52
+ function createContainerStyles(theme: Theme) {
53
+ return () => ({
54
+ alignItems: 'center' as const,
55
+ justifyContent: 'center' as const,
58
56
  variants: {
59
57
  intent: createContainerIntentVariants(theme),
60
58
  },
@@ -66,13 +64,27 @@ export const svgImageStyles = StyleSheet.create((theme: Theme) => {
66
64
  intent: createContainerNativeIntentVariants(theme),
67
65
  },
68
66
  },
69
- },
70
- image: {
71
- _web: {
72
- display: 'block',
73
- maxWidth: '100%',
74
- height: 'auto',
67
+ });
68
+ }
69
+
70
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
71
+ // transform on native cannot resolve function calls to extract variant structures.
72
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
73
+ export const svgImageStyles = StyleSheet.create((theme: Theme) => {
74
+ // Apply extensions to main visual elements
75
+ const extended = applyExtensions('SVGImage', theme, {
76
+ container: createContainerStyles(theme),
77
+ });
78
+
79
+ return {
80
+ ...extended,
81
+ // Minor utility styles (not extended)
82
+ image: {
83
+ _web: {
84
+ display: 'block',
85
+ maxWidth: '100%',
86
+ height: 'auto',
87
+ },
75
88
  },
76
- },
77
- };
89
+ };
78
90
  });
@@ -1,5 +1,6 @@
1
1
  import React, { forwardRef } from 'react';
2
- import { View as RNView, ScrollView as RNScrollView, SafeAreaView } from 'react-native';
2
+ import { View as RNView, ScrollView as RNScrollView } from 'react-native';
3
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
3
4
  import { ScreenProps } from './types';
4
5
  import { screenStyles } from './Screen.styles';
5
6
 
@@ -21,6 +22,7 @@ const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
21
22
  testID,
22
23
  id,
23
24
  }, ref) => {
25
+ const insets = useSafeAreaInsets();
24
26
 
25
27
  screenStyles.useVariants({
26
28
  background,
@@ -34,6 +36,14 @@ const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
34
36
  marginHorizontal,
35
37
  });
36
38
 
39
+ // Calculate safe area padding
40
+ const safeAreaStyle = safeArea ? {
41
+ paddingTop: insets.top,
42
+ paddingBottom: insets.bottom,
43
+ paddingLeft: insets.left,
44
+ paddingRight: insets.right,
45
+ } : undefined;
46
+
37
47
  if (scrollable) {
38
48
  // For ScrollView, flex: 1 goes on the ScrollView style
39
49
  // Background and padding go on contentContainerStyle (without flex: 1)
@@ -41,11 +51,12 @@ const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
41
51
 
42
52
  const contentContainerStyleArray = [
43
53
  screenStyles.screenContent,
54
+ safeAreaStyle,
44
55
  contentInset ? {
45
- paddingTop: contentInset.top,
46
- paddingBottom: contentInset.bottom,
47
- paddingLeft: contentInset.left,
48
- paddingRight: contentInset.right,
56
+ paddingTop: (safeArea ? insets.top : 0) + (contentInset.top ?? 0),
57
+ paddingBottom: (safeArea ? insets.bottom : 0) + (contentInset.bottom ?? 0),
58
+ paddingLeft: (safeArea ? insets.left : 0) + (contentInset.left ?? 0),
59
+ paddingRight: (safeArea ? insets.right : 0) + (contentInset.right ?? 0),
49
60
  } : undefined,
50
61
  ];
51
62
 
@@ -62,19 +73,18 @@ const Screen = forwardRef<RNView | RNScrollView, ScreenProps>(({
62
73
  );
63
74
  }
64
75
 
65
- const containerStyle = [screenStyles.screen, style];
76
+ const screenStyle = (screenStyles.screen as any)({});
77
+ const containerStyle = [
78
+ screenStyle,
79
+ safeAreaStyle,
80
+ style,
81
+ ];
66
82
 
67
- const view = (
83
+ return (
68
84
  <RNView ref={ref as any} nativeID={id} style={containerStyle} testID={testID}>
69
85
  {children}
70
86
  </RNView>
71
87
  );
72
-
73
- if (safeArea) {
74
- return <SafeAreaView style={{ flex: 1 }}>{view}</SafeAreaView>;
75
- }
76
-
77
- return view;
78
88
  });
79
89
 
80
90
  Screen.displayName = 'Screen';
@@ -9,6 +9,7 @@ import {
9
9
  buildMarginVerticalVariants,
10
10
  buildMarginHorizontalVariants,
11
11
  } from '../utils/buildViewStyleVariants';
12
+ import { applyExtensions } from '../extensions/applyExtension';
12
13
 
13
14
  function generateBackgroundVariants(theme: Theme) {
14
15
  return {
@@ -22,55 +23,65 @@ function generateBackgroundVariants(theme: Theme) {
22
23
  };
23
24
  }
24
25
 
26
+ // Style creators for extension support
27
+ function createScreenStyles(theme: Theme) {
28
+ return () => ({
29
+ flex: 1,
30
+ variants: {
31
+ background: generateBackgroundVariants(theme),
32
+ safeArea: {
33
+ true: {},
34
+ false: {},
35
+ },
36
+ // Spacing variants from ContainerStyleProps
37
+ gap: buildGapVariants(theme),
38
+ padding: buildPaddingVariants(theme),
39
+ paddingVertical: buildPaddingVerticalVariants(theme),
40
+ paddingHorizontal: buildPaddingHorizontalVariants(theme),
41
+ margin: buildMarginVariants(theme),
42
+ marginVertical: buildMarginVerticalVariants(theme),
43
+ marginHorizontal: buildMarginHorizontalVariants(theme),
44
+ },
45
+ _web: {
46
+ overflow: 'auto',
47
+ display: 'flex',
48
+ flexDirection: 'column',
49
+ minHeight: '100%',
50
+ boxSizing: 'border-box',
51
+ },
52
+ });
53
+ }
54
+
25
55
  // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
26
56
  // transform on native cannot resolve function calls to extract variant structures.
27
57
  // @ts-ignore - TS language server needs restart to pick up theme structure changes
28
58
  export const screenStyles = StyleSheet.create((theme: Theme) => {
29
- return {
30
- screen: {
31
- flex: 1,
32
- backgroundColor: theme.colors.surface.primary,
33
- variants: {
34
- background: generateBackgroundVariants(theme),
35
- safeArea: {
36
- true: {},
37
- false: {},
38
- },
39
- // Spacing variants from ContainerStyleProps
40
- gap: buildGapVariants(theme),
41
- padding: buildPaddingVariants(theme),
42
- paddingVertical: buildPaddingVerticalVariants(theme),
43
- paddingHorizontal: buildPaddingHorizontalVariants(theme),
44
- margin: buildMarginVariants(theme),
45
- marginVertical: buildMarginVerticalVariants(theme),
46
- marginHorizontal: buildMarginHorizontalVariants(theme),
47
- },
48
- _web: {
49
- overflow: 'auto',
50
- display: 'flex',
51
- flexDirection: 'column',
52
- minHeight: '100%',
53
- boxSizing: 'border-box',
54
- },
55
- },
56
- // Content style for ScrollView - no flex: 1 so content can grow
57
- screenContent: {
58
- backgroundColor: theme.colors.surface.primary,
59
- variants: {
60
- background: generateBackgroundVariants(theme),
61
- safeArea: {
62
- true: {},
63
- false: {},
59
+ // Apply extensions to main visual elements
60
+ const extended = applyExtensions('Screen', theme, {
61
+ screen: createScreenStyles(theme),
62
+ });
63
+
64
+ return {
65
+ ...extended,
66
+ // Minor utility styles (not extended)
67
+ // Content style for ScrollView - no flex: 1 so content can grow
68
+ screenContent: {
69
+ backgroundColor: theme.colors.surface.primary,
70
+ variants: {
71
+ background: generateBackgroundVariants(theme),
72
+ safeArea: {
73
+ true: {},
74
+ false: {},
75
+ },
76
+ // Spacing variants from ContainerStyleProps
77
+ gap: buildGapVariants(theme),
78
+ padding: buildPaddingVariants(theme),
79
+ paddingVertical: buildPaddingVerticalVariants(theme),
80
+ paddingHorizontal: buildPaddingHorizontalVariants(theme),
81
+ margin: buildMarginVariants(theme),
82
+ marginVertical: buildMarginVerticalVariants(theme),
83
+ marginHorizontal: buildMarginHorizontalVariants(theme),
84
+ },
64
85
  },
65
- // Spacing variants from ContainerStyleProps
66
- gap: buildGapVariants(theme),
67
- padding: buildPaddingVariants(theme),
68
- paddingVertical: buildPaddingVerticalVariants(theme),
69
- paddingHorizontal: buildPaddingHorizontalVariants(theme),
70
- margin: buildMarginVariants(theme),
71
- marginVertical: buildMarginVerticalVariants(theme),
72
- marginHorizontal: buildMarginHorizontalVariants(theme),
73
- },
74
- },
75
- };
86
+ };
76
87
  });
@@ -33,7 +33,7 @@ const Screen = forwardRef<HTMLDivElement, ScreenProps>(({
33
33
  });
34
34
 
35
35
  // Use getWebProps to generate className and ref for web
36
- const webProps = getWebProps([screenStyles.screen, style as any]);
36
+ const webProps = getWebProps([(screenStyles.screen as any)({}), style as any]);
37
37
 
38
38
  const mergedRef = useMergeRefs(ref, webProps.ref);
39
39
 
@@ -233,7 +233,7 @@ const Select = forwardRef<View, SelectProps>(({
233
233
  >
234
234
  <Animated.View
235
235
  style={[
236
- selectStyles.dropdown,
236
+ (selectStyles.dropdown as any)({}),
237
237
  {
238
238
  position: 'relative', // Override absolute positioning from styles
239
239
  top: 0, // Override top: '100%' from styles
@@ -264,7 +264,7 @@ const Select = forwardRef<View, SelectProps>(({
264
264
  {filteredOptions.map((option) => (
265
265
  <Pressable
266
266
  key={option.value}
267
- style={selectStyles.option}
267
+ style={(selectStyles.option as any)({})}
268
268
  onPress={() => handleOptionSelect(option)}
269
269
  disabled={option.disabled}
270
270
  android_ripple={{ color: 'rgba(0, 0, 0, 0.1)' }}
@@ -284,7 +284,7 @@ const Select = forwardRef<View, SelectProps>(({
284
284
  ))}
285
285
 
286
286
  {filteredOptions.length === 0 && (
287
- <View style={selectStyles.option}>
287
+ <View style={(selectStyles.option as any)({})}>
288
288
  <Text style={selectStyles.optionText}>
289
289
  No options found
290
290
  </Text>
@@ -298,8 +298,14 @@ const Select = forwardRef<View, SelectProps>(({
298
298
  );
299
299
  };
300
300
 
301
+ // Get dynamic styles
302
+ const containerStyle = (selectStyles.container as any)({});
303
+ const triggerStyle = (selectStyles.trigger as any)({ type, intent });
304
+ const dropdownStyle = (selectStyles.dropdown as any)({});
305
+ const optionStyle = (selectStyles.option as any)({});
306
+
301
307
  return (
302
- <View ref={ref} nativeID={id} style={[selectStyles.container, style]} testID={testID}>
308
+ <View ref={ref} nativeID={id} style={[containerStyle, style]} testID={testID}>
303
309
  {label && (
304
310
  <Text style={selectStyles.label}>
305
311
  {label}
@@ -308,7 +314,7 @@ const Select = forwardRef<View, SelectProps>(({
308
314
 
309
315
  <Pressable
310
316
  ref={mergedTriggerRef}
311
- style={selectStyles.trigger({ type, intent })}
317
+ style={triggerStyle}
312
318
  onPress={handleTriggerPress}
313
319
  disabled={disabled}
314
320
  accessibilityLabel={accessibilityLabel || label}
@@ -7,6 +7,7 @@ import {
7
7
  buildMarginHorizontalVariants,
8
8
  } from '../utils/buildViewStyleVariants';
9
9
  import { SelectIntentVariant } from './types';
10
+ import { applyExtensions } from '../extensions/applyExtension';
10
11
 
11
12
  // Type definitions
12
13
  type SelectSize = Size;
@@ -137,26 +138,87 @@ function buildDynamicTriggerStyles(theme: Theme) {
137
138
  }
138
139
  }
139
140
 
141
+ // Main element style creators (wrapped for extension support)
142
+ function createContainerStyles(theme: Theme) {
143
+ return () => ({
144
+ position: 'relative' as const,
145
+ variants: {
146
+ margin: buildMarginVariants(theme),
147
+ marginVertical: buildMarginVerticalVariants(theme),
148
+ marginHorizontal: buildMarginHorizontalVariants(theme),
149
+ },
150
+ });
151
+ }
152
+
153
+ function createDropdownStyles(theme: Theme) {
154
+ return () => ({
155
+ position: 'absolute' as const,
156
+ top: '100%',
157
+ left: 0,
158
+ right: 0,
159
+ backgroundColor: theme.colors.surface.primary,
160
+ borderRadius: 8,
161
+ borderWidth: 1,
162
+ borderStyle: 'solid' as const,
163
+ borderColor: theme.colors.border.primary,
164
+ shadowColor: '#000',
165
+ shadowOffset: { width: 0, height: 8 },
166
+ shadowOpacity: 0.1,
167
+ shadowRadius: 24,
168
+ elevation: 8,
169
+ zIndex: 9999,
170
+ maxHeight: 240,
171
+ minWidth: 200,
172
+ overflow: 'hidden' as const,
173
+ _web: {
174
+ border: `1px solid ${theme.colors.border.primary}`,
175
+ boxShadow: '0 8px 24px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.06)',
176
+ overflowY: 'auto',
177
+ },
178
+ });
179
+ }
180
+
181
+ function createOptionStyles(theme: Theme) {
182
+ return () => ({
183
+ paddingHorizontal: 8,
184
+ paddingVertical: 4,
185
+ flexDirection: 'row' as const,
186
+ alignItems: 'center' as const,
187
+ minHeight: 36,
188
+ _web: {
189
+ display: 'flex',
190
+ cursor: 'pointer',
191
+ _hover: {
192
+ backgroundColor: theme.colors.surface.secondary,
193
+ },
194
+ _active: {
195
+ opacity: 0.8,
196
+ },
197
+ },
198
+ });
199
+ }
200
+
140
201
  // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
141
202
  // transform on native cannot resolve function calls to extract variant structures.
142
203
  export const selectStyles = StyleSheet.create((theme: Theme) => {
204
+ // Apply extensions to main visual elements
205
+ const extendedStyles = applyExtensions('Select', theme, {
206
+ container: createContainerStyles(theme),
207
+ trigger: buildDynamicTriggerStyles(theme),
208
+ dropdown: createDropdownStyles(theme),
209
+ option: createOptionStyles(theme),
210
+ });
211
+
143
212
  return {
144
- container: {
145
- position: 'relative',
146
- variants: {
147
- // Spacing variants from FormInputStyleProps
148
- margin: buildMarginVariants(theme),
149
- marginVertical: buildMarginVerticalVariants(theme),
150
- marginHorizontal: buildMarginHorizontalVariants(theme),
151
- },
152
- },
213
+ // Extended main elements
214
+ ...extendedStyles,
215
+ // Minor utility styles (not extended)
153
216
  label: {
154
217
  fontSize: 14,
155
218
  fontWeight: '500',
156
219
  color: theme.colors.text.primary,
157
220
  marginBottom: 4,
158
221
  },
159
- trigger: buildDynamicTriggerStyles(theme),
160
222
  triggerContent: {
161
223
  flex: 1,
162
224
  flexDirection: 'row',
@@ -204,31 +266,6 @@ export const selectStyles = StyleSheet.create((theme: Theme) => {
204
266
  transform: 'rotate(180deg)',
205
267
  }
206
268
  },
207
- dropdown: {
208
- position: 'absolute',
209
- top: '100%',
210
- left: 0,
211
- right: 0,
212
- backgroundColor: theme.colors.surface.primary,
213
- borderRadius: 8,
214
- borderWidth: 1,
215
- borderStyle: 'solid',
216
- borderColor: theme.colors.border.primary,
217
- shadowColor: '#000',
218
- shadowOffset: { width: 0, height: 8 },
219
- shadowOpacity: 0.1,
220
- shadowRadius: 24,
221
- elevation: 8,
222
- zIndex: 9999,
223
- maxHeight: 240,
224
- minWidth: 200,
225
- overflow: 'hidden',
226
- _web: {
227
- border: `1px solid ${theme.colors.border.primary}`,
228
- boxShadow: '0 8px 24px rgba(0, 0, 0, 0.1), 0 4px 8px rgba(0, 0, 0, 0.06)',
229
- overflowY: 'auto',
230
- },
231
- },
232
269
  searchContainer: {
233
270
  padding: 8,
234
271
  borderBottomWidth: 1,
@@ -261,23 +298,6 @@ export const selectStyles = StyleSheet.create((theme: Theme) => {
261
298
  optionsList: {
262
299
  paddingVertical: 4,
263
300
  },
264
- option: {
265
- paddingHorizontal: 8,
266
- paddingVertical: 4,
267
- flexDirection: 'row',
268
- alignItems: 'center',
269
- minHeight: 36,
270
- _web: {
271
- display: 'flex',
272
- cursor: 'pointer',
273
- _hover: {
274
- backgroundColor: theme.colors.surface.secondary,
275
- },
276
- _active: {
277
- opacity: 0.8,
278
- },
279
- },
280
- },
281
301
  optionFocused: {
282
302
  backgroundColor: theme.interaction.focusedBackground,
283
303
  _web: {
@@ -176,12 +176,12 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
176
176
  };
177
177
 
178
178
  const containerWebProps = getWebProps([
179
- selectStyles.container,
179
+ (selectStyles.container as any)({}),
180
180
  style as any
181
181
  ]);
182
182
 
183
183
  const triggerWebProps = getWebProps([
184
- selectStyles.trigger({ type, intent }),
184
+ (selectStyles.trigger as any)({ type, intent }),
185
185
  ]);
186
186
 
187
187
  const handleClose = () => {
@@ -247,7 +247,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
247
247
  zIndex={1000}
248
248
  >
249
249
  <div
250
- {...getWebProps([selectStyles.dropdown])}
250
+ {...getWebProps([(selectStyles.dropdown as any)({})])}
251
251
  style={{
252
252
  maxHeight: maxHeight,
253
253
  position: 'relative',
@@ -282,7 +282,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
282
282
  aria-selected={option.value === value}
283
283
  onMouseEnter={() => setFocusedIndex(index)}
284
284
  {...getWebProps([
285
- selectStyles.option,
285
+ (selectStyles.option as any)({}),
286
286
  isFocused && selectStyles.optionFocused,
287
287
  option.disabled && selectStyles.optionDisabled,
288
288
  ])}
@@ -305,7 +305,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
305
305
  })}
306
306
 
307
307
  {filteredOptions.length === 0 && (
308
- <div {...getWebProps([selectStyles.option])} style={{ cursor: 'default' }}>
308
+ <div {...getWebProps([(selectStyles.option as any)({})])} style={{ cursor: 'default' }}>
309
309
  <span {...getWebProps([selectStyles.optionText])}>
310
310
  No options found
311
311
  </span>
@@ -1,5 +1,6 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, StylesheetStyles} from '@idealyst/theme';
2
+ import { Theme, StylesheetStyles } from '@idealyst/theme';
3
+ import { applyExtensions } from '../extensions/applyExtension';
3
4
 
4
5
  type SkeletonShape = 'rectangle' | 'rounded' | 'circle';
5
6
  type SkeletonAnimation = 'pulse' | 'wave' | 'none';
@@ -34,14 +35,11 @@ function createShapeVariants(theme: Theme) {
34
35
  } as const;
35
36
  }
36
37
 
37
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
38
- // transform on native cannot resolve function calls to extract variant structures.
39
- // @ts-ignore - TS language server needs restart to pick up theme structure changes
40
- export const skeletonStyles = StyleSheet.create((theme: Theme) => {
41
- return {
42
- skeleton: {
38
+ // Style creators for extension support
39
+ function createSkeletonStyles(theme: Theme) {
40
+ return () => ({
43
41
  backgroundColor: theme.colors.surface.tertiary,
44
- overflow: 'hidden',
42
+ overflow: 'hidden' as const,
45
43
  variants: {
46
44
  shape: createShapeVariants(theme),
47
45
  animation: {
@@ -50,10 +48,24 @@ export const skeletonStyles = StyleSheet.create((theme: Theme) => {
50
48
  none: {},
51
49
  },
52
50
  },
53
- },
54
- group: {
55
- display: 'flex',
56
- flexDirection: 'column',
57
- },
58
- };
51
+ });
52
+ }
53
+
54
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
55
+ // transform on native cannot resolve function calls to extract variant structures.
56
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
57
+ export const skeletonStyles = StyleSheet.create((theme: Theme) => {
58
+ // Apply extensions to main visual elements
59
+ const extended = applyExtensions('Skeleton', theme, {
60
+ skeleton: createSkeletonStyles(theme),
61
+ });
62
+
63
+ return {
64
+ ...extended,
65
+ // Minor utility styles (not extended)
66
+ group: {
67
+ display: 'flex',
68
+ flexDirection: 'column',
69
+ },
70
+ };
59
71
  });
@@ -193,7 +193,7 @@ const Slider = forwardRef<View, SliderProps>(({
193
193
  if (!icon) return null;
194
194
 
195
195
  if (typeof icon === 'string' && isIconName(icon)) {
196
- const iconStyle = sliderStyles.thumbIcon({ intent });
196
+ const iconStyle = (sliderStyles.thumbIcon as any)({ intent });
197
197
  return (
198
198
  <MaterialCommunityIcons
199
199
  name={icon}
@@ -206,8 +206,12 @@ const Slider = forwardRef<View, SliderProps>(({
206
206
  return icon;
207
207
  };
208
208
 
209
+ // Get dynamic styles
210
+ const containerStyle = (sliderStyles.container as any)({});
211
+ const trackStyle = (sliderStyles.track as any)({});
212
+
209
213
  return (
210
- <View ref={ref} nativeID={id} style={[sliderStyles.container, style]} testID={testID} {...nativeA11yProps}>
214
+ <View ref={ref} nativeID={id} style={[containerStyle, style]} testID={testID} {...nativeA11yProps}>
211
215
  {showValue && (
212
216
  <View style={sliderStyles.valueLabel as any}>
213
217
  <Text>{value}</Text>
@@ -217,7 +221,7 @@ const Slider = forwardRef<View, SliderProps>(({
217
221
  <View style={sliderStyles.sliderWrapper}>
218
222
  <GestureDetector gesture={composedGesture}>
219
223
  <View
220
- style={sliderStyles.track}
224
+ style={trackStyle}
221
225
  onLayout={(e) => {
222
226
  const width = e.nativeEvent.layout.width;
223
227
  trackWidth.value = width;
@@ -226,7 +230,7 @@ const Slider = forwardRef<View, SliderProps>(({
226
230
  >
227
231
  {/* Filled track */}
228
232
  <Animated.View
229
- style={[sliderStyles.filledTrack({ intent }), filledTrackAnimatedStyle]}
233
+ style={[(sliderStyles.filledTrack as any)({ intent }), filledTrackAnimatedStyle]}
230
234
  />
231
235
 
232
236
  {/* Marks */}
@@ -262,7 +266,7 @@ const Slider = forwardRef<View, SliderProps>(({
262
266
  {/* Thumb */}
263
267
  <Animated.View
264
268
  style={[
265
- sliderStyles.thumb({ intent, disabled }),
269
+ (sliderStyles.thumb as any)({ intent, disabled }),
266
270
  {
267
271
  // Manual positioning/sizing for native layout
268
272
  position: 'absolute',