Open AAfify1 opened 4 years ago
There's unfortunately no good way to do this right now, but I'll keep this shortcoming in mind and see if we can solve this in a nice way in future versions.
Okay thank you!
I also had issues figuring out a way to make my own button. What I had to do for my button label to match the button variant style (for example, I want the primary button to be blue and the text white) is to have the same variant name in both components. You could define your button properties as following:
type Props = VariantProps<Theme, "buttonVariants"> & {
label: string;
onPress?: () => void;
};
So, for example, you can create a ButtonContainer component,
const ButtonContainer = createRestyleComponent<
VariantProps<Theme, "buttonVariants"> & React.ComponentProps<typeof Box>,
Theme
>([createVariant({ themeKey: "buttonVariants" })], Box);
and use it in your Button
component
<ButtonContainer variant={variant}> <Text variant={variant}>{label}</Text></ButtonContainer>
Instead of just passing the VariantProps
you could also pass your ButtonContainer props, to be more flexible
So at the end you could have:
type Props = React.ComponentProps<typeof ButtonContainer> & {
label: string;
onPress?: () => void;
};
const Button = ({ label, variant, onPress, ...rest }: Props) => (
<ButtonContainer variant={variant} {...rest}>
<Touchableopacity onPress={onPress}>
<Text variant={variant}>
{label}
</Text>
</Touchableopacity>
</ButtonContainer>
)
The only downside i see is having to manually maintain the same variant names in both a Button container (just a box with variants) and the Text component, but it works... There might be some coding mistakes since, but that's the idea Hope this helps someone looking for a way to define a button component
@Charly6596 can you please add your theme, I have the same problem, having hard time understanding how did you define your variants
@minawalphonce sure thing, here's a simplified version of my theme
const theme = createTheme({
colors: {
buttonPrimary: palette.blue,
buttonPrimaryForeground: palette.white,
buttonSecondary: palette.white,
buttonSecondaryForeground: palette.darkBlue,
},
spacing: {}, // omitted
breakpoints: {}, // omitted
textVariants: {
buttonPrimary: {
color: "buttonPrimaryForeground",
fontSize: 16,
},
buttonSecondary: {
color: "buttonSecondaryForeground",
fontSize: 16,
},
},
buttonVariants: {
buttonPrimary: {
backgroundColor: "buttonPrimary",
padding: "m",
},
buttonSecondary: {
backgroundColor: "buttonSecondary",
padding: "m",
},
},
});
The ability to define styles for subcomponents when using variants would be a great addition to the library.
Not being able to do so feels limitating when trying to build component like : <Message variant="error">Something went wrong</Message>
, that renders a Box
and a Text
under the hood in the appropriate styles.
These kind of components, where you don't have to thing too much about the moving parts, are a good practice (IMO), resulting in a low overhead for developers when building screens, and overall more consistent styles.
@Charly6596 workaround works but it breaks the colocation of styles, which sounds like hard to maintain in the long run with hundreds of components.
I had this problem too and solved it by accessing the variant prop from useTheme
like so:
import {
SpacingProps,
createRestyleComponent,
VariantProps,
createVariant,
useTheme,
spacing,
} from "@shopify/restyle";
import React from "react";
import { TouchableOpacity } from "react-native";
import { Text } from "./Text";
import { Theme } from "./theme";
export const BaseButton = createRestyleComponent<
VariantProps<Theme, "buttonVariants"> &
SpacingProps<Theme> &
React.ComponentProps<typeof TouchableOpacity>,
Theme
>([createVariant({ themeKey: "buttonVariants" }), spacing], TouchableOpacity);
type ButtonProps = React.ComponentProps<typeof BaseButton> & {
label: string;
};
export const Button = ({ label, ...rest }: ButtonProps) => {
const theme = useTheme<Theme>();
return (
<BaseButton {...rest}>
<Text
variant="buttonLabel"
color={theme.buttonVariants[rest.variant].color}
>
{label}
</Text>
</BaseButton>
);
};
Theme:
export const theme = createTheme({
...
buttonVariants: {
primary: {
backgroundColor: "primary",
borderColor: "primary",
color: "white",
},
outlined: {
borderColor: "primary",
color: "secondary",
},
},
...
That buttonVariants[rest.variant]
still throws a TS error about index types that I'm not sure how to fix though.
That
buttonVariants[rest.variant]
still throws a TS error about index types that I'm not sure how to fix though.
@vonkanehoffen
Have you tried to force with type with as
like
// not sure if it will work but something like that 😅
color={theme.buttonVariants[rest.variant].color as ColorProps<Theme>}
That
buttonVariants[rest.variant]
still throws a TS error about index types that I'm not sure how to fix though.@vonkanehoffen Have you tried to force with type with
as
like// not sure if it will work but something like that 😅 color={theme.buttonVariants[rest.variant].color as ColorProps<Theme>}
Even if we add as there is a typescript issue with color
Type 'ColorProps<{ breakpoints: { phone: number; tablet: number; desktop: number; }; spacing: { none: number; '3xs': number; xxs: number; xs: number; s: number; m: number; l: number; xl: number; xxl: number; '3xl': number; '4xl': number; '6xl': number; '8xl': number; }; ... 5 more ...; buttonVariants: { ...; }; }>' is not assignable to type 'ResponsiveValue<"dark" | "primaryLow" | "primary" | "primaryHigh" | "primaryTransparent" | "blue" | "green" | "orange" | "orangeLow" | "red" | "red2" | "red3" | "purple" | "redLow" | "yellow" | ... 26 more ... | "shadowEnd", { ...; }> | undefined'.
I have tried with another property (fontSize) and it works fine with it. It seems to be specific to color
Here is how I fix the TS issue
<Text
variant="itemTitle"
fontSize={theme.buttonVariants[variant as keyof Theme['buttonVariants']].fontSize}
color={theme.buttonVariants[variant as keyof Theme['buttonVariants']].color as keyof Theme['colors']}>
{children}
</Text>
Here is how I fix the TS issue
<Text variant="itemTitle" fontSize={theme.buttonVariants[variant as keyof Theme['buttonVariants']].fontSize} color={theme.buttonVariants[variant as keyof Theme['buttonVariants']].color as keyof Theme['colors']}> {children} </Text>
I had some trouble with the fontWeight
prop and solved like this:
fontWeight={
theme.buttonVariants[variant as keyof Theme['buttonVariants']]
.fontWeight as keyof TypographyProps<Theme>['fontWeight']
}
i did something like this :
what do you think ?
Below is my custom button component.
I am trying to change the Text color based on the chosen variant, how can I access the restyle 'color' property directly from the props rather than using the style attribute. So for example I want to use
<Text variant="body" color= {props.color}>
which maps to thecolor
specified in the variant e.gprimrayDark
. Thanks!