yhhu преди 5 години
родител
ревизия
2e08c25d24
променени са 8 файла, в които са добавени 192 реда и са изтрити 126 реда
  1. 1 0
      src/app.js
  2. 34 30
      src/components/calendar/Header.js
  3. 43 35
      src/components/calendar/Index.js
  4. 29 22
      src/components/footer/Footer.js
  5. 54 22
      src/components/index/DatePicker.js
  6. 23 17
      src/components/input/Input.js
  7. 1 0
      src/utils/index.js
  8. 7 0
      src/utils/withContext.js

+ 1 - 0
src/app.js

@@ -5,6 +5,7 @@ import DatePicker from './index'
 
 const App = () => (
   <DatePicker
+    defaultDate="2018-01-01"
     placeholder="please choose date"
     onSelectDate={day => console.log(day)}
   />

+ 34 - 30
src/components/calendar/Header.js

@@ -1,36 +1,40 @@
 import React from 'react'
+import PropTypes from 'prop-types'
 import Styles from './header.css'
 import { DateContext } from '../../context'
+import { withContext, formatMonthOrDay } from '../../utils'
 
-const Header = () => (
-  <DateContext.Consumer>
-    {
-      ({
-        year,
-        month,
-        onPrevMonth,
-        onPrevYear,
-        onNextMonth,
-        onNextYear,
-      }) => {
-        console.log('Header year', year)
-        console.log('Header month', month)
-        return (
-          <div className={Styles.wrapper}>
-            <i className={Styles.prevYear} role="presentation" title="上一年" onClick={e => onPrevYear(e)} />
-            <i className={Styles.prevMonth} role="presentation" title="上一月" onClick={e => onPrevMonth(e)} />
-            <div className={Styles.text}>
-              <span className={Styles.link}>{`${year}年`}</span>
-              <span className={Styles.link}>{`${month}月`}</span>
-            </div>
-            <i className={Styles.nextMonth} role="presentation" title="下一月" onClick={e => onNextMonth(e)} />
-            <i className={Styles.nextYear} role="presentation" title="下一年" onClick={e => onNextYear(e)} />
-          </div>
-        )
-      }
-    }
-
-  </DateContext.Consumer>
+const Header = ({
+  context: {
+    year,
+    month,
+    onPrevMonth,
+    onPrevYear,
+    onNextMonth,
+    onNextYear,
+  },
+}) => (
+  <div className={Styles.wrapper}>
+    <i className={Styles.prevYear} role="presentation" title="上一年" onClick={e => onPrevYear(e)} />
+    <i className={Styles.prevMonth} role="presentation" title="上一月" onClick={e => onPrevMonth(e)} />
+    <div className={Styles.text}>
+      <span className={Styles.link}>{`${year}年`}</span>
+      <span className={Styles.link}>{`${formatMonthOrDay(month)}月`}</span>
+    </div>
+    <i className={Styles.nextMonth} role="presentation" title="下一月" onClick={e => onNextMonth(e)} />
+    <i className={Styles.nextYear} role="presentation" title="下一年" onClick={e => onNextYear(e)} />
+  </div>
 )
 
-export default Header
+Header.propTypes = {
+  context: PropTypes.shape({
+    year: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+    month: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+    onPrevMonth: PropTypes.func.isRequired,
+    onPrevYear: PropTypes.func.isRequired,
+    onNextMonth: PropTypes.func.isRequired,
+    onNextYear: PropTypes.func.isRequired,
+  }).isRequired,
+}
+
+export default withContext(DateContext, Header)

+ 43 - 35
src/components/calendar/Index.js

@@ -1,47 +1,55 @@
 import React from 'react'
+import PropTypes from 'prop-types'
 import classNames from 'classnames'
 import Styles from './index.css'
 import {
   PREV_DAY, NEXT_DAY,
 } from '../../const'
 import { DateContext } from '../../context'
+import { withContext } from '../../utils'
 
-const Index = () => (
-  <DateContext.Consumer>
+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>
+    )) }
     {
-      ({ 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>
-            ))
-          }
-        </div>
-      )
+      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>
+      ))
     }
-  </DateContext.Consumer>
+  </div>
 )
 
-export default Index
+Index.propTypes = {
+  context: PropTypes.shape({
+    weekTags: PropTypes.array.isRequired,
+    days: PropTypes.array.isRequired,
+    onSelectDay: PropTypes.func.isRequired,
+  }).isRequired,
+}
+
+export default withContext(DateContext, Index)

+ 29 - 22
src/components/footer/Footer.js

@@ -1,29 +1,36 @@
 import React from 'react'
+import PropTypes from 'prop-types'
 import Styles from './footer.css'
 import { DateContext } from '../../context'
 import { CHINESE_MODEL } from '../../const'
-import { getDateFormatFromSepecificDate } from '../../utils'
+import { getDateFormatFromSepecificDate, withContext } from '../../utils'
 
-const Footer = () => (
-  <DateContext.Consumer>
-    {
-      ({ model, onChangeModel, onSelectToday }) => (
-        <div className={Styles.wrapper}>
-          <div />
-          <div
-            role="presentation"
-            className={Styles.today}
-            onClick={e => onSelectToday(getDateFormatFromSepecificDate(), e)}
-          >
-            <span>今天</span>
-          </div>
-          <div role="presentation" className={Styles.lang} onClick={e => onChangeModel(model, e)}>
-            <span>{ model === CHINESE_MODEL ? '中' : '西' }</span>
-          </div>
-        </div>
-      )
-    }
-  </DateContext.Consumer>
+const Footer = ({
+  context: {
+    model, onChangeModel, onSelectToday,
+  },
+}) => (
+  <div className={Styles.wrapper}>
+    <div />
+    <div
+      role="presentation"
+      className={Styles.today}
+      onClick={e => onSelectToday(getDateFormatFromSepecificDate(), e)}
+    >
+      <span>今天</span>
+    </div>
+    <div role="presentation" className={Styles.lang} onClick={e => onChangeModel(model, e)}>
+      <span>{ model === CHINESE_MODEL ? '中' : '西' }</span>
+    </div>
+  </div>
 )
 
-export default Footer
+Footer.propTypes = {
+  context: PropTypes.shape({
+    model: PropTypes.string.isRequired,
+    onChangeModel: PropTypes.func.isRequired,
+    onSelectToday: PropTypes.func.isRequired,
+  }).isRequired,
+}
+
+export default withContext(DateContext, Footer)

+ 54 - 22
src/components/index/DatePicker.js

@@ -6,6 +6,8 @@ import {
   getCurrentYear,
   getCurrentMonth,
   isDateValid,
+  getYearFromSpecificDate,
+  getMonthFromSpecificDate,
 } from '../../utils'
 import Modal from '../modal/Modal'
 import {
@@ -24,6 +26,7 @@ import {
 } from '../../helper'
 import '../../utils/closest-polyfill'
 
+/* eslint-disable no-underscore-dangle */
 class DatePicker extends Component {
   constructor(props) {
     super(props)
@@ -38,21 +41,11 @@ class DatePicker extends Component {
   }
 
   componentDidMount() {
-    document.addEventListener('click', event => {
-      if (event.target.closest('.picker-wrapper')) {
-        return
-      }
-
-      const { value } = this.state
-      if (!isDateValid(value)) {
-        this.onSelectToday(getDateFormatFromSepecificDate())
-      } else {
-        this.onModalClose()
-      }
-    })
+    const { value } = this.state
+    this._onInitialDefaultDay({ full: value })
+    this._addGlobalClickListener()
   }
 
-  /* eslint-disable no-underscore-dangle */
   onModalOpen = () => {
     this.setState({ showModal: true })
   }
@@ -116,28 +109,39 @@ class DatePicker extends Component {
   }
 
   onSelectToday = today => {
-    const { days, value } = this.state
+    const {
+      days, value, year, month,
+    } = 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 },
-      () => this._selectDayCallback(today))
+    this.setState({
+      value: today,
+      days: afterSetDays,
+      year: changeYear,
+      month: changeMonth,
+    }, () => this._selectDayCallback(today))
   }
 
   _onChangeYearOrMonth = (changeYear, changeMonth) => {
-    const { model, year, month } = this.state
+    const {
+      model, year, month, value,
+    } = this.state
     const days = getDaysAfterchangedYearOrMonth(changeYear, changeMonth, model)
+    const afterSetDays = setSelectedDays(days, value)
     this.setState({
-      days: days,
+      days: afterSetDays,
       year: changeYear === _ ? year : changeYear,
       month: changeMonth === _ ? month : changeMonth,
-    }, () => {
-      // todo bug
-      this.forceUpdate()
     })
   }
 
@@ -163,6 +167,34 @@ class DatePicker extends Component {
     this._onChangeYearOrMonth(+year + 1, _)
   }
 
+  _addGlobalClickListener() {
+    document.addEventListener('click', event => {
+      if (event.target.closest('.picker-wrapper')) {
+        return
+      }
+
+      const { value } = this.state
+      if (!isDateValid(value)) {
+        this.onSelectToday(getDateFormatFromSepecificDate())
+      } else {
+        this.onModalClose()
+      }
+    })
+  }
+
+  _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,
+    })
+  }
+
   render() {
     const { inline, placeholder } = this.props
     const { value, showModal } = this.state
@@ -192,8 +224,8 @@ class DatePicker extends Component {
         <DateContext.Provider
           value={
             {
-              ...this.state,
               ...this.props,
+              ...this.state,
               onSelectDay: this.onSelectDay,
               onSelectToday: this.onSelectToday,
               onChangeModel: this.onChangeModel,

+ 23 - 17
src/components/input/Input.js

@@ -1,6 +1,8 @@
 import React from 'react'
+import PropTypes from 'prop-types'
 import Styles from './input.css'
 import { DateContext } from '../../context'
+import { withContext } from '../../utils'
 
 class Input extends React.Component {
   constructor(props) {
@@ -13,25 +15,29 @@ class Input extends React.Component {
   }
 
   render() {
+    const { context: { value, placeholder, onInputChange } } = this.props
+
     return (
-      <DateContext.Consumer>
-        {
-          ({ value, placeholder, onInputChange }) => (
-            <div className={Styles.wrapper}>
-              <input
-                ref={this.textInput}
-                className={Styles.input}
-                type="text"
-                placeholder={placeholder}
-                value={value}
-                onChange={e => onInputChange(e)}
-              />
-            </div>
-          )
-        }
-      </DateContext.Consumer>
+      <div className={Styles.wrapper}>
+        <input
+          ref={this.textInput}
+          className={Styles.input}
+          type="text"
+          placeholder={placeholder}
+          value={value}
+          onChange={e => onInputChange(e)}
+        />
+      </div>
     )
   }
 }
 
-export default Input
+Input.propTypes = {
+  context: PropTypes.shape({
+    value: PropTypes.string.isRequired,
+    placeholder: PropTypes.string.isRequired,
+    onInputChange: PropTypes.func.isRequired,
+  }).isRequired,
+}
+
+export default withContext(DateContext, Input)

+ 1 - 0
src/utils/index.js

@@ -1,3 +1,4 @@
 export * from './warning'
 export * from './isPlainObject'
 export * from './date'
+export * from './withContext'

+ 7 - 0
src/utils/withContext.js

@@ -0,0 +1,7 @@
+import React from 'react'
+
+export const withContext = (Context, Component) => props => (
+  <Context.Consumer>
+    {context => <Component {...props} context={context} />}
+  </Context.Consumer>
+)