zoukankan      html  css  js  c++  java
  • [Recompose] When nesting affects Style

    In CSS we use the descendant selector to style elements based on their nesting. Thankfully in React we don't need to consider this most of the time because this nesting of elements happens within the bounds of a single component.

    However occasionally the nesting of components affects the styles. In these rare cases we can use context to influence styles yielding a user friendly api to our components.

    The html structure should looks like:

                        <ButtonGroup isVertical>
                            <Button element={'a'}>Click</Button>
                            <Button element={'a'}>Click</Button>
                        </ButtonGroup>

    So what ButtonGroup should do is affect its child elements alignments and styling (marign or padding).

    So if html is like:

                        <ButtonGroup>
                            <Button element={'a'}>Click</Button>
                            <Button element={'a'}>Click</Button>
                        </ButtonGroup>

    It should looks like:

    In the article, only point out 3 important thing, so rest of stuff, go to the github.

    1. Styling ButtonGroup component itself, add padding it.

    We have a default theme.js file to config the theme:

    export default {
        color: {
            keyColor: '#3f8bae',
            textLight: '#fff',
        },
        number: {
            buttonRadius: 5,
            buttonGroupSpace: 6,
        },
        string: {
            mainFontFamily: 'sans-serif'
        }
    }

    'buttonGroupSpace' is the one to control the padding for ButtonGroup.

    And we have the function to modify style according to the theme ready in hocs.js:

    export const themeStyle = mapThemeToStyle => mapProps(
        props => {
            const { theme, style } = props;
    
            return {
                ...props,
                style: [
                    mapThemeToStyle(theme, props),
                    style
                ]
            };
        }
    );

    So what we need to do is give 'mapThemeToStyle' fucntion to enable it modify the style according to the theme.

    ButtonGroup.js:

    import React, {PropTypes} from 'react';
    
    import {
        addStyle,
        getTheme,
        themeStyle
    } from './hocs';
    import {
        setDisplayName,
        compose
    } from 'recompose';
    import Radium from 'radium';
    
    const mapThemeToStyle = ({number}, porps) => ({
        padding: (number.buttonGroupSpace || 6) * 1
    });
    
    const ButtonGroup = ({ children, ...rest }) => (
        <div {...rest}>
            {children}
        </div>
    );
    
    const enhance = compose(
        setDisplayName('ButtonGroup'),
        getTheme,
        themeStyle(mapThemeToStyle),
        addStyle({
            padding: 6,
            display: 'flex'
                 }),
        Radium
    );
    
    export default enhance(ButtonGroup);

    Notice that  'themeStyle' can override 'addStyle' function, 'compose' read from buttom to top. 

    2. Pass context down from ButtonGroup to Button.

    For the Buttons inside ButtonGroup, we want each has some margin instead of stick with each other. So we need one way to tell whether the Buttons are inside ButtonGroup or not. 

    One way is to use Context. From ButtonGroup we provide a context called 'buttonGroup', boolean value. 

    We can use recompose's withContext method:

    import React, {PropTypes} from 'react';
    
    import {
        addStyle,
        getTheme,
        themeStyle
    } from './hocs';
    import {
        setDisplayName,
        withContext,
        compose
    } from 'recompose';
    import Radium from 'radium';
    
    const mapThemeToStyle = ({number}, porps) => ({
        padding: (number.buttonGroupSpace || 6) * 1
    });
    
    const ButtonGroup = ({ children, ...rest }) => (
        <div {...rest}>
            {children}
        </div>
    );
    
    const enhance = compose(
        setDisplayName('ButtonGroup'),
        getTheme,
        themeStyle(mapThemeToStyle),
        withContext(
            {buttonGroup: PropTypes.bool}, // define the context type
            (props) => ({buttonGroup: true}) // set the value of context
        ),
        addStyle({
            padding: 60,
            display: 'flex'
                 }),
        Radium
    );
    
    export default enhance(ButtonGroup);

    Now, because the concept of 'Context' is for re-useable. We put 'buttonGroup' context into hocs.js:

    import {
        getContext
    } from 'recompose';
    
    export const getButtonGroup = getContext({
        buttonGroup: PropTypes.bool
                                             });

    It uses 'getContext' from recompose lib. 

    Now, in the Button.js, we can get the context on props:

    import React from 'react';
    import {
        mapProps,
        compose,
        defaultProps,
        setDisplayName,
        componentFromProp
    } from 'recompose';
    import Radium from 'radium';
    
    import {
        getTheme,
        themeStyle,
        addStyle,
        getButtonGroup
    } from './hocs';
    
    const mapThemeToStyle = ({
                                color,
                                number,
                                string
                             }, props) => {
        return {
            ...(color.keyColor &&
                {backgroundColor: color.keyColor} || {}
            ),
            ...(props.buttonGroup &&
                {margin: number.buttonGroupSpace} || {}
            ),
            color: color.textLight,
            borderRadius: number.buttonRadius,
            fontFamily: string.mainFontFamily
        };
    };
    
    const style = {
        backgroundColor: 'red',
        borderWidth: 0,
        borderStyle: 'solid',
        boxSizing: 'border-box',
        fontFamily: 'sans-serif',
        fontSize: 18,
        borderRadius: 3,
        fontWeight: 100,
        padding: 12,
        verticalAlign: 'middle',
        whiteSpace: 'nowrap',
        color: 'white',
        alignItems: 'center',
        justifyContent: 'center',
        textDecoration: 'none',
        display: 'flex',
        flex: 1,
        cursor: 'pointer',
        ':hover': {
            backgroundColor: 'purple'
        }
    };
    
    const enhance = compose(
        getButtonGroup,
        getTheme, // using the container's defined theme
        themeStyle(mapThemeToStyle), // apply the default theme to the component
        addStyle(style),
        setDisplayName('Button'),
        defaultProps({
            element: 'button'
                     }),
        Radium
    );
    export default enhance(componentFromProp('element'));

    Once 'buttonGroup' is true, it will add margin for each Buttons inside ButtonGroup.

    3. 'isVertical' prop.

    We can add this prop on to the html:

                        <ButtonGroup isVertical>
                            <Button element={'a'}>Click</Button>
                            <Button element={'a'}>Click</Button>
                        </ButtonGroup>

    Then in the ButtonGroup.js, we can check that whether this props exists, if yes, then set display direction to 'column' otherwise to 'row'.

    import React, {PropTypes} from 'react';
    
    import {
        addStyle,
        getTheme,
        themeStyle
    } from './hocs';
    import {
        setDisplayName,
        withContext,
        compose
    } from 'recompose';
    import Radium from 'radium';
    
    const mapThemeToStyle = ({number}, porps) => ({
        padding: (number.buttonGroupSpace || 6) * 1,
        flexDirection: porps.isVertical ? 'column': 'row'
    });
    
    const ButtonGroup = ({ children, ...rest }) => (
        <div {...rest}>
            {children}
        </div>
    );
    
    const enhance = compose(
        setDisplayName('ButtonGroup'),
        getTheme,
        themeStyle(mapThemeToStyle),
        withContext(
            {buttonGroup: PropTypes.bool},
            (props) => ({buttonGroup: true})
        ),
        addStyle({
            padding: 60,
            display: 'flex'
                 }),
        Radium
    );
    
    export default enhance(ButtonGroup);
  • 相关阅读:
    上传文件是常要处理的事情,使用ajaxFileUpload.js处理比较方便,这里的ajaxFileUpload.js文件修改过的,
    文件上传控件bootstrap-fileinput的使用
    常用开发中使用到的作图工具(开发向)
    mybatis-generator + mysql/ptsql
    表单嵌套问题的解决方法
    C++和QML混合的QT程序调试方法
    windows下,Qt Creator 中javascript调试器安装并使用
    Qt浮动按钮的实现(使用窗口背景透明、实现只显示浮动按钮的目的)
    不能继承于QObject的类就一定不能使用信号槽?(用一个代理类进行发射就行了)
    关于SetLength报Out of memory的研究及解决办法
  • 原文地址:https://www.cnblogs.com/Answer1215/p/6538652.html
Copyright © 2011-2022 走看看