Skip to content

Commit 0cbf803

Browse files
committed
feat: added new fucntionality to style react native components with design tokens
1 parent 268ce98 commit 0cbf803

File tree

1 file changed

+64
-4
lines changed

1 file changed

+64
-4
lines changed

packages/jacaranda/src/index.ts

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
// }
4141
// });
4242

43+
import React, { type ComponentType, type ReactNode } from 'react';
4344
import { ImageStyle, TextStyle, ViewStyle, StyleProp } from 'react-native';
4445

4546
// Define a type that removes token strings from style properties
@@ -91,10 +92,7 @@ export type OmitUndefined<T> = T extends undefined ? never : T;
9192
type OptionalIfHasDefault<Props, Defaults> = Omit<Props, keyof Defaults> &
9293
Partial<Pick<Props, Extract<keyof Defaults, keyof Props>>>;
9394

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;
9896

9997
/**
10098
* Creates a function that generates styles based on variants
@@ -185,9 +183,21 @@ type TokenConfig = {
185183
[K in keyof AllowedTokenCategories]?: Record<string, AllowedTokenCategories[K]>;
186184
};
187185

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+
188197
interface CreateTokensReturn {
189198
sva: typeof styles;
190199
tokens: TokenConfig;
200+
styled: StyledFunction;
191201
}
192202

193203
// Helper to resolve token references in style objects
@@ -266,8 +276,58 @@ export function defineTokens<T extends TokenConfig>(tokenConfig: T): CreateToken
266276
});
267277
};
268278

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+
269328
return {
270329
sva,
271330
tokens,
331+
styled,
272332
};
273333
}

0 commit comments

Comments
 (0)