|
40 | 40 | // } |
41 | 41 | // }); |
42 | 42 |
|
| 43 | +import React, { type ComponentType, type ReactNode } from 'react'; |
43 | 44 | import { ImageStyle, TextStyle, ViewStyle, StyleProp } from 'react-native'; |
44 | 45 |
|
45 | 46 | // Define a type that removes token strings from style properties |
@@ -91,10 +92,7 @@ export type OmitUndefined<T> = T extends undefined ? never : T; |
91 | 92 | type OptionalIfHasDefault<Props, Defaults> = Omit<Props, keyof Defaults> & |
92 | 93 | Partial<Pick<Props, Extract<keyof Defaults, keyof Props>>>; |
93 | 94 |
|
94 | | -export type VariantProps<Component extends <T>(arg: T) => ReturnType<Component>> = Omit< |
95 | | - OmitUndefined<Parameters<Component>[0]>, |
96 | | - 'style' |
97 | | ->; |
| 95 | +export type VariantProps<T> = T extends (props?: infer P) => StyleProp<ResolvedStyle> ? P : never; |
98 | 96 |
|
99 | 97 | /** |
100 | 98 | * Creates a function that generates styles based on variants |
@@ -185,9 +183,21 @@ type TokenConfig = { |
185 | 183 | [K in keyof AllowedTokenCategories]?: Record<string, AllowedTokenCategories[K]>; |
186 | 184 | }; |
187 | 185 |
|
| 186 | +// Helper type to extract component props |
| 187 | +type ComponentProps<T> = T extends ComponentType<infer P> ? P : never; |
| 188 | + |
| 189 | +type StyledFunction = <C extends ComponentType<any>>( |
| 190 | + Component: C, |
| 191 | +) => <P extends object = {}>( |
| 192 | + styleObject: StyleObject, |
| 193 | +) => ComponentType< |
| 194 | + P & Omit<ComponentProps<C>, 'style'> & { style?: StyleProp<ViewStyle | TextStyle | ImageStyle> } |
| 195 | +>; |
| 196 | + |
188 | 197 | interface CreateTokensReturn { |
189 | 198 | sva: typeof styles; |
190 | 199 | tokens: TokenConfig; |
| 200 | + styled: StyledFunction; |
191 | 201 | } |
192 | 202 |
|
193 | 203 | // Helper to resolve token references in style objects |
@@ -266,8 +276,58 @@ export function defineTokens<T extends TokenConfig>(tokenConfig: T): CreateToken |
266 | 276 | }); |
267 | 277 | }; |
268 | 278 |
|
| 279 | + /** |
| 280 | + * Styled function for creating styled components with token-aware styles |
| 281 | + */ |
| 282 | + const styled: StyledFunction = <C extends ComponentType<any>>(Component: C) => { |
| 283 | + return <P extends object = {}>(styleObject: StyleObject) => { |
| 284 | + // Resolve tokens in the style object |
| 285 | + const resolvedStyle = resolveTokens(styleObject, tokens); |
| 286 | + |
| 287 | + // Create and return a new component that applies the resolved styles |
| 288 | + const StyledComponent: ComponentType< |
| 289 | + P & |
| 290 | + Omit<ComponentProps<C>, 'style'> & { |
| 291 | + style?: StyleProp<ViewStyle | TextStyle | ImageStyle>; |
| 292 | + } |
| 293 | + > = (props) => { |
| 294 | + const { |
| 295 | + children, |
| 296 | + style: propStyle, |
| 297 | + ...rest |
| 298 | + } = props as { |
| 299 | + children?: ReactNode; |
| 300 | + style?: StyleProp<ViewStyle | TextStyle | ImageStyle>; |
| 301 | + } & Record<string, unknown>; |
| 302 | + |
| 303 | + // Merge the resolved style with any style passed in props |
| 304 | + const mergedStyle = propStyle |
| 305 | + ? Array.isArray(propStyle) |
| 306 | + ? [resolvedStyle, ...propStyle] |
| 307 | + : [resolvedStyle, propStyle] |
| 308 | + : resolvedStyle; |
| 309 | + |
| 310 | + // We need to cast here to handle the component props correctly |
| 311 | + const componentProps = { |
| 312 | + ...rest, |
| 313 | + style: mergedStyle, |
| 314 | + } as ComponentProps<C>; |
| 315 | + |
| 316 | + // Use createElement instead of JSX to avoid syntax issues |
| 317 | + return React.createElement(Component, componentProps, children); |
| 318 | + }; |
| 319 | + |
| 320 | + // Set display name for better debugging |
| 321 | + const componentName = Component.displayName || Component.name || 'Component'; |
| 322 | + StyledComponent.displayName = `Styled(${componentName})`; |
| 323 | + |
| 324 | + return StyledComponent; |
| 325 | + }; |
| 326 | + }; |
| 327 | + |
269 | 328 | return { |
270 | 329 | sva, |
271 | 330 | tokens, |
| 331 | + styled, |
272 | 332 | }; |
273 | 333 | } |
0 commit comments