为样式化组件创建响应性道具

时间:2019-10-27 16:14:47

标签: javascript reactjs styled-components react-props react-component

我正在尝试为样式化组件创建响应式道具,如下所示。首先,我们有一个组件(比方说一个按钮):

<Button primary large>Click Me</Button>

此按钮将获得原色和较大尺寸(由主题文件确定)的背景颜色。

我现在要创建此按钮的响应式版本。这就是我希望的工作方式:

<Button 
  primary 
  large 
  mobile={{size: 'small', style: 'secondary'}}
  tablet={size: 'small'}} 
  widescreen={{style: 'accent'}}
>
  Click Me
</Button>

我现在有一个相同的按钮,但是样式和大小因屏幕大小而异。

现在,我已经开始使用它了-但是它涉及很多重复的代码。这是一个看起来像的例子:

const Button = styled('button')(
  ({
    mobile,
    tablet,
    tabletOnly,
    desktop,
    widescreen
  }) => css`

      ${mobile &&
      css`
        @media screen and (max-width: ${theme.breakpoints.mobile.max}) {
          background-color: ${colors[mobile.style] || mobile.style};
          border: ${colors[mobile.style] || mobile.style};
          border-radius: ${radii[mobile.radius] || mobile.radius};
          color: ${mobile.style && rc(colors[mobile.style] || mobile.style)};

        }
      `}

    ${tablet &&
      css`
        @media screen and (min-width: ${theme.breakpoints.tablet.min}), print {
          background-color: ${colors[tablet.style] || tablet.style};
          border: ${colors[tablet.style] || tablet.style};
          border-radius: ${radii[tablet.radius] || tablet.radius};
          color: ${tablet.style && rc(colors[tablet.style] || tablet.style)};
        }
      `}

    ${tabletOnly &&
      css`
        @media screen and (min-width: ${theme.breakpoints.mobile.min}) and (max-width: ${theme.breakpoints.tablet.max}) {
          background-color: ${colors[tabletOnly.style] || tabletOnly.style};
          border: ${colors[tabletOnly.style] || tabletOnly.style};
          border-radius: ${radii[tabletOnly.radius] || tabletOnly.radius};
          color: ${tabletOnly.style &&
            rc(colors[tabletOnly.style] || tabletOnly.style)};
        }
      `}
`

我正在寻找一种简化此代码的方法。基本上,我只想编写CSS样式一次,然后基于这样的查询对象生成不同的道具和媒体查询:

const mediaQueries = {
  mobile: {
    min: '0px',
    max: '768px'
  },
  tablet: {
    print: true,
    min: '769px',
    max: '1023px'
  },
  desktop: {
    min: '1024px',
    max: '1215px'
  },
  widescreen: {
    min: '1216px',
    max: '1407px'
  },
  fullhd: {
    min: '1408px',
    max: null
  }
}

我想我应该能够创建一个遍历mediaQueries对象的函数,并为每次迭代插入适当的CSS。但是,我似乎无法弄清楚该怎么做。

关于如何执行此操作的任何想法?

此外,在此先感谢您提供的任何帮助。

2 个答案:

答案 0 :(得分:2)

也许您正在寻找这样的东西:

import { css } from "styled-components";

//mobile first approach min-width
const screenSizes = {
  fullhd: 1408,
  widescreen: 1215,
  desktop: 1023,
  tablet: 768,
  mobile: 0
}
const media = Object
    .keys(screenSizes)
    .reduce((acc, label) => {
        acc[label] = (...args) => css`
            @media (min-width: ${screenSizes[label] / 16}rem) {
                ${css(...args)}
            }
        `
        return acc
    }, {});

然后您就可以像这样导入和使用:

import media from './media'
const button = styled.button`
   ${({large , small})=> media.mobile`
      color: red;
      font-size: ${large ? '2em' : '1em'};
   `}
`

这里还有一些进一步的阅读,包括与主题配合使用:

Media queries in styled-components

使用道具:

从上方使用相同的媒体查询对象:

创建一个辅助函数,以将样式对象格式化为css字符串:

const formatCss = (styleObject) => {
    return JSON.stringify(styleObject)
        .replace(/[{}"']/g,'')
        .replace(/,/g,';') 
        + ';'
}

创建另一个帮助器函数以映射样式并通过映射其键并使用括号表示法动态添加查询来生成查询:

const mapQueries = (myQueries) =>{
    return Object.keys(myQueries).map(key=> media[key]`
        ${formatCss(myQueries[key])}
    `)
}

在样式组件中:

export const Button = styled.button`
    ${({myQueries}) => !myQueries ? '' : mapQueries(myQueries)}
`

最后向您的组件添加myQueries道具,如下所示(为简单起见,请使用css-formatted键而不是javascriptFormatted键)

<Button myQueries={{
    mobile:{ color:'red' },
    tablet:{ color:'blue', "background-color":'green'},
    desktop:{ height:'10rem' , width:'100%'}
}}>Button</Button>

答案 1 :(得分:0)

要遍历所有媒体查询,您可以创建类似于以下功能:

import { css } from "styled-components";

const sizes = {
  desktop: 992,
  tablet: 768,
  phone: 576
};

// Iterate through the sizes and create a media template
const media = Object.keys(sizes).map(screenLabel => {
  return {
    query: (...args) => css`
      @media (max-width: ${sizes[screenLabel] / 16}em) {
        ${css(...args)}
      }
    `,
    screenLabel
  };
});

export default media;

要在组件中使用:

import media from "./media";

// The labels for this has to be same as the ones in sizes object
const colors = {
  phone: "red",
  tablet: "yellow",
  desktop: "green"
};

const Heading = styled.h2`
  color: blue;

  ${media.map(
    ({ query, screenLabel }) => query`
    color: ${colors[screenLabel]};
  `
  )}
`;