@idealyst/components 1.1.5 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/package.json +8 -3
  2. package/src/Accordion/Accordion.native.tsx +15 -9
  3. package/src/Accordion/Accordion.styles.tsx +193 -168
  4. package/src/Accordion/Accordion.web.tsx +12 -7
  5. package/src/ActivityIndicator/ActivityIndicator.native.tsx +3 -2
  6. package/src/ActivityIndicator/ActivityIndicator.styles.tsx +22 -11
  7. package/src/ActivityIndicator/ActivityIndicator.web.tsx +2 -2
  8. package/src/Alert/Alert.native.tsx +11 -10
  9. package/src/Alert/Alert.styles.tsx +162 -253
  10. package/src/Alert/Alert.web.tsx +6 -10
  11. package/src/Avatar/Avatar.native.tsx +5 -2
  12. package/src/Avatar/Avatar.styles.tsx +48 -18
  13. package/src/Avatar/Avatar.web.tsx +2 -2
  14. package/src/Badge/Badge.native.tsx +2 -2
  15. package/src/Badge/Badge.styles.tsx +37 -16
  16. package/src/Badge/Badge.web.tsx +6 -6
  17. package/src/Breadcrumb/Breadcrumb.native.tsx +12 -5
  18. package/src/Breadcrumb/Breadcrumb.styles.tsx +59 -58
  19. package/src/Breadcrumb/Breadcrumb.web.tsx +13 -6
  20. package/src/Button/Button.native.tsx +39 -14
  21. package/src/Button/Button.styles.tsx +106 -208
  22. package/src/Button/Button.web.tsx +10 -8
  23. package/src/Card/Card.native.tsx +14 -6
  24. package/src/Card/Card.styles.tsx +64 -62
  25. package/src/Card/Card.web.tsx +5 -4
  26. package/src/Checkbox/Checkbox.native.tsx +7 -3
  27. package/src/Checkbox/Checkbox.styles.tsx +49 -25
  28. package/src/Checkbox/Checkbox.web.tsx +3 -3
  29. package/src/Chip/Chip.native.tsx +5 -5
  30. package/src/Chip/Chip.styles.tsx +71 -21
  31. package/src/Chip/Chip.web.tsx +5 -5
  32. package/src/Dialog/Dialog.native.tsx +10 -4
  33. package/src/Dialog/Dialog.styles.tsx +130 -90
  34. package/src/Dialog/Dialog.web.tsx +4 -4
  35. package/src/Divider/Divider.native.tsx +29 -42
  36. package/src/Divider/Divider.styles.tsx +138 -242
  37. package/src/Divider/Divider.web.tsx +17 -14
  38. package/src/Icon/Icon.native.tsx +11 -3
  39. package/src/Icon/Icon.styles.tsx +10 -4
  40. package/src/Image/Image.styles.tsx +53 -37
  41. package/src/Input/Input.native.tsx +6 -7
  42. package/src/Input/Input.styles.tsx +194 -174
  43. package/src/Input/Input.web.tsx +5 -8
  44. package/src/Link/Link.native.tsx +4 -1
  45. package/src/List/List.styles.tsx +79 -105
  46. package/src/List/ListItem.native.tsx +5 -3
  47. package/src/List/ListItem.web.tsx +4 -3
  48. package/src/Menu/Menu.native.tsx +1 -1
  49. package/src/Menu/Menu.styles.tsx +53 -37
  50. package/src/Menu/Menu.web.tsx +2 -2
  51. package/src/Menu/MenuItem.native.tsx +5 -3
  52. package/src/Menu/MenuItem.styles.tsx +68 -69
  53. package/src/Menu/MenuItem.web.tsx +16 -3
  54. package/src/Popover/Popover.native.tsx +1 -1
  55. package/src/Popover/Popover.styles.tsx +40 -29
  56. package/src/Popover/Popover.web.tsx +1 -1
  57. package/src/Pressable/Pressable.native.tsx +3 -1
  58. package/src/Pressable/Pressable.styles.tsx +20 -13
  59. package/src/Pressable/Pressable.web.tsx +1 -1
  60. package/src/Progress/Progress.native.tsx +15 -6
  61. package/src/Progress/Progress.styles.tsx +125 -85
  62. package/src/Progress/Progress.web.tsx +10 -9
  63. package/src/RadioButton/RadioButton.native.tsx +8 -3
  64. package/src/RadioButton/RadioButton.styles.tsx +44 -37
  65. package/src/RadioButton/RadioButton.web.tsx +3 -3
  66. package/src/SVGImage/SVGImage.styles.tsx +28 -16
  67. package/src/Screen/Screen.native.tsx +23 -13
  68. package/src/Screen/Screen.styles.tsx +57 -46
  69. package/src/Screen/Screen.web.tsx +1 -1
  70. package/src/Select/Select.native.tsx +11 -5
  71. package/src/Select/Select.styles.tsx +72 -52
  72. package/src/Select/Select.web.tsx +5 -5
  73. package/src/Skeleton/Skeleton.styles.tsx +26 -14
  74. package/src/Slider/Slider.native.tsx +9 -5
  75. package/src/Slider/Slider.styles.tsx +59 -48
  76. package/src/Slider/Slider.web.tsx +5 -5
  77. package/src/Switch/Switch.native.tsx +6 -2
  78. package/src/Switch/Switch.styles.tsx +46 -19
  79. package/src/Switch/Switch.web.tsx +4 -4
  80. package/src/TabBar/TabBar.native.tsx +23 -31
  81. package/src/TabBar/TabBar.styles.tsx +215 -371
  82. package/src/TabBar/TabBar.web.tsx +21 -33
  83. package/src/Table/Table.native.tsx +1 -1
  84. package/src/Table/Table.styles.tsx +11 -4
  85. package/src/Table/Table.web.tsx +1 -1
  86. package/src/Text/Text.native.tsx +3 -4
  87. package/src/Text/Text.styles.tsx +7 -1
  88. package/src/Text/Text.web.tsx +1 -1
  89. package/src/TextArea/TextArea.styles.tsx +90 -58
  90. package/src/Tooltip/Tooltip.native.tsx +2 -2
  91. package/src/Tooltip/Tooltip.styles.tsx +21 -12
  92. package/src/Tooltip/Tooltip.web.tsx +2 -2
  93. package/src/Video/Video.styles.tsx +39 -23
  94. package/src/View/View.native.tsx +4 -2
  95. package/src/View/View.styles.tsx +33 -22
  96. package/src/View/View.web.tsx +13 -2
  97. package/src/extensions/applyExtension.ts +210 -0
  98. package/src/extensions/extendComponent.ts +377 -0
  99. package/src/extensions/index.ts +102 -0
  100. package/src/extensions/types.ts +497 -0
  101. package/src/globals.ts +16 -0
  102. package/src/index.native.ts +4 -0
  103. package/src/index.ts +28 -0
  104. package/src/utils/accessibility/index.ts +3 -3
  105. package/src/utils/deepMerge.ts +54 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/components",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Shared component library for React and React Native",
