具有样式化组件的单选按钮,用于React

时间:2019-06-03 08:27:03

标签: reactjs radio-button styled-components

我试图以this pen为例使用样式化组件在React中实现单选按钮集,但是同级选择器使我失望。如何转动

.radio-input:checked ~ .radio-fill {
  width: calc(100% - 4px);
  height: calc(100% - 4px);
  transition: width 0.2s ease-out, height 0.2s ease-out;
}
.radio-input:checked ~ .radio-fill::before {
  opacity: 1;
  transition: opacity 1s ease;
}

在样式化的组件中放入CSS?

谁能指出我的错误或进行快速笔演示?谢谢!这是我的完整代码:

import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { colors } from '../../../theme/vars';

export class RadioGroup extends React.Component {
  getChildContext() {
    const { name, selectedValue, onChange } = this.props;
    return {
      radioGroup: {
        name, selectedValue, onChange,
      },
    };
  }

  render() {
    const { Component, name, selectedValue, onChange, children, ...rest } = this.props;
    return <Component role="radiogroup" {...rest}>{children}</Component>;
  }
}

RadioGroup.childContextTypes = {
  radioGroup: PropTypes.object,
};

RadioGroup.propTypes = {
  name: PropTypes.string,
  selectedValue: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.bool,
  ]),
  children: PropTypes.node.isRequired,
  Component: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func,
    PropTypes.object,
  ]),
};

RadioGroup.defaultProps = {
  name: '',
  selectedValue: '',
  Component: 'div',
};




// eslint-disable-next-line react/no-multi-comp
export class Radio extends React.Component {
  render() {
    const { name, selectedValue } = this.context.radioGroup;
    const { onChange, value, labelText } = this.props;
    let checked = false;

    if (selectedValue !== undefined) {
      checked = (value === selectedValue);
    }

    console.log('value: ', value);
    console.log('checked: ', checked);
    console.log('selectedValue: ', selectedValue);

    return (
      <Root>
        <Input
          type="radio"
          name={name}
          value={value}
          checked={checked}
          aria-checked={checked}
          onChange={onChange}
        />
        <Fill />
        {/* <div style={{ marginLeft: '25px' }}>{labelText}</div> */}
      </Root>
    );
  }
}

Radio.contextTypes = {
  radioGroup: PropTypes.object,
};

Radio.propTypes = {
  onChange: PropTypes.func,
  value: PropTypes.string,
  labelText: PropTypes.string,
};

Radio.defaultProps = {
  onChange: () => {},
  value: '',
  labelText: '',
};


const Root = styled.div`
  width: ${props => props.size ? props.size : 20}px;
  height: ${props => props.size ? props.size : 20}px;
  position: relative;

  &::before {
    content: '';
    border-radius: 100%;
    border: 1px solid ${props => props.borderColor ? props.borderColor : '#DDD'};
    background: ${props => props.backgroundColor ? props.backgroundColor : '#FAFAFA'};
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    box-sizing: border-box;
    pointer-events: none;
    z-index: 0;
  }
`;

const Fill = styled.div`
  background: ${props => props.fillColor ? props.fillColor : '#A475E4'};
  width: 0;
  height: 0;
  border-radius: 100%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  transition: width 0.2s ease-in, height 0.2s ease-in;
  pointer-events: none;
  z-index: 1;

  &::before {
    content: '';
    opacity: 0;
    width: calc(20px - 4px);
    position: absolute;
    height: calc(20px - 4px);
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    border: 1px solid ${props => props.borderActive ? props.borderActive : '#A475E4'};
    border-radius: 100%;
  }
`;

const Input = styled.input`
  opacity: 0;
  z-index: 2;
  position: absolute;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;

  &:focus {
    outline: none;
  }

  &:checked {
    ${Fill} {
      width: calc(100% - 8px);
      height: calc(100% - 8px);
      transition: width 0.2s ease-out, height 0.2s ease-out;

      &::before {
        opacity: 1;
        transition: opacity 1s ease;
      }
    }

  }
`;

以及组件的用法:

<RadioGroup name="setYAxis" onChange={e => this.toggleSetYAxis(e)} selectedValue={this.state.setYAxis}>
  <Radio value="autoscale" labelText="Autoscale" />
  <Radio value="manual" labelText="Manual" />
</RadioGroup>

2 个答案:

答案 0 :(得分:1)

您在填充选择器之前缺少〜:

  &:checked {
    & ~ ${Fill} {
      width: calc(100% - 8px);
      height: calc(100% - 8px);
      transition: width 0.2s ease-out, height 0.2s ease-out;

      &::before {
        opacity: 1;
        transition: opacity 1s ease;
      }
    }

在此示例中,您似乎对状态的实际更新方式也存在问题,但这与样式无关:在RadioGroup上,您没有将onChange属性传递给Radio。散布运算符的使用方式,使您的const rest仅包含const中尚未定义的属性。因此,您需要从此处删除onChange声明以使其进入rest内。

export class RadioGroup extends React.Component {
  getChildContext() {
    const { name, selectedValue, onChange } = this.props;
    return {
      radioGroup: {
        name,
        selectedValue,
        onChange
      }
    };
  }

  render() {
    const {
      Component,
      name,
      selectedValue,
      // Remove onChange from here
      // onChange, 
      children,
      ...rest
    } = this.props;
    return (
      <Component role="radiogroup" {...rest}>
        {children}
      </Component>
    );
  }
}

工作示例:https://codesandbox.io/s/serene-cdn-1fw1i

答案 1 :(得分:1)

我创建了一个codesandbox来解决CSS问题和状态管理问题。 提供的代码更简单,并且不依赖于过时的React上下文API (doc here)