react 实战:写一个年份选择器

上代码。

组件的Js文件。

import React, { Component } from "react";
import Style from './myYearSelect.less'

function getOffsetTop(el){
  return el.offsetParent
   ? el.offsetTop + getOffsetTop(el.offsetParent)
   : el.offsetTop
}
function getOffsetLeft(el){
  return el.offsetParent
   ? el.offsetLeft + getOffsetLeft(el.offsetParent)
   : el.offsetLeft
}

class YearPicker extends Component {
  constructor(props) {
      super(props);
      this.state = {
        isShow: false,
        selectedyear:"",
        boxPos:{},
        years: []
      };
  }

  componentWillMount() {
    let { defaultValue } = this.props;
    if (defaultValue) {
      this.setState({ selectedyear: defaultValue });
    }
  }
  componentDidMount() {
    let _this = this;
    document.addEventListener(
        "click",
        _this.clickFunc,
        false
    );
  }
  componentWillUnmount(){
    let _this = this
    document.removeEventListener(
      "click",
      _this.clickFunc,
      false
    );
  }
  clickFunc = e => { 
    const { isShow } = this.state;
    let clsName = e.target.className;
    // console.log(e.target.dataset)
    if (!e.target.dataset || !e.target.dataset.tag || e.target.dataset.tag !== "year") {
      this.onBlurFunc()
    }
  }
  onFocusFunc = e => {
    const { selectedyear } = this.state;
    const operand = 12;
    const d = document.documentElement || document.body || null
    let top, left

    this.initData(operand, selectedyear);
    
    if (d) {
      top = d.scrollTop
      left = d.scrollLeft
    }
    this.setState({
      isShow: true,
      boxPos:{
        x: getOffsetTop(e.currentTarget) - (top ? top : 0),
        y: getOffsetLeft(e.currentTarget) - (left ? left : 0),
      }
    })
  }
  onBlurFunc = e => {
    this.setState({
      isShow: false
    })
  }
  // 向前的年份
  prev = () => {
    const { years } = this.state;
    if (years[0] <= 1970) {
        return;
    }
    this.getNewYearRangestartAndEnd("prev");
  };
  // 向后的年份
  next = () => {
    this.getNewYearRangestartAndEnd("next");
  };
  getNewYearRangestartAndEnd = type => {
    const { selectedyear, years } = this.state;
    let operand = Number(this.props.operand);
    operand = operand ? operand : 12;
    let start = Number(years[0]);
    let end = Number(years[years.length - 1]);
    let newstart;
    let newend;
    if (type == "prev") {
        newstart = parseInt(start - operand);
        newend = parseInt(end - operand);
        this.getYearsArr(newstart, newend);
    }
    if (type == "next") {
        newstart = parseInt(start + operand);
        newend = parseInt(end + operand);
        this.getYearsArr(newstart, newend);
    }
  }
  initData = (operand, defaultValue) => {
    operand = operand ? operand : 12;

    let year = defaultValue - 1970;
    let curr = year % operand;
    let start = defaultValue - curr;
    let end = parseInt(defaultValue) + parseInt(operand) - 1 - curr;
    this.getYearsArr(start, end);
  };
  getYearsArr = (start, end) => {
    let arr = [];
    for (let i = start; i <= end; i++) {
        arr.push(Number(i));
    }
    this.setState({
        years: arr
    });
  };
  // 选中某一年
  selects = e => {
    let val = Number(e.target.value);
    this.setState({ selectedyear: val });
    if (this.props.callback) {
        this.props.callback(val);
    }
  };


render() {
    const { width } = this.props
    const { isShow, boxPos, selectedyear, years } = this.state;
    const w = width ? width : 200

    return (
        <div className={Style.main} data-tag="year">
          <input className={Style.mainInput}
            data-tag="year"
            value={selectedyear}
            readOnly
            onFocus={this.onFocusFunc}
          />
          { isShow && boxPos.x ? <div data-tag="year" tabIndex="0" className={Style.box} 
            style={{ top:boxPos.x, left: boxPos.y, width:w }}>
              <List
              data={years}
              value={selectedyear} 
              prev={this.prev}
              next={this.next}
              cback={this.selects}
              />
            </div> : '' }
        </div>
    );
}
}

const List = props => {
  const { data, value, prev, next, cback } = props;
  return (
      <div className={Style.list} tabIndex="-1" data-tag="year">
          <div className={Style.header} data-tag="year">
            <a
                type="double-left"
                role="button"
                className={Style.prevBtn}
                onClick={prev}
                data-tag="year"
            ></a>
            <span data-tag="year">{value}</span>
            <a
                type="double-left"
                role="button"
                className={Style.nextBtn}
                onClick={next}
                data-tag="year"
            ></a>
          </div>
          <div className={Style.body} data-tag="year">
              <ul className={Style.yearUl} data-tag="year">
                {data.map((item, index) => (
                    <li
                        key={index}
                        title={item}
                        data-tag="year"
                        className={
                          item == value
                              ? Style.calendarYearSelected
                              : ""
                        }
                    >
                        <button onClick={cback} value={item}>
                            {item}
                        </button>
                    </li>
                ))}
              </ul>
          </div>
      </div>
  );
};

export default YearPicker;

组件的 Less 文件。

.main{
  width: 100%;
}
.mainInput{
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-variant: tabular-nums;
  list-style: none;
  font-feature-settings: 'tnum';
  position: relative;
  display: inline-block;
  width: 100%;
  height: 32px;
  padding: 4px 11px;
  color: rgba(0, 0, 0, 0.65);
  font-size: 14px;
  line-height: 1.5;
  background-color: #fff;
  background-image: none;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  transition: all 0.3s;
}
.box{
  // width: 100%;
  height: 260px;
  max-width: 400px;
  // max-height: 400px;
  background-color: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  position: fixed;
}
.list{
  width: 100%;
  height: 100%;
}
.header{
  position: relative;
  height: 34px;
  line-height: 34px;
  text-align: center;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  border-bottom: 1px solid #e9e9e9;
  background-color: #fff;
  color:#000;
}
.prevBtn, .nextBtn{
  position: absolute;
  top: 0;
  color: #000;
  font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", sans-serif;
  padding: 0 5px;
  font-size: 16px;
  display: inline-block;
  line-height: 34px;  
  height: 100%;  
  &:hover{
    &::before, &::after{
      border-color: rgba(0, 0, 0, 0.65);
    }
  }
  &::before, &::after{
    position: relative;
    top: -1px;
    display: inline-block;
    width: 8px;
    height: 8px;
    vertical-align: middle;
    border: 0 solid #aaa;
    border-width: 1.5px 0 0 1.5px;
    border-radius: 1px;
    transform: rotate(-45deg) scale(0.8);
    transition: all 0.3s;
    content: '';
  }
  &::after{
    left:-3px
  }
}
.nextBtn{
  &::before,&::after{
    transform: rotate(135deg) scale(0.8);
  }
}
.prevBtn{
  left:7%;
}
.nextBtn{
  right:7%;
}
.body{
  height: calc(100% - 34px);
  background-color: #fff;
}
.yearUl{
  padding: 0;
  margin: 0;
  overflow: hidden;
  list-style: none;
  li{
    float: left;
    width: 33.33%;
    max-width: 76px;
    text-align: center;
    cursor: pointer;
    button{
      cursor: pointer;
      outline: none;
      border: 0;
      display: inline-block;
      margin: 0 auto;
      color: rgba(0, 0, 0, 0.65);
      background: transparent;
      text-align: center;
      height: 24px;
      line-height: 24px;
      padding: 0 6px;
      border-radius: 4px;
      transition: background 0.3s ease;
      margin: 14px 0;
    }
  }
}
li.calendarYearSelected{
  button{
    background-color: #108ee9;
    color: #fff;
  }
}

使用组件的页面 jsx 文件。

import React from 'react'
import moment from 'moment'

import YearPicker from './yearPicker/myYearSelect'

class yaerSelectPageNew extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      mockData:{
        year:2020
      },
      YPWidth:231
    }
  }

  filterByYear = (e) => {
    const { mockData } = this.state
    this.setState({mockData: {
      ...mockData,
      year: e.toString()
    }});
  }

  render(){
    const { YPWidth, mockData } = this.state

    return (
      <div>
        
        <h1>Page </h1>
        <div style={{ width: '200px' }}>
          <YearPicker 
          defaultValue={moment().format('YYYY')} 
          callback={this.filterByYear} 
          width={YPWidth}
          />
        </div>
      </div>
    );
  }
}

export default yaerSelectPageNew

猜你喜欢

转载自www.cnblogs.com/foxcharon/p/12318578.html