5
5
  "documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/components#readme",
6
6
  "readme": "README.md",
@@ -25,6 +25,11 @@
25
25
  "require": "./src/index.ts",
26
26
  "types": "./src/index.ts"
27
27
  },
28
+ "./extensions": {
29
+ "import": "./src/extensions/index.ts",
30
+ "require": "./src/extensions/index.ts",
31
+ "types": "./src/extensions/index.ts"
32
+ },
28
33
  "./utils": {
29
34
  "import": "./src/utils/index.ts",
30
35
  "require": "./src/utils/index.ts",
@@ -51,7 +56,7 @@
51
56
  "publish:npm": "npm publish"
52
57
  },
53
58
  "peerDependencies": {
54
- "@idealyst/theme": "^1.1.5",
59
+ "@idealyst/theme": "^1.1.7",
55
60
  "@mdi/js": ">=7.0.0",
56
61
  "@mdi/react": ">=1.0.0",
57
62
  "@react-native-vector-icons/common": ">=12.0.0",
@@ -101,7 +106,7 @@
101
106
  }
102
107
  },
103
108
  "devDependencies": {
104
- "@idealyst/theme": "^1.1.5",
109
+ "@idealyst/theme": "^1.1.7",
105
110
  "@mdi/react": "^1.6.1",
106
111
  "@types/react": "^19.1.0",
107
112
  "react": "^19.1.0",
@@ -30,15 +30,19 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
30
30
  const iconRotation = useSharedValue(0);
31
31
  const [measuredHeight, setMeasuredHeight] = useState(0);
32
32
 
33
- // Apply item-specific variants
33
+ // Apply item-specific variants (for size, expanded, disabled)
34
34
  accordionStyles.useVariants({
35
- type,
36
- isLast,
37
35
  size,
38
36
  expanded: isExpanded,
39
37
  disabled: Boolean(item.disabled),
40
38
  });
41
39
 
40
+ // Get dynamic styles
41
+ const itemStyle = (accordionStyles.item as any)({ type, isLast });
42
+ const headerStyle = (accordionStyles.header as any)({});
43
+ const iconStyle = (accordionStyles.icon as any)({});
44
+ const contentStyle = (accordionStyles.content as any)({});
45
+
42
46
  // Animate height and icon rotation when expanded state changes
43
47
  useEffect(() => {
44
48
  contentHeight.value = withTiming(
@@ -74,9 +78,9 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
74
78
  };
75
79
 
76
80
  return (
77
- <View style={accordionStyles.item} testID={testID}>
81
+ <View style={itemStyle} testID={testID}>
78
82
  <TouchableOpacity
79
- style={accordionStyles.header}
83
+ style={headerStyle}
80
84
  onPress={onToggle}
81
85
  disabled={item.disabled}
82
86
  activeOpacity={0.7}
@@ -85,15 +89,15 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
85
89
  accessibilityState={{ expanded: isExpanded, disabled: item.disabled }}
86
90
  >
87
91
  <View style={accordionStyles.title}>
88
- <Text style={accordionStyles.header}>
92
+ <Text style={headerStyle}>
89
93
  {item.title}
90
94
  </Text>
91
95
  </View>
92
- <Animated.View style={[accordionStyles.icon, animatedIconStyle]}>
96
+ <Animated.View style={[iconStyle, animatedIconStyle]}>
93
97
  <MaterialCommunityIcons
94
98
  name="chevron-down"
95
99
  size={20}
96
- style={accordionStyles.icon}
100
+ style={iconStyle}
97
101
  />
98
102
  </Animated.View>
99
103
  </TouchableOpacity>
@@ -196,8 +200,10 @@ const Accordion = forwardRef<View, AccordionProps>(({
196
200
  });
197
201
  };
198
202
 
203
+ const containerStyle = (accordionStyles.container as any)({});
204
+
199
205
  return (
200
- <View ref={ref} nativeID={id} style={[accordionStyles.container, style]} testID={testID} {...nativeA11yProps}>
206
+ <View ref={ref} nativeID={id} style={[containerStyle, style]} testID={testID} {...nativeA11yProps}>
201
207
  {items.map((item, index) => (
202
208
  <AccordionItem
203
209
  key={item.id}
@@ -1,5 +1,5 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
- import { Theme, StylesheetStyles, CompoundVariants, Size} from '@idealyst/theme';
2
+ import { Theme, StylesheetStyles, Size } from '@idealyst/theme';
3
3
  import { buildSizeVariants } from '../utils/buildSizeVariants';
4
4
  import {
5
5
  buildGapVariants,
@@ -11,6 +11,7 @@ import {
11
11
  buildMarginHorizontalVariants,
12
12
  } from '../utils/buildViewStyleVariants';
13
13
  import { AccordionType } from './types';
14
+ import { applyExtensions } from '../extensions/applyExtension';
14
15
 
15
16
  type AccordionSize = Size;
16
17
 
@@ -24,6 +25,11 @@ type AccordionVariants = {
24
25
 
25
26
  export type ExpandedAccordionStyles = StylesheetStyles<keyof AccordionVariants>;
26
27
 
28
+ type ItemDynamicProps = {
29
+ type?: AccordionType;
30
+ isLast?: boolean;
31
+ };
32
+
27
33
  /**
28
34
  * Create type variants for container
29
35
  */
@@ -47,50 +53,52 @@ function createContainerTypeVariants(theme: Theme) {
47
53
  }
48
54
 
49
55
  /**
50
- * Create type variants for items
56
+ * Get item styles based on type and isLast
51
57
  */
52
- function createItemTypeVariants(theme: Theme) {
53
- return {
54
- standard: {
55
- borderBottomWidth: 1,
56
- borderBottomStyle: 'solid' as const,
57
- borderBottomColor: theme.colors.border.primary,
58
- },
59
- separated: {
60
- borderWidth: 1,
61
- borderStyle: 'solid' as const,
62
- borderColor: theme.colors.border.primary,
63
- borderRadius: 8,
64
- overflow: 'hidden' as const,
65
- },
66
- bordered: {
67
- borderBottomWidth: 1,
68
- borderBottomStyle: 'solid' as const,
69
- borderBottomColor: theme.colors.border.primary,
70
- },
71
- } as const;
58
+ function getItemTypeStyles(theme: Theme, type: AccordionType, isLast: boolean) {
59
+ switch (type) {
60
+ case 'standard':
61
+ return {
62
+ borderBottomWidth: isLast ? 0 : 1,
63
+ borderBottomStyle: 'solid' as const,
64
+ borderBottomColor: theme.colors.border.primary,
65
+ };
66
+ case 'separated':
67
+ return {
68
+ borderWidth: 1,
69
+ borderStyle: 'solid' as const,
70
+ borderColor: theme.colors.border.primary,
71
+ borderRadius: 8,
72
+ overflow: 'hidden' as const,
73
+ };
74
+ case 'bordered':
75
+ return {
76
+ borderBottomWidth: isLast ? 0 : 1,
77
+ borderBottomStyle: 'solid' as const,
78
+ borderBottomColor: theme.colors.border.primary,
79
+ };
80
+ default:
81
+ return {};
82
+ }
72
83
  }
73
84
 
74
85
  /**
75
- * Create compound variants for item (type + isLast)
86
+ * Create dynamic item styles
76
87
  */
77
- function createItemCompoundVariants(): CompoundVariants<keyof AccordionVariants> {
78
- return [
79
- {
80
- type: 'standard',
81
- isLast: true,
82
- styles: {
83
- borderBottomWidth: 0,
84
- },
85
- },
86
- {
87
- type: 'bordered',
88
- isLast: true,
89
- styles: {
90
- borderBottomWidth: 0,
88
+ function createItemStyles(theme: Theme) {
89
+ return ({ type = 'standard', isLast = false }: ItemDynamicProps) => {
90
+ const typeStyles = getItemTypeStyles(theme, type, isLast);
91
+ return {
92
+ display: 'flex' as const,
93
+ flexDirection: 'column' as const,
94
+ ...typeStyles,
95
+ variants: {
96
+ size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
97
+ expanded: { true: {}, false: {} },
98
+ disabled: { true: {}, false: {} },
91
99
  },
92
- },
93
- ];
100
+ } as const;
101
+ };
94
102
  }
95
103
 
96
104
  /**
@@ -124,154 +132,171 @@ function createContentInnerSizeVariants(theme: Theme) {
124
132
  }));
125
133
  }
126
134
 
127
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
128
- export const accordionStyles = StyleSheet.create((theme: Theme) => {
129
- return {
130
- container: {
131
- display: 'flex' as const,
132
- flexDirection: 'column' as const,
133
- variants: {
134
- size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
135
- type: createContainerTypeVariants(theme),
136
- expanded: { true: {}, false: {} },
137
- disabled: { true: {}, false: {} },
138
- isLast: { true: {}, false: {} },
139
- // Spacing variants from ContainerStyleProps
140
- gap: buildGapVariants(theme),
141
- padding: buildPaddingVariants(theme),
142
- paddingVertical: buildPaddingVerticalVariants(theme),
143
- paddingHorizontal: buildPaddingHorizontalVariants(theme),
144
- margin: buildMarginVariants(theme),
145
- marginVertical: buildMarginVerticalVariants(theme),
146
- marginHorizontal: buildMarginHorizontalVariants(theme),
147
- },
135
+ // Helper functions to create static styles wrapped in dynamic functions
136
+ function createContainerStyles(theme: Theme) {
137
+ return () => ({
138
+ display: 'flex' as const,
139
+ flexDirection: 'column' as const,
140
+ variants: {
141
+ size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
142
+ type: createContainerTypeVariants(theme),
143
+ expanded: { true: {}, false: {} },
144
+ disabled: { true: {}, false: {} },
145
+ isLast: { true: {}, false: {} },
146
+ // Spacing variants from ContainerStyleProps
147
+ gap: buildGapVariants(theme),
148
+ padding: buildPaddingVariants(theme),
149
+ paddingVertical: buildPaddingVerticalVariants(theme),
150
+ paddingHorizontal: buildPaddingHorizontalVariants(theme),
151
+ margin: buildMarginVariants(theme),
152
+ marginVertical: buildMarginVerticalVariants(theme),
153
+ marginHorizontal: buildMarginHorizontalVariants(theme),
148
154
  },
149
- item: {
150
- display: 'flex' as const,
151
- flexDirection: 'column' as const,
152
- variants: {
153
- size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
154
- type: createItemTypeVariants(theme),
155
- expanded: { true: {}, false: {} },
156
- disabled: { true: {}, false: {} },
157
- isLast: {
158
- true: {},
159
- false: {},
155
+ });
156
+ }
157
+
158
+ function createHeaderStyles(theme: Theme) {
159
+ return () => ({
160
+ display: 'flex' as const,
161
+ flexDirection: 'row' as const,
162
+ alignItems: 'center' as const,
163
+ justifyContent: 'space-between' as const,
164
+ width: '100%' as const,
165
+ backgroundColor: 'transparent' as const,
166
+ color: theme.colors.text.primary,
167
+ textAlign: 'left' as const,
168
+ variants: {
169
+ size: createHeaderSizeVariants(theme),
170
+ type: { standard: {}, separated: {}, bordered: {} },
171
+ expanded: {
172
+ true: {
173
+ fontWeight: '600' as const,
174
+ },
175
+ false: {
176
+ fontWeight: '500' as const,
160
177
  },
161
178
  },
162
- compoundVariants: createItemCompoundVariants(),
163
- },
164
- header: {
165
- display: 'flex' as const,
166
- flexDirection: 'row' as const,
167
- alignItems: 'center' as const,
168
- justifyContent: 'space-between' as const,
169
- width: '100%' as const,
170
- backgroundColor: 'transparent' as const,
171
- color: theme.colors.text.primary,
172
- textAlign: 'left' as const,
173
- variants: {
174
- size: createHeaderSizeVariants(theme),
175
- type: { standard: {}, separated: {}, bordered: {} },
176
- expanded: {
177
- true: {
178
- fontWeight: '600' as const,
179
- },
180
- false: {
181
- fontWeight: '500' as const,
179
+ disabled: {
180
+ true: {
181
+ opacity: 0.5,
182
+ _web: {
183
+ cursor: 'not-allowed' as const,
182
184
  },
183
185
  },
184
- disabled: {
185
- true: {
186
- opacity: 0.5,
187
- _web: {
188
- cursor: 'not-allowed' as const,
189
- },
190
- },
191
- false: {
192
- _web: {
193
- cursor: 'pointer' as const,
194
- _hover: {
195
- backgroundColor: theme.colors.surface.secondary,
196
- },
186
+ false: {
187
+ _web: {
188
+ cursor: 'pointer' as const,
189
+ _hover: {
190
+ backgroundColor: theme.colors.surface.secondary,
197
191
  },
198
192
  },
199
193
  },
200
- isLast: { true: {}, false: {} },
201
- } as const,
202
- _web: {
203
- border: 'none' as const,
204
- outline: 'none' as const,
205
- transition: 'background-color 0.2s ease' as const,
206
194
  },
195
+ isLast: { true: {}, false: {} },
196
+ } as const,
197
+ _web: {
198
+ border: 'none' as const,
199
+ outline: 'none' as const,
200
+ transition: 'background-color 0.2s ease' as const,
207
201
  },
208
- title: {
209
- flex: 1,
210
- variants: {
211
- size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
212
- type: { standard: {}, separated: {}, bordered: {} },
213
- expanded: { true: {}, false: {} },
214
- disabled: { true: {}, false: {} },
215
- isLast: { true: {}, false: {} },
216
- },
202
+ });
203
+ }
204
+
205
+ function createTitleStyles() {
206
+ return () => ({
207
+ flex: 1,
208
+ variants: {
209
+ size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
210
+ type: { standard: {}, separated: {}, bordered: {} },
211
+ expanded: { true: {}, false: {} },
212
+ disabled: { true: {}, false: {} },
213
+ isLast: { true: {}, false: {} },
217
214
  },
218
- icon: {
219
- display: 'flex' as const,
220
- alignItems: 'center' as const,
221
- justifyContent: 'center' as const,
222
- marginLeft: 8,
223
- color: theme.intents.primary.primary,
224
- variants: {
225
- size: createIconSizeVariants(theme),
226
- type: { standard: {}, separated: {}, bordered: {} },
227
- expanded: {
228
- true: {
229
- _web: {
230
- transform: 'rotate(180deg)' as const,
231
- },
215
+ });
216
+ }
217
+
218
+ function createIconStyles(theme: Theme) {
219
+ return () => ({
220
+ display: 'flex' as const,
221
+ alignItems: 'center' as const,
222
+ justifyContent: 'center' as const,
223
+ marginLeft: 8,
224
+ color: theme.intents.primary.primary,
225
+ variants: {
226
+ size: createIconSizeVariants(theme),
227
+ type: { standard: {}, separated: {}, bordered: {} },
228
+ expanded: {
229
+ true: {
230
+ _web: {
231
+ transform: 'rotate(180deg)' as const,
232
232
  },
233
- false: {
234
- _web: {
235
- transform: 'rotate(0deg)' as const,
236
- },
233
+ },
234
+ false: {
235
+ _web: {
236
+ transform: 'rotate(0deg)' as const,
237
237
  },
238
238
  },
239
- disabled: { true: {}, false: {} },
240
- isLast: { true: {}, false: {} },
241
- },
242
- _web: {
243
- transition: 'transform 0.2s ease' as const,
244
239
  },
240
+ disabled: { true: {}, false: {} },
241
+ isLast: { true: {}, false: {} },
245
242
  },
246
- content: {
247
- overflow: 'hidden' as const,
248
- variants: {
249
- size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
250
- type: { standard: {}, separated: {}, bordered: {} },
251
- expanded: {
252
- true: {
253
- maxHeight: 2000,
254
- },
255
- false: {
256
- maxHeight: 0,
257
- },
243
+ _web: {
244
+ transition: 'transform 0.2s ease' as const,
245
+ },
246
+ });
247
+ }
248
+
249
+ function createContentStyles() {
250
+ return () => ({
251
+ overflow: 'hidden' as const,
252
+ variants: {
253
+ size: { xs: {}, sm: {}, md: {}, lg: {}, xl: {} },
254
+ type: { standard: {}, separated: {}, bordered: {} },
255
+ expanded: {
256
+ true: {
257
+ maxHeight: 2000,
258
+ },
259
+ false: {
260
+ maxHeight: 0,
258
261
  },
259
- disabled: { true: {}, false: {} },
260
- isLast: { true: {}, false: {} },
261
- },
262
- _web: {
263
- transition: 'height 0.15s ease, padding 0.3s ease' as const,
264
262
  },
263
+ disabled: { true: {}, false: {} },
264
+ isLast: { true: {}, false: {} },
265
265
  },
266
- contentInner: {
267
- color: theme.colors.text.secondary,
268
- variants: {
269
- size: createContentInnerSizeVariants(theme),
270
- type: { standard: {}, separated: {}, bordered: {} },
271
- expanded: { true: {}, false: {} },
272
- disabled: { true: {}, false: {} },
273
- isLast: { true: {}, false: {} },
274
- },
266
+ _web: {
267
+ transition: 'height 0.15s ease, padding 0.3s ease' as const,
268
+ },
269
+ });
270
+ }
271
+
272
+ function createContentInnerStyles(theme: Theme) {
273
+ return () => ({
274
+ color: theme.colors.text.secondary,
275
+ variants: {
276
+ size: createContentInnerSizeVariants(theme),
277
+ type: { standard: {}, separated: {}, bordered: {} },
278
+ expanded: { true: {}, false: {} },
279
+ disabled: { true: {}, false: {} },
280
+ isLast: { true: {}, false: {} },
275
281
  },
282
+ });
283
+ }
284
+
285
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel transform on native cannot resolve function calls to extract variant structures.
286
+ export const accordionStyles = StyleSheet.create((theme: Theme) => {
287
+ // Apply extensions to main visual elements
288
+ const extended = applyExtensions('Accordion', theme, {
289
+ container: createContainerStyles(theme),
290
+ item: createItemStyles(theme),
291
+ header: createHeaderStyles(theme),
292
+ content: createContentStyles(),
293
+ icon: createIconStyles(theme),
294
+ });
295
+
296
+ return {
297
+ ...extended,
298
+ // Minor utility styles (not extended)
299
+ title: createTitleStyles()(),
300
+ contentInner: createContentInnerStyles(theme)(),
276
301
  };
277
302
  });
@@ -12,6 +12,7 @@ interface AccordionItemProps {
12
12
  isExpanded: boolean;
13
13
  onToggle: () => void;
14
14
  size: AccordionProps['size'];
15
+ isLast: boolean;
15
16
  testID?: string;
16
17
  headerId: string;
17
18
  panelId: string;
@@ -24,6 +25,7 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
24
25
  onToggle,
25
26
  type,
26
27
  size,
28
+ isLast,
27
29
  testID,
28
30
  headerId,
29
31
  panelId,
@@ -33,19 +35,21 @@ const AccordionItem: React.FC<AccordionItemProps> = ({
33
35
  const [contentHeight, setContentHeight] = useState(0);
34
36
  const chevronIconPath = resolveIconPath('chevron-down');
35
37
 
38
+ // Apply item-specific variants (for size, expanded, disabled)
36
39
  accordionStyles.useVariants({
37
40
  size,
38
- type,
39
41
  expanded: isExpanded,
40
42
  disabled: Boolean(item.disabled),
41
43
  });
42
44
 
43
- const itemProps = getWebProps([accordionStyles.item]);
44
- const headerProps = getWebProps([accordionStyles.header]);
45
+ // Get dynamic item style with type and isLast props
46
+ const itemStyle = (accordionStyles.item as any)({ type, isLast });
47
+ const itemProps = getWebProps([itemStyle]);
48
+ const headerProps = getWebProps([(accordionStyles.header as any)({})]);
45
49
  const titleProps = getWebProps([accordionStyles.title]);
46
- const iconProps = getWebProps([accordionStyles.icon]);
50
+ const iconProps = getWebProps([(accordionStyles.icon as any)({})]);
47
51
  const contentProps = getWebProps([
48
- accordionStyles.content,
52
+ (accordionStyles.content as any)({}),
49
53
  {
50
54
  height: isExpanded ? contentHeight : 0,
51
55
  overflow: 'hidden' as const,
@@ -193,7 +197,7 @@ const Accordion: React.FC<AccordionProps> = ({
193
197
  marginHorizontal,
194
198
  });
195
199
 
196
- const containerProps = getWebProps([accordionStyles.container, style as any]);
200
+ const containerProps = getWebProps([(accordionStyles.container as any)({}), style as any]);
197
201
 
198
202
  const toggleItem = (itemId: string, disabled?: boolean) => {
199
203
  if (disabled) return;
@@ -213,7 +217,7 @@ const Accordion: React.FC<AccordionProps> = ({
213
217
 
214
218
  return (
215
219
  <div {...containerProps} {...ariaProps} id={accordionId} data-testid={testID}>
216
- {items.map((item) => (
220
+ {items.map((item, index) => (
217
221
  <AccordionItem
218
222
  key={item.id}
219
223
  item={item}
@@ -221,6 +225,7 @@ const Accordion: React.FC<AccordionProps> = ({
221
225
  isExpanded={expandedItems.includes(item.id)}
222
226
  onToggle={() => toggleItem(item.id, item.disabled)}
223
227
  size={size}
228
+ isLast={index === items.length - 1}
224
229
  testID={`${testID}-item-${item.id}`}
225
230
  headerId={getHeaderId(item.id)}
226
231
  panelId={getPanelId(item.id)}
@@ -39,7 +39,8 @@ const ActivityIndicator = forwardRef<View, ActivityIndicatorProps>(({
39
39
  });
40
40
 
41
41
  // Call dynamic style with intent variant
42
- const spinnerStyle = activityIndicatorStyles.spinner({ intent });
42
+ const spinnerStyle = (activityIndicatorStyles.spinner as any)({ intent });
43
+ const containerStyle = (activityIndicatorStyles.container as any)({});
43
44
 
44
45
  // Get the color from styles or use custom color
45
46
  const indicatorColor = color || spinnerStyle.color;
@@ -47,7 +48,7 @@ const ActivityIndicator = forwardRef<View, ActivityIndicatorProps>(({
47
48
  return (
48
49
  <View
49
50
  style={[
50
- activityIndicatorStyles.container,
51
+ containerStyle,
51
52
  customSize && {
52
53
  width: customSize,
53
54
  height: customSize,
@@ -1,6 +1,7 @@
1
1
  import { StyleSheet } from 'react-native-unistyles';
2
2
  import { Theme, StylesheetStyles, Intent, Size} from '@idealyst/theme';
3
3
  import { buildSizeVariants } from '../utils/buildSizeVariants';
4
+ import { applyExtensions } from '../extensions/applyExtension';
4
5
 
5
6
  type ActivityIndicatorSize = Size;
6
7
  type ActivityIndicatorIntent = Intent;
@@ -62,14 +63,11 @@ function createSpinnerStyles(theme: Theme) {
62
63
  }
63
64
  }
64
65
 
65
- // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
66
- // transform on native cannot resolve function calls to extract variant structures.
67
- // @ts-ignore - TS language server needs restart to pick up theme structure changes
68
- export const activityIndicatorStyles = StyleSheet.create((theme: Theme) => {
69
- return {
70
- container: {
71
- alignItems: 'center',
72
- justifyContent: 'center',
66
+ // Style creators for extension support
67
+ function createContainerStyles(theme: Theme) {
68
+ return () => ({
69
+ alignItems: 'center' as const,
70
+ justifyContent: 'center' as const,
73
71
  variants: {
74
72
  size: createContainerSizeVariants(theme),
75
73
  animating: {
@@ -81,7 +79,20 @@ export const activityIndicatorStyles = StyleSheet.create((theme: Theme) => {
81
79
  },
82
80
  },
83
81
  },
84
- },
85
- spinner: createSpinnerStyles(theme),
86
- };
82
+ });
83
+ }
84
+
85
+ // Styles are inlined here instead of in @idealyst/theme because Unistyles' Babel
86
+ // transform on native cannot resolve function calls to extract variant structures.
87
+ // @ts-ignore - TS language server needs restart to pick up theme structure changes
88
+ export const activityIndicatorStyles = StyleSheet.create((theme: Theme) => {
89
+ // Apply extensions to main visual elements
90
+ const extended = applyExtensions('ActivityIndicator', theme, {
91
+ container: createContainerStyles(theme),
92
+ spinner: createSpinnerStyles(theme),
93
+ });
94
+
95
+ return {
96
+ ...extended,
97
+ };
87
98
  });