Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using styles within custom components #55

Open
AAfify1 opened this issue Oct 1, 2020 · 12 comments
Open

Using styles within custom components #55

AAfify1 opened this issue Oct 1, 2020 · 12 comments

Comments

@AAfify1
Copy link

AAfify1 commented Oct 1, 2020

Below is my custom button component.

const Button = ({onPress, label, ...rest}: Props) => {
  const props = useRestyle([buttonVariants as any], rest);

  return (
    <TouchableOpacity onPress={onPress}>
      <Box alignItems="center" {...props}>
        <Text variant="body" style={{color: props.style[0]?.color }}>
          {label}
        </Text>
      </Box>
    </TouchableOpacity>
  );
};

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 the color specified in the variant e.g primrayDark. Thanks!

@JoelBesada
Copy link
Contributor

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.

@AAfify1
Copy link
Author

AAfify1 commented Oct 4, 2020

Okay thank you!

@Charly6596
Copy link
Contributor

Charly6596 commented Oct 8, 2020

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

@minawalphonce
Copy link

@Charly6596 can you please add your theme, I have the same problem, having hard time understanding how did you define your variants

@Charly6596
Copy link
Contributor

@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",
    },
  },
});

@clementoriol
Copy link

clementoriol commented Mar 18, 2022

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.

@vonkanehoffen
Copy link

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.

@flexbox
Copy link

flexbox commented May 23, 2022

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>} 

@mlecoq
Copy link
Contributor

mlecoq commented Jul 25, 2022

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

@mlecoq
Copy link
Contributor

mlecoq commented Jul 25, 2022

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>

@nemethricsi
Copy link

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']
}

@lucas-garrido
Copy link

i did something like this :
image

what do you think ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants