Browse Source

fix: fix some bugs & update readme

yhhu 5 years ago
parent
commit
a210a294d7
9 changed files with 254 additions and 124 deletions
  1. 1 0
      .eslintrc
  2. 24 1
      README.md
  3. BIN
      docs/datepicker.gif
  4. 10 1
      src/app.js
  5. 45 34
      src/components/calendar/Index.js
  6. 15 0
      src/components/calendar/index.css
  7. 119 84
      src/components/index/DatePicker.js
  8. 38 4
      src/helper.js
  9. 2 0
      src/utils/index.js

+ 1 - 0
.eslintrc

@@ -35,6 +35,7 @@
       "never"
     ],
     "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
+    "no-unused-expressions": ["error", { "allowShortCircuit": true, "allowTernary": true }],
     "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
     "react/require-extension": "off",
     "arrow-parens": ["error", "as-needed"],

+ 24 - 1
README.md

@@ -9,7 +9,30 @@ $ git checkout v1.0.0
 ```
 ## 重构之后版本 
 
-`tag` v2.0.0
+`tag` v2.0.0(目前在master上开发,待基本功能完善推到此TAG)
+
+### UI
+
+参照 `Ant Design`,功能有所缩减
+
+### 效果图
+
+![datepicker](./docs/datepicker.gif)
+
+### 支持功能
+
+- [] line模式
+- [] 禁止选择模式
+- [] 设置禁用起始日期以及结束日期
+- [] 基本选择日期功能
+- [x] 年模式
+- [x] 月模式
+- [x] 中国农历月份
+
+### 待完善
+
+- [x] 案例制作
+- [x] 使用文档完善
 
 ### node及npm版本
 

BIN
docs/datepicker.gif


+ 10 - 1
src/app.js

@@ -3,9 +3,18 @@ import React from 'react'
 import ReactDOM from 'react-dom'
 import DatePicker from './index'
 
+const disabledDate = current => (
+  // start & end
+  // ['2018-01-02', current]
+
+  // end
+  [current]
+)
+
 const App = () => (
   <DatePicker
-    disable
+    // disable
+    disabledDate={current => disabledDate(current)}
     defaultDate="2018-01-31"
     placeholder="please choose date"
     onSelectDate={day => console.log(day)}

+ 45 - 34
src/components/calendar/Index.js

@@ -8,41 +8,52 @@ import {
 import { DateContext } from '../../context'
 import { withContext } from '../../utils'
 
-const Index = ({
-  context: {
-    weekTags, days, onSelectDay,
-  },
-}) => (
-  <div className={Styles.wrapper}>
-    { weekTags.map(weekName => (
-      <span
-        className={`${Styles.normal} ${Styles.week}`}
-        title={`星期${weekName}`}
-        key={weekName}
-      >
-        { weekName }
-      </span>
-    )) }
-    {
-      days.map(day => (
-        <span
-          className={classNames(Styles.normal, {
-            [Styles.prev]: day.tag === PREV_DAY,
-            [Styles.next]: day.tag === NEXT_DAY,
-            [Styles.current]: day.current,
-            [Styles.selected]: day.selected,
-          })}
-          title={day.full}
-          key={day.full}
-          onClick={e => onSelectDay(day, e)}
-          role="presentation"
-        >
-          { day.day }
-        </span>
-      ))
+class Index extends React.Component {
+  selectDay = (day, event) => {
+    if (day.disabled) {
+      return
     }
-  </div>
-)
+    const { context: { onSelectDay } } = this.props
+    onSelectDay(day, event)
+  }
+
+  render() {
+    const { context: { weekTags, days } } = this.props
+
+    return (
+      <div className={Styles.wrapper}>
+        { weekTags.map(weekName => (
+          <span
+            className={`${Styles.normal} ${Styles.week}`}
+            title={`星期${weekName}`}
+            key={weekName}
+          >
+            { weekName }
+          </span>
+        )) }
+        {
+          days.map(day => (
+            <span
+              className={classNames(Styles.normal, {
+                [Styles.prev]: day.tag === PREV_DAY,
+                [Styles.next]: day.tag === NEXT_DAY,
+                [Styles.current]: day.current,
+                [Styles.selected]: day.selected,
+                [Styles.disabled]: day.disabled,
+              })}
+              title={day.full}
+              key={day.full}
+              onClick={e => this.selectDay(day, e)}
+              role="presentation"
+            >
+              { day.day }
+            </span>
+          ))
+        }
+      </div>
+    )
+  }
+}
 
 Index.propTypes = {
   context: PropTypes.shape({

+ 15 - 0
src/components/calendar/index.css

@@ -38,4 +38,19 @@
 }
 .selected:hover {
   background-color: #1890ff;
+}
+.disabled {
+  margin: 3px 0;
+  background-color: #f5f5f5;
+  cursor: not-allowed;
+  color: #bcbcbc;
+}
+.disabled:hover {
+  background-color: #f5f5f5;
+}
+.disabled.current {
+  border: none;
+}
+.disabled.selected {
+  color: #000;
 }

+ 119 - 84
src/components/index/DatePicker.js

@@ -7,16 +7,14 @@ import {
   getCurrentYear,
   getCurrentMonth,
   isDateValid,
-  getYearFromSpecificDate,
-  getMonthFromSpecificDate,
+  getCurrentDate,
 } from '../../utils'
 import Modal from '../modal/Modal'
 import {
-  CHINESE_MODEL, WESTERN_MODEL, _, INPUT_DEFAULT_PLACEHOLDER,
+  CHINESE_MODEL, WESTERN_MODEL, _, INPUT_DEFAULT_PLACEHOLDER, noop,
 } from '../../const'
 import { DateContext, initialData } from '../../context'
 import {
-  setSelectedDays,
   getDaysOfMonth,
   getWeekSort,
   getDaysAfterchangedYearOrMonth,
@@ -47,6 +45,10 @@ class DatePicker extends Component {
     this._addGlobalClickListener()
   }
 
+  componentWillUnmount() {
+    this._removeGlobalClickListener()
+  }
+
   onModalOpen = () => {
     this.setState({ showModal: true })
   }
@@ -55,13 +57,46 @@ class DatePicker extends Component {
     this.setState({ showModal: false })
   }
 
+  _setDateFromSepcialDay = ({
+    originDays,
+    setDay,
+    currentDay,
+    model,
+    callback,
+    setYear,
+    setMonth,
+  }, extralState) => {
+    const { days, year, month } = this.state
+    const { disabledDate } = this.props
+    const range = disabledDate(getCurrentDate())
+    const specialDays = resetCalendarFromSpecialDay(originDays || days,
+      setDay, currentDay, model, range)
+    let { changeYear, changeMonth } = specialDays
+    const { afterDays } = specialDays
+
+    if (setYear) {
+      changeYear = setYear
+    }
+
+    if (setMonth) {
+      changeMonth = setMonth
+    }
+    this.setState({
+      days: afterDays,
+      year: changeYear === _ ? year : changeYear,
+      month: changeMonth === _ ? month : changeMonth,
+      value: setDay,
+      ...extralState,
+    }, () => {
+      callback && callback()
+    })
+  }
+
   onInputChange = event => {
     const val = event.target.value
     this.setState({ value: val }, () => {
       if (!val || isDateValid(val)) {
-        const { days } = this.state
-        const afterSetDays = setSelectedDays(days, val)
-        this.setState({ days: afterSetDays })
+        this._setDateFromSepcialDay({ setDay: val })
       }
     })
   }
@@ -81,12 +116,11 @@ class DatePicker extends Component {
 
     const weekTags = getWeekSort(nextModel)
     const changeModelDays = getDaysOfMonth(year, month, nextModel)
-    const afterSetDays = setSelectedDays(changeModelDays, value)
-    this.setState({
-      model: nextModel,
-      weekTags: weekTags,
-      days: afterSetDays,
-    })
+    // debugger
+    this._setDateFromSepcialDay(
+      { originDays: changeModelDays, setDay: value, currentDay: value },
+      { model: nextModel, weekTags: weekTags },
+    )
   }
 
   _selectDayCallback = day => {
@@ -96,53 +130,41 @@ class DatePicker extends Component {
   }
 
   onSelectDay = day => {
-    const {
-      days, value, year, month,
-    } = this.state
-    const specialDays = resetCalendarFromSpecialDay(days, day.full, value)
-    const { changeYear, changeMonth, afterDays } = specialDays
-    this.setState({
-      days: afterDays,
-      year: changeYear === _ ? year : changeYear,
-      month: changeMonth === _ ? month : changeMonth,
-      value: day.full,
-    }, () => this._selectDayCallback(day.full))
+    const { days, value } = this.state
+    this._setDateFromSepcialDay({
+      originDays: days,
+      setDay: day.full,
+      currentDay: value,
+      callback: () => this._selectDayCallback(day.full),
+    })
   }
 
   onSelectToday = today => {
-    const {
-      days, value, year, month,
-    } = this.state
+    const { days, value } = this.state
     let renderDays = days
-    let changeYear = year
-    let changeMonth = month
 
     if (!isInCurrentMonth(today, value)) {
       // 不是在【今天】这个月份,需要重新换数据源
       renderDays = initialData.days
-      changeYear = getYearFromSpecificDate(today)
-      changeMonth = getMonthFromSpecificDate(today)
     }
 
-    const afterSetDays = setSelectedDays(renderDays, today)
-    this.setState({
-      value: today,
-      days: afterSetDays,
-      year: changeYear,
-      month: changeMonth,
-    }, () => this._selectDayCallback(today))
+    this._setDateFromSepcialDay({
+      originDays: renderDays,
+      setDay: today,
+      currentDay: value,
+      callback: () => this._selectDayCallback(today),
+    })
   }
 
   _onChangeYearOrMonth = (changeYear, changeMonth) => {
-    const {
-      model, year, month, value,
-    } = this.state
+    const { model, value } = this.state
     const days = getDaysAfterchangedYearOrMonth(changeYear, changeMonth, model)
-    const afterSetDays = setSelectedDays(days, value)
-    this.setState({
-      days: afterSetDays,
-      year: changeYear === _ ? year : changeYear,
-      month: changeMonth === _ ? month : changeMonth,
+    this._setDateFromSepcialDay({
+      originDays: days,
+      setDay: value,
+      currentDay: value,
+      setYear: changeYear,
+      setMonth: changeMonth,
     })
   }
 
@@ -153,8 +175,8 @@ class DatePicker extends Component {
   }
 
   onPrevYear = () => {
-    const { year } = this.state
-    this._onChangeYearOrMonth(year - 1, _)
+    const { year, month } = this.state
+    this._onChangeYearOrMonth(year - 1, month)
   }
 
   onNextMonth = () => {
@@ -164,12 +186,12 @@ class DatePicker extends Component {
   }
 
   onNextYear = () => {
-    const { year } = this.state
-    this._onChangeYearOrMonth(+year + 1, _)
+    const { year, month } = this.state
+    this._onChangeYearOrMonth(+year + 1, month)
   }
 
   _addGlobalClickListener() {
-    document.addEventListener('click', event => {
+    this.globalClickListener = document.addEventListener('click', event => {
       if (event.target.closest('.picker-wrapper')) {
         return
       }
@@ -183,25 +205,21 @@ class DatePicker extends Component {
     })
   }
 
+  _removeGlobalClickListener() {
+    document.removeEventListener('click', this.globalClickListener)
+  }
+
   _onInitialDefaultDay() {
-    const {
-      days, value, year, month,
-    } = this.state
-    const specialDays = resetCalendarFromSpecialDay(days, value)
-    const { changeYear, changeMonth, afterDays } = specialDays
-    this.setState({
-      days: afterDays,
-      year: changeYear === _ ? year : changeYear,
-      month: changeMonth === _ ? month : changeMonth,
-    })
+    const { days, value } = this.state
+    this._setDateFromSepcialDay({ originDays: days, setDay: value })
   }
 
-  render() {
+  renderInput = () => {
     const { inline, placeholder, disable } = this.props
-    const { value, showModal } = this.state
+    const { value } = this.state
 
     return (
-      <div className={`picker-wrapper ${Styles.wrapper}`}>
+      <React.Fragment>
         <div
           className={Styles.container}
           style={inline ? { display: 'inline-block' } : {}}
@@ -227,27 +245,42 @@ class DatePicker extends Component {
           <div className={Styles.line} />
         </div>
         { disable && <div className={Styles.inputDisable} /> }
-        <DateContext.Provider
-          value={
-            {
-              ...this.props,
-              ...this.state,
-              onSelectDay: this.onSelectDay,
-              onSelectToday: this.onSelectToday,
-              onChangeModel: this.onChangeModel,
-              onPrevMonth: this.onPrevMonth,
-              onPrevYear: this.onPrevYear,
-              onNextMonth: this.onNextMonth,
-              onNextYear: this.onNextYear,
-              onInputChange: this.onInputChange,
-            }
+      </React.Fragment>
+    )
+  }
+
+  renderModal = () => {
+    const { showModal } = this.state
+    return (
+      <DateContext.Provider
+        value={
+          {
+            ...this.props,
+            ...this.state,
+            onSelectDay: this.onSelectDay,
+            onSelectToday: this.onSelectToday,
+            onChangeModel: this.onChangeModel,
+            onPrevMonth: this.onPrevMonth,
+            onPrevYear: this.onPrevYear,
+            onNextMonth: this.onNextMonth,
+            onNextYear: this.onNextYear,
+            onInputChange: this.onInputChange,
           }
-        >
-          <Modal
-            isMounted={showModal}
-            delayTime={200}
-          />
-        </DateContext.Provider>
+        }
+      >
+        <Modal
+          isMounted={showModal}
+          delayTime={200}
+        />
+      </DateContext.Provider>
+    )
+  }
+
+  render() {
+    return (
+      <div className={`picker-wrapper ${Styles.wrapper}`}>
+        { this.renderInput() }
+        { this.renderModal() }
       </div>
     )
   }
@@ -260,6 +293,7 @@ DatePicker.defaultProps = {
   year: getCurrentYear(),
   month: getCurrentMonth(),
   disable: false,
+  disabledDate: noop,
 }
 
 DatePicker.propTypes = {
@@ -270,6 +304,7 @@ DatePicker.propTypes = {
   month: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
   onSelectDate: PropTypes.func.isRequired,
   disable: PropTypes.bool,
+  disabledDate: PropTypes.func,
 }
 
 export default DatePicker

+ 38 - 4
src/helper.js

@@ -123,11 +123,45 @@ export const selectDayByIndex = (days, index) => days.map((day, idx) => {
   return tempDay
 })
 
-export const setSelectedDays = (days, selectedDay) => {
+const compareDate = (d1, d2) => {
+  let dNum1 = d1
+  let dNum2 = d2
+
+  if (typeof d1 === 'string') {
+    dNum1 = +d1.split(/[/\-\\:]/).join('')
+  }
+
+  if (typeof d2 === 'string') {
+    dNum2 = +d2.split(/[/\-\\:]/).join('')
+  }
+
+  if (dNum1 > dNum2) {
+    return 1
+  }
+
+  if (dNum1 < dNum2) {
+    return -1
+  }
+
+  return 0
+}
+
+export const setSelectedDaysAndRange = (days, selectedDay, disabledRange) => {
   const fDate = formatDate(selectedDay)
+  let range = disabledRange
+
+  if (!range) {
+    range = [0]
+  }
+
+  if (!range[1]) {
+    range.unshift(0)
+  }
+
   return days.map(day => {
     const tempDay = day
     tempDay.selected = day.full === fDate.format
+    tempDay.disabled = compareDate(day.full, range[0]) >= 0 && compareDate(day.full, range[1]) <= 0
     return tempDay
   })
 }
@@ -142,13 +176,13 @@ export const isInCurrentMonth = (date, current = getCurrentDate()) => (
 )
 
 export const resetCalendarFromSpecialDay = (originDays, date,
-  current = getCurrentDate(), model = CHINESE_MODEL) => {
+  current = getCurrentDate(), model = CHINESE_MODEL, disabledRange) => {
   let days = originDays
   if (!isInCurrentMonth(date, current)) {
     const year = date.substring(0, 4)
     const month = date.substring(5, 7)
     days = getDaysAfterchangedYearOrMonth(year, month, model)
-    const afterDays = setSelectedDays(days, date)
+    const afterDays = setSelectedDaysAndRange(days, date, disabledRange)
     return {
       afterDays: afterDays,
       changeYear: year,
@@ -156,6 +190,6 @@ export const resetCalendarFromSpecialDay = (originDays, date,
     }
   }
 
-  const afterDays = setSelectedDays(days, date)
+  const afterDays = setSelectedDaysAndRange(days, date, disabledRange)
   return { afterDays }
 }

+ 2 - 0
src/utils/index.js

@@ -1,2 +1,4 @@
 export * from './date'
 export * from './withContext'
+
+export const compose = (...fns) => fns.reduceRight((acc, fn) => (...args) => fn(acc(...args)))