├── .editorconfig
├── .gitignore
├── .stylelintrc
├── .travis.yml
├── HISTORY.md
├── README.md
├── assets
├── common
│ ├── Animation.less
│ ├── Calendar.less
│ ├── ConfirmPanel.less
│ ├── DatePicker.less
│ ├── ShortcutPanel.less
│ ├── SingleMonth.less
│ ├── TimePicker.less
│ ├── WeekPanel.less
│ ├── icon.svg
│ └── index.less
└── index.less
├── examples
├── basic.html
└── basic.tsx
├── index.android.js
├── index.ios.js
├── index.js
├── package.json
├── src
├── Calendar.tsx
├── CalendarProps.ts
├── DatePicker.base.tsx
├── DatePicker.tsx
├── DatePickerProps.ts
├── TimePicker.tsx
├── calendar
│ ├── AnimateWrapper.tsx
│ ├── ConfirmPanel.tsx
│ ├── Header.tsx
│ └── ShortcutPanel.tsx
├── date
│ ├── DataTypes.ts
│ ├── SingleMonth.tsx
│ └── WeekPanel.tsx
├── index.ts
├── locale
│ ├── en_US.ts
│ ├── pt_BR.ts
│ └── zh_CN.ts
└── util
│ └── index.ts
├── tests
├── Calendar.spec.tsx
├── DatePicker.spec.tsx
└── __snapshots__
│ ├── Calendar.spec.tsx.snap
│ └── DatePicker.spec.tsx.snap
├── tsconfig.json
├── tslint.json
└── typings
└── models.d.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | # Unix-style newlines with a newline ending every file
5 | [*.{js,css}]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | indent_style = space
9 | indent_size = 2
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | *.log
3 | *.log.*
4 | .idea
5 | .ipr
6 | .iws
7 | *~
8 | ~*
9 | *.diff
10 | *.patch
11 | *.bak
12 | .DS_Store
13 | Thumbs.db
14 | .project
15 | .*proj
16 | .svn
17 | *.swp
18 | *.swo
19 | *.pyc
20 | *.pyo
21 | node_modules
22 | .cache
23 | *.css
24 | build
25 | lib
26 | es
27 | coverage
28 | *.js
29 | *.jsx
30 | *.map
31 | !tests/index.js
32 | !/index*.js
33 | ios/
34 | android/
35 | xcuserdata
36 | yarn.lock
37 | _ts2js
38 | package-lock.json
39 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-standard",
3 | "rules": {
4 | at-rule-empty-line-before: null,
5 | at-rule-name-space-after: null,
6 | at-rule-no-unknown: null,
7 | comment-empty-line-before: null,
8 | declaration-bang-space-before: null,
9 | declaration-empty-line-before: null,
10 | function-comma-newline-after: null,
11 | function-name-case: null,
12 | function-parentheses-newline-inside: null,
13 | function-max-empty-lines: null,
14 | function-whitespace-after: null,
15 | indentation: null,
16 | number-leading-zero: null,
17 | number-no-trailing-zeros: null,
18 | rule-empty-line-before: null,
19 | selector-combinator-space-after: null,
20 | selector-list-comma-newline-after: null,
21 | selector-pseudo-element-colon-notation: null,
22 | unit-no-unknown: null,
23 | value-list-max-empty-lines: null,
24 | unit-case: null,
25 | color-hex-case: null,
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | sudo: false
4 |
5 | notifications:
6 | email:
7 | - zhang740@qq.com
8 |
9 | node_js:
10 | - 6.9.1
11 |
12 | before_install:
13 | - |
14 | if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(\.md$)|(^(docs|examples))/'
15 | then
16 | echo "Only docs were updated, stopping build process."
17 | exit
18 | fi
19 |
20 | script:
21 | - |
22 | if [ "$TEST_TYPE" = test ]; then
23 | npm test
24 | else
25 | npm run $TEST_TYPE
26 | fi
27 | env:
28 | matrix:
29 | - TEST_TYPE=lint
30 | - TEST_TYPE=test
31 | - TEST_TYPE=coverage:upload
32 |
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | # History
2 | ----
3 |
4 | ## 0.0.1 / 2017-08-10
5 |
6 | - TODO
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rmc-calendar
2 | ---
3 |
4 | React Mobile Calendar Component (web)
5 |
6 |
7 | [![NPM version][npm-image]][npm-url]
8 | 
9 | [![build status][travis-image]][travis-url]
10 | [![Test coverage][coveralls-image]][coveralls-url]
11 | [![gemnasium deps][gemnasium-image]][gemnasium-url]
12 | [![npm download][download-image]][download-url]
13 |
14 | [npm-image]: http://img.shields.io/npm/v/rmc-calendar.svg?style=flat-square
15 | [npm-url]: http://npmjs.org/package/rmc-calendar
16 | [travis-image]: https://img.shields.io/travis/react-component/m-calendar.svg?style=flat-square
17 | [travis-url]: https://travis-ci.org/react-component/m-calendar
18 | [coveralls-image]: https://img.shields.io/coveralls/react-component/m-calendar.svg?style=flat-square
19 | [coveralls-url]: https://coveralls.io/r/react-component/m-calendar?branch=master
20 | [gemnasium-image]: http://img.shields.io/gemnasium/react-component/m-calendar.svg?style=flat-square
21 | [gemnasium-url]: https://gemnasium.com/react-component/m-calendar
22 | [node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square
23 | [node-url]: http://nodejs.org/download/
24 | [download-image]: https://img.shields.io/npm/dm/rmc-calendar.svg?style=flat-square
25 | [download-url]: https://npmjs.org/package/rmc-calendar
26 |
27 | ## Screenshots
28 |
29 |
30 |
31 |
32 | ## Development
33 |
34 | ```
35 | npm i
36 | npm start
37 | ```
38 |
39 | ## Example
40 |
41 | http://localhost:8000/examples/
42 |
43 | online example: http://react-component.github.io/m-calendar/
44 |
45 | ## react-native (TBC)
46 |
47 | ```
48 | ./node_modules/rc-tools run react-native-init
49 | npm run watch-tsc
50 | react-native start
51 | react-native run-ios
52 | ```
53 |
54 | ## install
55 |
56 | [](https://npmjs.org/package/rmc-calendar)
57 |
58 |
59 | # docs
60 |
61 | ## Usage
62 | ```jsx
63 | import React, { Component } from 'react';
64 |
65 | import { Calendar } from 'rmc-calendar';
66 | import 'rmc-calendar/assets/index.css';
67 |
68 | class App extends Component {
69 | constructor(props) {
70 | super(props);
71 | this.state = {
72 | visible: false,
73 | };
74 | }
75 |
76 | setVisiable = () => {
77 | this.setState({
78 | visible: !this.state.visible,
79 | });
80 | }
81 |
82 | render() {
83 | return (
84 |
85 |
90 |
91 | );
92 | }
93 | }
94 |
95 | export default App;
96 | ```
97 |
98 | ## API
99 |
100 | ### Calendar props
101 | ```ts
102 | interface PropsType {
103 | /** enter direction,default: vertical */
104 | enterDirection?: 'horizontal' | 'vertical';
105 | /** locale */
106 | locale?: GlobalModels.Locale;
107 | onCancel?: () => void;
108 | onConfirm?: (startDateTime?: Date, endDateTime?: Date) => void;
109 | /** choose time,default: false */
110 | pickTime?: boolean;
111 | /** (web only) prefix class,default: rmc-calendar */
112 | prefixCls?: string;
113 | /** shortcut render, need showShortcut: true */
114 | renderShortcut?: (select: (startDate?: Date, endDate?: Date) => void) => React.ReactNode;
115 | /** show header, default: true */
116 | showHeader?: boolean;
117 | /** show shortcut, default: false */
118 | showShortcut?: boolean;
119 | /** header title, default: {locale.title} */
120 | title?: string;
121 | /** select type, default: range,one: one-day, range: range */
122 | type?: 'one' | 'range';
123 | /** visible, default: false */
124 | visible?: boolean;
125 |
126 | // DatePicker Component
127 | /** default date for show, default: today */
128 | defaultDate?: Date;
129 | /** extra info of date */
130 | getDateExtra?: (date: Date) => DateModels.ExtraData;
131 | /** infinite scroll, default: true */
132 | infinite?: boolean;
133 | /** infinite scroll optimization, default: false */
134 | infiniteOpt?: boolean;
135 | /** inital generate months, default: 6 */
136 | initalMonths?: number;
137 | /** max date */
138 | maxDate?: Date;
139 | /** min date */
140 | minDate?: Date;
141 | /** select range has disable date */
142 | onSelectHasDisableDate?: (date: Date[]) => void;
143 |
144 | // TimePicker Component
145 | /** inital time of TimePicker */
146 | defaultTimeValue?: Date;
147 | }
148 | ```
149 |
150 | ### DatePicker props
151 | ```ts
152 | export default interface PropsType {
153 | /** default date for show, default: today */
154 | defaultDate?: Date;
155 | /** select value of start date */
156 | startDate?: Date;
157 | /** select value of end date */
158 | endDate?: Date;
159 | /** extra info of date */
160 | getDateExtra?: (date: Date) => Models.ExtraData;
161 | /** infinite scroll, default: true */
162 | infinite?: boolean;
163 | /** infinite scroll optimization, default: false */
164 | infiniteOpt?: boolean;
165 | /** inital generate months, default: 6 */
166 | initalMonths?: number;
167 | /** locale */
168 | locale?: GlobalModels.Locale;
169 | /** max date */
170 | maxDate?: Date;
171 | /** min date */
172 | minDate?: Date;
173 | /** callback when click the cell of date */
174 | onCellClick?: (date: Date) => void;
175 | /** select range has disable date */
176 | onSelectHasDisableDate?: (date: Date[]) => void;
177 | /** (web only) prefix class */
178 | prefixCls?: string;
179 | /** select type, default: range,one: one-day, range: range */
180 | type?: 'one' | 'range';
181 | }
182 | ```
183 |
184 | ## Test Case
185 |
186 | ```
187 | npm test
188 | npm run chrome-test
189 | ```
190 |
191 | ## Coverage
192 |
193 | ```
194 | npm run coverage
195 | ```
196 |
197 | open coverage/ dir
198 |
199 | ## License
200 |
201 | rmc-calendar is released under the MIT license.
202 |
--------------------------------------------------------------------------------
/assets/common/Animation.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} {
2 |
3 | .animate {
4 | animation-duration: .3s;
5 | animation-fill-mode: both;
6 | }
7 |
8 | @keyframes fadeIn {
9 | 0% {
10 | opacity: 0;
11 | }
12 |
13 | to {
14 | opacity: 1;
15 | }
16 | }
17 |
18 | @keyframes fadeOut {
19 | 0% {
20 | opacity: 1;
21 | }
22 |
23 | to {
24 | opacity: 0;
25 | }
26 | }
27 |
28 | .fade-enter {
29 | animation-name: fadeIn;
30 | }
31 |
32 | .fade-leave {
33 | animation-name: fadeOut;
34 | }
35 |
36 | @keyframes slideInUp {
37 | 0% {
38 | transform: translate3d(0, 100%, 0);
39 | visibility: visible;
40 | }
41 |
42 | to {
43 | transform: translateZ(0);
44 | }
45 | }
46 |
47 | @keyframes slideInDown {
48 | 0% {
49 | transform: translateZ(0);
50 | visibility: visible;
51 | }
52 |
53 | to {
54 | transform: translate3d(0, 100%, 0);
55 | }
56 | }
57 |
58 | @keyframes slideInLeft {
59 | 0% {
60 | transform: translate3d(100%, 0, 0);
61 | visibility: visible;
62 | }
63 |
64 | to {
65 | transform: translateZ(0);
66 | }
67 | }
68 |
69 | @keyframes slideInRight {
70 | 0% {
71 | transform: translateZ(0);
72 | visibility: visible;
73 | }
74 |
75 | to {
76 | transform: translate3d(100%, 0, 0);
77 | }
78 | }
79 |
80 | .slideV-enter {
81 | animation-name: slideInUp;
82 | }
83 |
84 | .slideV-leave {
85 | animation-name: slideInDown;
86 | }
87 |
88 | .slideH-enter {
89 | animation-name: slideInLeft;
90 | }
91 |
92 | .slideH-leave {
93 | animation-name: slideInRight;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/assets/common/Calendar.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} {
2 | .mask {
3 | position: fixed;
4 | width: 100%;
5 | height: 100%;
6 | top: 0;
7 | left: 0;
8 | z-index: 999;
9 | background: rgba(0, 0, 0, .5);
10 | }
11 |
12 | .content {
13 | position: fixed;
14 | display: flex;
15 | flex-direction: column;
16 | width: 100%;
17 | height: 100%;
18 | top: 0;
19 | left: 0;
20 | z-index: 999;
21 | background: #fff;
22 | }
23 |
24 | .header {
25 | margin: 5px;
26 | display: flex;
27 | flex-shrink: 0;
28 | align-items: center;
29 |
30 | .title {
31 | text-align: center;
32 | width: 100%;
33 | font-weight: 700;
34 | }
35 |
36 | .left {
37 | position: absolute;
38 | display: flex;
39 | justify-content: center;
40 | align-items: center;
41 | padding: 0 8px;
42 | height: 24px;
43 | left: 5px;
44 | top: 5px;
45 | color: #068EEF;
46 | }
47 |
48 | .right {
49 | position: absolute;
50 | display: flex;
51 | justify-content: center;
52 | align-items: center;
53 | padding: 0 8px;
54 | height: 24px;
55 | right: 5px;
56 | top: 5px;
57 | color: #068EEF;
58 | font-size: 14px;
59 | }
60 | }
61 |
62 | .timePicker {
63 | border-top: 1px #ccc solid;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/assets/common/ConfirmPanel.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} .confirm-panel {
2 | display: flex;
3 | flex-shrink: 0;
4 | justify-content: center;
5 | align-items: center;
6 | background: #eee;
7 | padding: 2px 10px;
8 | border-top: #E5E4E4 1px solid;
9 |
10 | .info {
11 | font-size: 12px;
12 |
13 | p {
14 | margin: 0;
15 | }
16 |
17 | .grey {
18 | color: #999;
19 | }
20 | }
21 |
22 | .button {
23 | padding: 5px 20px;
24 | border-radius: 5px;
25 | align-self: center;
26 | margin: 5px 0 5px auto;
27 | color: #fff;
28 | font-size: 14px;
29 | background: #1A7BE6;
30 | }
31 |
32 | .button-disable {
33 | color: #aaa;
34 | background: #d5d5d5;
35 | }
36 |
37 | .button-full {
38 | margin: 5px;
39 | width: 100%;
40 | text-align: center;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/assets/common/DatePicker.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} .date-picker {
2 | display: flex;
3 | flex-direction: column;
4 | background: #eee;
5 |
6 | .wrapper {
7 | height: auto;
8 | position: relative;
9 | }
10 |
11 | .months {
12 | background: #fff;
13 | }
14 |
15 | .load-tip {
16 | position: absolute;
17 | display: flex;
18 | justify-content: center;
19 | align-items: flex-end;
20 | left: 0;
21 | right: 0;
22 | padding: 10px 0;
23 | top: -40px;
24 | color: #bbb;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/assets/common/ShortcutPanel.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} .shortcut-panel {
2 | display: flex;
3 | flex-direction: row;
4 | flex-shrink: 0;
5 | justify-content: space-between;
6 | padding: 8px 20px;
7 | border-top: #E5E4E4 1px solid;
8 |
9 | .item {
10 | display: inline-block;
11 | color: #068EEF;
12 | font-size: 14px;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/assets/common/SingleMonth.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} .single-month {
2 | padding: 10px 2px 0;
3 | overflow: hidden;
4 |
5 | .month-title {
6 | margin-left: 15px;
7 | }
8 |
9 | .row {
10 | display: flex;
11 | padding: 3px 0;
12 | @cell-size: 35px;
13 |
14 | .cell {
15 | display: flex;
16 | flex-direction: column;
17 | width: 100/7%;
18 | justify-content: center;
19 | align-items: center;
20 |
21 | .date-wrapper {
22 | display: flex;
23 | height: @cell-size;
24 | width: 100%;
25 | justify-content: center;
26 | align-items: center;
27 | margin-bottom: 1px;
28 |
29 | .disable {
30 | color: #aaa;
31 | background: #eee;
32 | border: none;
33 | border-radius: 100%;
34 | }
35 |
36 | .date {
37 | display: flex;
38 | justify-content: center;
39 | align-items: center;
40 | width: @cell-size;
41 | height: @cell-size;
42 | flex-shrink: 0;
43 | }
44 |
45 | .grey {
46 | color: #999;
47 | }
48 |
49 | .important {
50 | border: 1px #999 solid;
51 | border-radius: 100%;
52 | }
53 |
54 | .left,
55 | .right {
56 | border: none;
57 | width: 100%;
58 | height: 100%;
59 | }
60 |
61 | .date-selected {
62 | border: none;
63 | background: rgb(26, 123, 230);
64 | color: #fff;
65 | }
66 |
67 | .selected-start {
68 | border-radius: 100% 0 0 100%;
69 | }
70 |
71 | .selected-single {
72 | border-radius: 100%;
73 | }
74 |
75 | .selected-middle {
76 | border-radius: 0;
77 | }
78 |
79 | .selected-end {
80 | border-radius: 0 100% 100% 0;
81 | }
82 | }
83 |
84 | .info {
85 | height: 15px;
86 | width: 100%;
87 | padding: 0 5px;
88 | font-size: 12px;
89 | color: #999;
90 | white-space: nowrap;
91 | text-overflow: ellipsis;
92 | overflow: hidden;
93 | text-align: center;
94 | }
95 |
96 | .date-selected {
97 | color: rgb(26, 123, 230);
98 | }
99 | }
100 | }
101 |
102 | .row+.row {
103 | margin-top: 6px;
104 | }
105 |
106 | .row-xl+.row-xl {
107 | margin-top: 21px;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/assets/common/TimePicker.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} .time-picker {
2 | flex-shrink: 0;
3 | text-align: center;
4 | background: #fff;
5 |
6 | .title {
7 | font-size: 13px;
8 | padding: 5px 0;
9 | border-top: #E5E4E4 1px solid;
10 | border-bottom: #E5E4E4 1px solid;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/assets/common/WeekPanel.less:
--------------------------------------------------------------------------------
1 | .@{prefixClass} .week-panel {
2 | background: #fff;
3 | display: flex;
4 | flex-shrink: 0;
5 | padding: 0 2px;
6 | border-bottom: 1px #ccc solid;
7 |
8 | .cell {
9 | height: 24px;
10 | display: flex;
11 | width: 100/7%;
12 | justify-content: center;
13 | align-items: center;
14 | }
15 |
16 | .cell-grey {
17 | color: #999;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/assets/common/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/common/index.less:
--------------------------------------------------------------------------------
1 | @prefixClass: rmc-calendar;
2 | .@{prefixClass} {
3 | font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", "WenQuanYi Micro Hei", sans-serif;
4 | }
5 |
6 | .@{prefixClass}-hidden {
7 | display: none;
8 | }
9 |
10 | @import "./Animation.less";
11 | @import "./Calendar.less";
12 | @import "./WeekPanel.less";
13 | @import "./DatePicker.less";
14 | @import "./ConfirmPanel.less";
15 | @import "./TimePicker.less";
16 | @import "./SingleMonth.less";
17 | @import "./ShortcutPanel.less";
18 |
--------------------------------------------------------------------------------
/assets/index.less:
--------------------------------------------------------------------------------
1 | @import './common/index.less';
2 |
--------------------------------------------------------------------------------
/examples/basic.html:
--------------------------------------------------------------------------------
1 | placeholder
--------------------------------------------------------------------------------
/examples/basic.tsx:
--------------------------------------------------------------------------------
1 | /* tslint:disable:no-console */
2 |
3 | import 'rmc-picker/assets/index.css';
4 | import 'rmc-date-picker/assets/index.css';
5 | import 'rmc-calendar/assets/index.less';
6 |
7 | import React from 'react';
8 | import ReactDOM from 'react-dom';
9 | import { Calendar, ExtraData, CalendarPropsType } from '../src';
10 |
11 | import zhCN from '../src/locale/zh_CN';
12 | import enUS from '../src/locale/en_US';
13 | const en = location.search.indexOf('en') !== -1;
14 |
15 | const extra: { [key: string]: ExtraData } = {
16 | 1501516800000: { info: '建军节' },
17 | '2017/06/14': { info: '禁止选择', disable: true },
18 | '2017/06/15': { info: 'Disable', disable: true },
19 | };
20 |
21 | const now = new Date;
22 | extra[+new Date(now.getFullYear(), now.getMonth(), now.getDate() + 5)] = { info: '禁止选择', disable: true };
23 | extra[+new Date(now.getFullYear(), now.getMonth(), now.getDate() + 6)] = { info: 'Disable', disable: true };
24 | extra[+new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7)] = { info: 'Disable', disable: true };
25 | extra[+new Date(now.getFullYear(), now.getMonth(), now.getDate() + 8)] = { info: 'Disable', disable: true };
26 |
27 | for (let key in extra) {
28 | if (extra.hasOwnProperty(key)) {
29 | let info = extra[key];
30 | const date = new Date(key);
31 | if (!Number.isNaN(+date) && !extra[+date]) {
32 | extra[+date] = info;
33 | }
34 | }
35 | }
36 |
37 | class BasicDemo extends React.Component<{}, {
38 | show: boolean;
39 | config?: CalendarPropsType;
40 | startTime?: Date;
41 | endTime?: Date;
42 | }> {
43 | originbodyScrollY = document.getElementsByTagName('body')[0].style.overflowY;
44 |
45 | constructor(props: any) {
46 | super(props);
47 | this.state = {
48 | show: false,
49 | config: {},
50 | };
51 | }
52 |
53 | renderBtn(text: string, text2: string, config: CalendarPropsType = {}) {
54 | return {
56 | document.getElementsByTagName('body')[0].style.overflowY = 'hidden';
57 | this.setState({
58 | show: true,
59 | config,
60 | });
61 | }}>
62 |
{text}
63 |
{text2}
64 |
;
65 | }
66 |
67 | render() {
68 | return (
69 |
70 | {this.renderBtn('选择日期区间', 'Select Date Range')}
71 | {this.renderBtn('选择日期时间区间', 'Select DateTime Range', { pickTime: true })}
72 | {this.renderBtn('选择日期', 'Select Date', { type: 'one' })}
73 | {this.renderBtn('选择日期时间', 'Select DateTime', { type: 'one', pickTime: true })}
74 | {this.renderBtn('选择日期区间(快捷)', 'Select Date Range (Shortcut)', { showShortcut: true })}
75 | {this.renderBtn('选择日期时间区间(快捷)', 'Select DateTime Range (Shortcut)', { pickTime: true, showShortcut: true })}
76 | {this.renderBtn('水平进入', 'Horizontal Enter Aniamtion', { enterDirection: 'horizontal' })}
77 | {this.renderBtn('默认选择范围', 'Selected Date Range', { defaultValue: [new Date(+new Date - 1 * 24 * 3600 * 1000), new Date(+new Date - 4 * 24 * 3600 * 1000)] })}
78 | {this.renderBtn('onSelectAPI', 'onSelectAPI', {
79 | onSelect: (date) => {
80 | console.log('onSelect', date);
81 | return [date, new Date(+new Date - 7 * 24 * 3600 * 1000)];
82 | }
83 | })}
84 |
85 | {
86 | this.state.startTime &&
87 |
开始时间:{this.state.startTime.toLocaleString()}
88 | }
89 | {
90 | this.state.endTime &&
91 |
结束时间:{this.state.endTime.toLocaleString()}
92 | }
93 |
94 |
{
99 | document.getElementsByTagName('body')[0].style.overflowY = this.originbodyScrollY;
100 | this.setState({
101 | show: false,
102 | startTime: undefined,
103 | endTime: undefined,
104 | });
105 | }}
106 | onConfirm={(startTime, endTime) => {
107 | console.log('onConfirm', startTime, endTime);
108 | document.getElementsByTagName('body')[0].style.overflowY = this.originbodyScrollY;
109 | this.setState({
110 | show: false,
111 | startTime,
112 | endTime,
113 | });
114 | }}
115 | onSelectHasDisableDate={(dates: Date[]) => {
116 | console.warn('onSelectHasDisableDate', dates);
117 | }}
118 | getDateExtra={(date) => {
119 | return extra[+date];
120 | }}
121 | minDate={new Date(+new Date - 62 * 24 * 3600 * 1000)}
122 | maxDate={new Date(+new Date + 365 * 24 * 3600 * 1000)}
123 | />
124 |
125 | );
126 | }
127 | }
128 |
129 | ReactDOM.render(, document.getElementById('__react-content'));
130 |
--------------------------------------------------------------------------------
/index.android.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./index.ios');
2 |
--------------------------------------------------------------------------------
/index.ios.js:
--------------------------------------------------------------------------------
1 | // only for react-native examples
2 |
3 | import getList from 'react-native-index-page';
4 |
5 | getList({
6 | demos: [
7 | ],
8 | title: require('./package.json').name,
9 | });
10 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // export this package's api
2 | import Picker from './src/';
3 | export default Picker;
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rmc-calendar",
3 | "version": "1.1.4",
4 | "description": "React Mobile Calendar Component(web and react-native)",
5 | "keywords": [
6 | "react",
7 | "react-component",
8 | "react-m-calendar",
9 | "m-calendar"
10 | ],
11 | "homepage": "https://github.com/react-component/m-calendar",
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/react-component/m-calendar.git"
15 | },
16 | "bugs": {
17 | "url": "https://github.com/react-component/m-calendar/issues"
18 | },
19 | "files": [
20 | "lib",
21 | "es",
22 | "assets/*.css"
23 | ],
24 | "licenses": "MIT",
25 | "main": "./lib/index",
26 | "module": "./es/index",
27 | "config": {
28 | "port": 8021
29 | },
30 | "scripts": {
31 | "watch-tsc": "rc-tools run watch-tsc",
32 | "compile": "rc-tools run compile --babel-runtime",
33 | "build": "rc-tools run build",
34 | "gh-pages": "rc-tools run gh-pages",
35 | "start": "rc-tools run server",
36 | "pub": "rc-tools run pub --babel-runtime",
37 | "lint": "rc-tools run lint --no-js-lint",
38 | "rn-start": "node node_modules/react-native/local-cli/cli.js start",
39 | "test": "jest",
40 | "update-snap": "jest --updateSnapshot",
41 | "coverage": "jest --coverage",
42 | "coverage:upload": "npm run coverage && cat ./coverage/lcov.info | coveralls"
43 | },
44 | "jest": {
45 | "testMatch": [
46 | "**/__tests__/**/*.ts?(x)",
47 | "**/?(*.)(spec|test).ts?(x)"
48 | ],
49 | "coverageDirectory": "./coverage/",
50 | "moduleFileExtensions": [
51 | "ts",
52 | "tsx",
53 | "js"
54 | ],
55 | "collectCoverageFrom": [
56 | "src/**/*"
57 | ],
58 | "transform": {
59 | "\\.tsx?$": "./node_modules/rc-tools/scripts/jestPreprocessor.js",
60 | "\\.jsx?$": "./node_modules/rc-tools/scripts/jestPreprocessor.js"
61 | }
62 | },
63 | "dependencies": {
64 | "babel-runtime": "6.x",
65 | "rc-animate": "^2.4.1",
66 | "rmc-date-picker": "~6.0.0-alpha.10"
67 | },
68 | "devDependencies": {
69 | "@types/jest": "^21.1.2",
70 | "jest": "^21.2.1",
71 | "@types/enzyme": "^2.8.11",
72 | "enzyme": "^3.1.0",
73 | "@types/enzyme-to-json": "^1.5.0",
74 | "enzyme-to-json": "^3.1.2",
75 | "enzyme-adapter-react-15": "^1.0.1",
76 | "coveralls": "^3.0.0",
77 | "@types/react": "^16.0.10",
78 | "@types/react-dom": "^16.0.1",
79 | "pre-commit": "1.x",
80 | "rc-test": "^6.0.8",
81 | "rc-tools": "^7.0.0",
82 | "react": "^15.x",
83 | "react-dom": "^15.x",
84 | "react-test-renderer": "^15.x",
85 | "stylelint-config-standard": "^17.0.0"
86 | },
87 | "typings": "./lib/index.d.ts",
88 | "pre-commit": [
89 | "lint"
90 | ]
91 | }
92 |
--------------------------------------------------------------------------------
/src/Calendar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Animate from 'rc-animate';
3 | import TimePicker from './TimePicker';
4 |
5 | import DatePicker from './DatePicker';
6 | import ConfirmPanel from './calendar/ConfirmPanel';
7 | import ShortcutPanel from './calendar/ShortcutPanel';
8 | import AnimateWrapper from './calendar/AnimateWrapper';
9 | import Header from './calendar/Header';
10 | import { Models } from './date/DataTypes';
11 | import PropsType from './CalendarProps';
12 |
13 | import { mergeDateTime } from './util';
14 |
15 | import defaultLocale from './locale/zh_CN';
16 |
17 | export type ExtraData = Models.ExtraData;
18 | export { PropsType };
19 |
20 | export class StateType {
21 | showTimePicker: boolean = false;
22 | timePickerTitle?: string;
23 | startDate?: Date = undefined;
24 | endDate?: Date = undefined;
25 | disConfirmBtn?: boolean = true;
26 | clientHight?: number = 0;
27 | }
28 | export default class Calendar extends React.PureComponent {
29 | public static DefaultHeader = Header;
30 | public static DefaultShortcut = ShortcutPanel;
31 |
32 | static defaultProps = {
33 | visible: false,
34 | showHeader: true,
35 | locale: defaultLocale,
36 | pickTime: false,
37 | showShortcut: false,
38 | prefixCls: 'rmc-calendar',
39 | type: 'range',
40 | defaultTimeValue: new Date(2000, 0, 1, 8),
41 | } as PropsType;
42 |
43 | constructor(props: PropsType) {
44 | super(props);
45 |
46 | this.state = new StateType;
47 | if (props.defaultValue) {
48 | const defaultValue = props.defaultValue;
49 | this.state = {
50 | ...this.state,
51 | ...this.selectDate(defaultValue[1], true, { startDate: defaultValue[0] }, props),
52 | };
53 | }
54 | }
55 |
56 | componentWillReceiveProps(nextProps: PropsType) {
57 | if (!this.props.visible && nextProps.visible && nextProps.defaultValue) {
58 | this.shortcutSelect(nextProps.defaultValue[0], nextProps.defaultValue[1], nextProps);
59 | }
60 | }
61 |
62 | selectDate = (date: Date, useDateTime = false, oldState: { startDate?: Date, endDate?: Date } = {}, props = this.props) => {
63 | if (!date) return {} as StateType;
64 | let newState = {} as StateType;
65 | const { type, pickTime, defaultTimeValue, locale = {} as Models.Locale } = props;
66 | const newDate = pickTime && !useDateTime ? mergeDateTime(date, defaultTimeValue) : date;
67 | const { startDate, endDate } = oldState;
68 |
69 | switch (type) {
70 | case 'one':
71 | newState = {
72 | ...newState,
73 | startDate: newDate,
74 | disConfirmBtn: false,
75 | };
76 | if (pickTime) {
77 | newState = {
78 | ...newState,
79 | timePickerTitle: locale.selectTime,
80 | showTimePicker: true,
81 | };
82 | }
83 | break;
84 |
85 | case 'range':
86 | if (!startDate || endDate) {
87 | newState = {
88 | ...newState,
89 | startDate: newDate,
90 | endDate: undefined,
91 | disConfirmBtn: true,
92 | };
93 | if (pickTime) {
94 | newState = {
95 | ...newState,
96 | timePickerTitle: locale.selectStartTime,
97 | showTimePicker: true,
98 | };
99 | }
100 | } else {
101 | newState = {
102 | ...newState,
103 | timePickerTitle: +newDate >= +startDate ? locale.selectEndTime : locale.selectStartTime,
104 | disConfirmBtn: false,
105 | endDate: (pickTime && !useDateTime && +newDate >= +startDate) ?
106 | new Date(+mergeDateTime(newDate, startDate) + 3600000) : newDate,
107 | };
108 | }
109 | break;
110 | }
111 | return newState;
112 | }
113 |
114 | onSelectedDate = (date: Date) => {
115 | const { startDate, endDate } = this.state;
116 | const { onSelect } = this.props;
117 | if (onSelect) {
118 | let value = onSelect(date, [startDate, endDate]);
119 | if (value) {
120 | this.shortcutSelect(value[0], value[1]);
121 | return;
122 | }
123 | }
124 | this.setState(this.selectDate(date, false, { startDate, endDate }));
125 | }
126 |
127 | onSelectHasDisableDate = (date: Date[]) => {
128 | this.onClear();
129 | if (this.props.onSelectHasDisableDate) {
130 | this.props.onSelectHasDisableDate(date);
131 | }
132 | }
133 |
134 | onClose = () => {
135 | this.setState(new StateType);
136 | }
137 |
138 | onCancel = () => {
139 | this.props.onCancel && this.props.onCancel();
140 | this.onClose();
141 | }
142 |
143 | onConfirm = () => {
144 | const { onConfirm } = this.props;
145 | let { startDate, endDate } = this.state;
146 | if (startDate && endDate && +startDate > +endDate) {
147 | return onConfirm && onConfirm(endDate, startDate);
148 | }
149 | onConfirm && onConfirm(startDate, endDate);
150 | this.onClose();
151 | }
152 |
153 | onTimeChange = (date: Date) => {
154 | const { startDate, endDate } = this.state;
155 | if (endDate) {
156 | this.setState({
157 | endDate: date,
158 | });
159 | } else if (startDate) {
160 | this.setState({
161 | startDate: date,
162 | });
163 | }
164 | }
165 |
166 | onClear = () => {
167 | this.setState({
168 | startDate: undefined,
169 | endDate: undefined,
170 | showTimePicker: false,
171 | });
172 | this.props.onClear && this.props.onClear();
173 | }
174 |
175 | shortcutSelect = (startDate: Date, endDate: Date, props = this.props) => {
176 | this.setState({
177 | startDate,
178 | ...this.selectDate(endDate, true, { startDate }, props),
179 | showTimePicker: false,
180 | });
181 | }
182 |
183 | setClientHeight = (height: number) => {
184 | this.setState({
185 | clientHight: height,
186 | });
187 | }
188 |
189 | render() {
190 | const {
191 | type, locale = {} as Models.Locale, prefixCls, visible, pickTime, showShortcut, renderHeader,
192 | infiniteOpt, initalMonths, defaultDate, minDate, maxDate, getDateExtra, rowSize,
193 | defaultTimeValue, renderShortcut, enterDirection, timePickerPrefixCls, timePickerPickerPrefixCls,
194 | style,
195 | } = this.props;
196 | const {
197 | showTimePicker, timePickerTitle,
198 | startDate, endDate,
199 | disConfirmBtn, clientHight
200 | } = this.state;
201 |
202 | const headerProps = {
203 | locale,
204 | showClear: !!startDate,
205 | onCancel: this.onCancel,
206 | onClear: this.onClear,
207 | };
208 |
209 | return (
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 | {
218 | renderHeader ? renderHeader(headerProps) :
219 | }
220 |
237 | {
238 | showTimePicker &&
239 |
251 | }
252 | {
253 | showShortcut && !showTimePicker &&
254 | (
255 | renderShortcut ?
256 | renderShortcut(this.shortcutSelect) :
257 |
258 | )
259 | }
260 | {
261 | startDate &&
262 |
271 | }
272 |
273 |
274 |
275 | );
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/src/CalendarProps.ts:
--------------------------------------------------------------------------------
1 | import { Models } from './date/DataTypes';
2 | import { PropsType as HeaderPropsType } from './calendar/Header';
3 |
4 | export type SelectDateType = [Date, Date] | [Date];
5 |
6 | export default interface PropsType {
7 | /** 入场方向,default: vertical,vertical: 垂直,horizontal: 水平 */
8 | enterDirection?: 'horizontal' | 'vertical';
9 | /** 本地化 */
10 | locale?: Models.Locale;
11 | /** 关闭时回调 */
12 | onCancel?: () => void;
13 | /** 清除时回调 */
14 | onClear?: () => void;
15 | /** 确认时回调 */
16 | onConfirm?: (startDateTime?: Date, endDateTime?: Date) => void;
17 | /** 是否选择时间,default: false */
18 | pickTime?: boolean;
19 | /** (web only) 样式前缀,default: rmc-calendar */
20 | prefixCls?: string;
21 | /** 替换快捷选择栏,需要设置showShortcut: true */
22 | renderShortcut?: (select: (startDate?: Date, endDate?: Date) => void) => React.ReactNode;
23 | /** 替换标题栏 */
24 | renderHeader?: (prop: HeaderPropsType) => React.ReactNode;
25 | /** 快捷日期选择, default: false */
26 | showShortcut?: boolean;
27 | style?: React.CSSProperties;
28 | /** header title, default: {locale.title} */
29 | title?: string;
30 | /** 选择类型,default: range,one: 单日,range: 日期区间 */
31 | type?: 'one' | 'range';
32 | /** 是否显示,default: false */
33 | visible?: boolean;
34 | /** 默认选择值,开始时间、结束时间 */
35 | defaultValue?: SelectDateType;
36 |
37 | // DatePicker
38 | /** 显示开始日期,default: today */
39 | defaultDate?: Date;
40 | /** 日期扩展数据 */
41 | getDateExtra?: (date: Date) => Models.ExtraData;
42 | /** 无限滚动优化(大范围选择),default: false */
43 | infiniteOpt?: boolean;
44 | /** 初始化月个数,default: 6 */
45 | initalMonths?: number;
46 | /** 最大日期 */
47 | maxDate?: Date;
48 | /** 最小日期 */
49 | minDate?: Date;
50 | /** 选择区间包含不可用日期 */
51 | onSelectHasDisableDate?: (date: Date[]) => void;
52 | /** 选择日期回调,如果有返回值,选择范围将使用返回值 */
53 | onSelect?: (date: Date, state?: [Date | undefined, Date | undefined]) => SelectDateType | void;
54 | /** 行大小,default: normal */
55 | rowSize?: 'normal' | 'xl';
56 |
57 | // TimePicker
58 | /** 默认时间选择值 */
59 | defaultTimeValue?: Date;
60 | timePickerPrefixCls?: string;
61 | timePickerPickerPrefixCls?: string;
62 | }
63 |
--------------------------------------------------------------------------------
/src/DatePicker.base.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Models } from './date/DataTypes';
3 | import PropsType from './DatePickerProps';
4 | import { formatDate, shallowEqual } from './util';
5 |
6 | import defaultLocale from './locale/zh_CN';
7 |
8 | export interface StateType {
9 | months: Models.MonthData[];
10 | }
11 | export default abstract class DatePicker extends React.Component {
12 | static defaultProps = {
13 | prefixCls: 'rmc-calendar',
14 | infinite: false,
15 | infiniteOpt: false,
16 | defaultDate: new Date,
17 | initalMonths: 6,
18 | locale: defaultLocale,
19 | } as PropsType;
20 |
21 | visibleMonth: Models.MonthData[] = [];
22 | abstract genMonthComponent: (data: Models.MonthData) => React.ReactNode;
23 |
24 | constructor(props: PropsType) {
25 | super(props);
26 |
27 | this.state = {
28 | months: [],
29 | };
30 | }
31 |
32 | shouldComponentUpdate(nextProps: PropsType, nextState: StateType, nextContext: any) {
33 | return !shallowEqual(this.props, nextProps, ['startDate', 'endDate']) ||
34 | !shallowEqual(this.state, nextState) ||
35 | !shallowEqual(this.context, nextContext);
36 | }
37 |
38 | componentWillReceiveProps(nextProps: PropsType) {
39 | const oldValue = this.props;
40 | const newValue = nextProps;
41 |
42 | if (oldValue.startDate !== newValue.startDate || oldValue.endDate !== newValue.endDate) {
43 | if (oldValue.startDate) {
44 | this.selectDateRange(oldValue.startDate, oldValue.endDate, true);
45 | }
46 | if (newValue.startDate) {
47 | this.selectDateRange(newValue.startDate, newValue.endDate);
48 | }
49 | }
50 | }
51 |
52 | componentWillMount() {
53 | const { initalMonths = 6, defaultDate } = this.props;
54 | for (let i = 0; i < initalMonths; i++) {
55 | this.canLoadNext() && this.genMonthData(defaultDate, i);
56 | }
57 | this.visibleMonth = [...this.state.months];
58 | }
59 |
60 | getMonthDate(date = new Date, addMonth = 0) {
61 | const y = date.getFullYear(), m = date.getMonth();
62 | return {
63 | firstDate: new Date(y, m + addMonth, 1),
64 | lastDate: new Date(y, m + 1 + addMonth, 0),
65 | };
66 | }
67 |
68 | canLoadPrev() {
69 | const { minDate } = this.props;
70 | return !minDate || this.state.months.length <= 0 || +this.getMonthDate(minDate).firstDate < +this.state.months[0].firstDate;
71 | }
72 |
73 | canLoadNext() {
74 | const { maxDate } = this.props;
75 | return !maxDate || this.state.months.length <= 0 || +this.getMonthDate(maxDate).firstDate > +this.state.months[this.state.months.length - 1].firstDate;
76 | }
77 |
78 | getDateWithoutTime = (date?: Date) => {
79 | if (!date) return 0;
80 | return +new Date(
81 | date.getFullYear(),
82 | date.getMonth(),
83 | date.getDate(),
84 | );
85 | }
86 |
87 | genWeekData = (firstDate: Date) => {
88 | const minDateTime = this.getDateWithoutTime(this.props.minDate);
89 | const maxDateTime = this.getDateWithoutTime(this.props.maxDate) || Number.POSITIVE_INFINITY;
90 |
91 | const weeks: Models.CellData[][] = [];
92 | const nextMonth = this.getMonthDate(firstDate, 1).firstDate;
93 | let currentDay = firstDate;
94 | let currentWeek: Models.CellData[] = [];
95 | weeks.push(currentWeek);
96 |
97 | let startWeekday = currentDay.getDay();
98 | if (startWeekday > 0) {
99 | for (let i = 0; i < startWeekday; i++) {
100 | currentWeek.push({} as Models.CellData);
101 | }
102 | }
103 | while (currentDay < nextMonth) {
104 | if (currentWeek.length === 7) {
105 | currentWeek = [];
106 | weeks.push(currentWeek);
107 | }
108 | const dayOfMonth = currentDay.getDate();
109 | const tick = +currentDay;
110 | currentWeek.push({
111 | tick,
112 | dayOfMonth,
113 | selected: Models.SelectType.None,
114 | isFirstOfMonth: dayOfMonth === 1,
115 | isLastOfMonth: false,
116 | outOfDate: tick < minDateTime || tick > maxDateTime,
117 | });
118 | currentDay = new Date(currentDay.getTime() + 3600 * 24 * 1000);
119 | }
120 | currentWeek[currentWeek.length - 1].isLastOfMonth = true;
121 | return weeks;
122 | }
123 |
124 | genMonthData(date?: Date, addMonth: number = 0) {
125 | if (!date) {
126 | date = addMonth >= 0 ? this.state.months[this.state.months.length - 1].firstDate : this.state.months[0].firstDate;
127 | }
128 | if (!date) {
129 | date = new Date;
130 | }
131 | const { locale } = this.props;
132 | const { firstDate, lastDate } = this.getMonthDate(date, addMonth);
133 |
134 | const weeks = this.genWeekData(firstDate);
135 | const title = formatDate(firstDate, locale ? locale.monthTitle : 'yyyy/MM', this.props.locale);
136 | const data = {
137 | title,
138 | firstDate,
139 | lastDate,
140 | weeks,
141 | } as Models.MonthData;
142 | data.component = this.genMonthComponent(data);
143 | if (addMonth >= 0) {
144 | this.state.months.push(data);
145 | } else {
146 | this.state.months.unshift(data);
147 | }
148 | const { startDate, endDate } = this.props;
149 | if (startDate) {
150 | this.selectDateRange(startDate, endDate);
151 | }
152 | return data;
153 | }
154 |
155 | inDate(date: number, tick: number) {
156 | return date <= tick && tick < date + 24 * 3600000;
157 | }
158 |
159 | selectDateRange = (startDate: Date, endDate?: Date, clear = false) => {
160 | const { getDateExtra, type, onSelectHasDisableDate } = this.props;
161 | if (type === 'one') {
162 | endDate = undefined;
163 | }
164 | const time1 = this.getDateWithoutTime(startDate), time2 = this.getDateWithoutTime(endDate);
165 | const startDateTick = !time2 || time1 < time2 ? time1 : time2;
166 | const endDateTick = time2 && time1 > time2 ? time1 : time2;
167 |
168 | const startMonthDate = this.getMonthDate(new Date(startDateTick)).firstDate;
169 | const endMonthDate = endDateTick ? new Date(endDateTick) : this.getMonthDate(new Date(startDateTick)).lastDate;
170 |
171 | let unuseable: number[] = [], needUpdate = false;
172 | this.state.months
173 | .filter(m => {
174 | return m.firstDate >= startMonthDate && m.firstDate <= endMonthDate;
175 | })
176 | .forEach(m => {
177 | m.weeks.forEach(w => w
178 | .filter(d => {
179 | if (!endDateTick) {
180 | return d.tick && this.inDate(startDateTick, d.tick);
181 | } else {
182 | return d.tick && d.tick >= startDateTick && d.tick <= endDateTick;
183 | }
184 | })
185 | .forEach(d => {
186 | const oldValue = d.selected;
187 | if (clear) {
188 | d.selected = Models.SelectType.None;
189 | } else {
190 | const info = getDateExtra && getDateExtra(new Date(d.tick)) || {};
191 | if (d.outOfDate || info.disable) {
192 | unuseable.push(d.tick);
193 | }
194 | if (this.inDate(startDateTick, d.tick)) {
195 | if (type === 'one') {
196 | d.selected = Models.SelectType.Single;
197 | } else if (!endDateTick) {
198 | d.selected = Models.SelectType.Only;
199 | } else if (startDateTick !== endDateTick) {
200 | d.selected = Models.SelectType.Start;
201 | } else {
202 | d.selected = Models.SelectType.All;
203 | }
204 | } else if (this.inDate(endDateTick, d.tick)) {
205 | d.selected = Models.SelectType.End;
206 | } else {
207 | d.selected = Models.SelectType.Middle;
208 | }
209 | }
210 | needUpdate = needUpdate || d.selected !== oldValue;
211 | })
212 | );
213 | if (needUpdate && m.componentRef) {
214 | m.componentRef.updateWeeks();
215 | m.componentRef.forceUpdate();
216 | };
217 | });
218 | if (unuseable.length > 0) {
219 | if (onSelectHasDisableDate) {
220 | onSelectHasDisableDate(unuseable.map(tick => new Date(tick)));
221 | } else {
222 | console.warn('Unusable date. You can handle by onSelectHasDisableDate.', unuseable);
223 | }
224 | }
225 | }
226 |
227 | computeVisible = (clientHeight: number, scrollTop: number) => {
228 | let needUpdate = false;
229 | const MAX_VIEW_PORT = clientHeight * 2;
230 | const MIN_VIEW_PORT = clientHeight;
231 |
232 | // 大缓冲区外过滤规则
233 | const filterFunc = (vm: Models.MonthData) => vm.y && vm.height && (vm.y + vm.height > scrollTop - MAX_VIEW_PORT && vm.y < scrollTop + clientHeight + MAX_VIEW_PORT);
234 |
235 | if (this.props.infiniteOpt && this.visibleMonth.length > 12) {
236 | this.visibleMonth = this.visibleMonth.filter(filterFunc).sort((a, b) => +a.firstDate - +b.firstDate);
237 | }
238 |
239 | // 当小缓冲区不满时填充
240 | if (this.visibleMonth.length > 0) {
241 | const last = this.visibleMonth[this.visibleMonth.length - 1];
242 | if (last.y !== undefined && last.height && last.y + last.height < scrollTop + clientHeight + MIN_VIEW_PORT) {
243 | const lastIndex = this.state.months.indexOf(last);
244 | for (let i = 1; i <= 2; i++) {
245 | const index = lastIndex + i;
246 | if (index < this.state.months.length && this.visibleMonth.indexOf(this.state.months[index]) < 0) {
247 | this.visibleMonth.push(this.state.months[index]);
248 | } else {
249 | this.canLoadNext() && this.genMonthData(undefined, 1);
250 | }
251 | }
252 | needUpdate = true;
253 | }
254 |
255 | const first = this.visibleMonth[0];
256 | if (first.y !== undefined && first.height && first.y > scrollTop - MIN_VIEW_PORT) {
257 | const firstIndex = this.state.months.indexOf(first);
258 | for (let i = 1; i <= 2; i++) {
259 | const index = firstIndex - i;
260 | if (index >= 0 && this.visibleMonth.indexOf(this.state.months[index]) < 0) {
261 | this.visibleMonth.unshift(this.state.months[index]);
262 | needUpdate = true;
263 | }
264 | }
265 | }
266 | } else if (this.state.months.length > 0) {
267 | this.visibleMonth = this.state.months.filter(filterFunc);
268 | needUpdate = true;
269 | }
270 |
271 | return needUpdate;
272 | }
273 |
274 | createOnScroll = () => {
275 | let timer: any;
276 | let clientHeight = 0, scrollTop = 0;
277 |
278 | return (data: { full: number, client: number, top: number }) => {
279 | const { client, top } = data;
280 | clientHeight = client;
281 | scrollTop = top;
282 |
283 | if (timer) {
284 | return;
285 | }
286 |
287 | timer = setTimeout(() => {
288 | timer = undefined;
289 | if (this.computeVisible(clientHeight, scrollTop)) {
290 | this.forceUpdate();
291 | }
292 | }, 64);
293 | };
294 | }
295 |
296 | onCellClick = (day: Models.CellData) => {
297 | if (!day.tick) return;
298 | this.props.onCellClick && this.props.onCellClick(new Date(day.tick));
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/src/DatePicker.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import PropsType from './DatePickerProps';
3 | import Component from './DatePicker.base';
4 | import WeekPanel from './date/WeekPanel';
5 | import SingleMonth from './date/SingleMonth';
6 | import { Models } from './date/DataTypes';
7 |
8 | export { PropsType };
9 | export default class DatePicker extends Component {
10 |
11 | panel: HTMLDivElement;
12 | transform: string = '';
13 |
14 | genMonthComponent = (data?: Models.MonthData) => {
15 | if (!data) return;
16 |
17 | return {
24 | // FIXME?: sometimes will callback twice, and the second is null, when use preact.
25 | data.componentRef = dom || data.componentRef || undefined;
26 | data.updateLayout = () => {
27 | this.computeHeight(data, dom);
28 | };
29 | data.updateLayout();
30 | }}
31 | />;
32 | }
33 |
34 | computeHeight = (data: Models.MonthData, singleMonth: SingleMonth | null) => {
35 | if (singleMonth && singleMonth.wrapperDivDOM) {
36 | // preact, ref时dom有可能无height, offsetTop数据。
37 | if (!data.height && !singleMonth.wrapperDivDOM.clientHeight) {
38 | setTimeout(() => this.computeHeight(data, singleMonth), 500);
39 | return;
40 | }
41 | data.height = singleMonth.wrapperDivDOM.clientHeight || data.height || 0;
42 | data.y = singleMonth.wrapperDivDOM.offsetTop || data.y || 0;
43 | }
44 | }
45 |
46 | setLayout = (dom: HTMLDivElement) => {
47 | if (dom) {
48 | const { onLayout } = this.props;
49 | onLayout && onLayout(dom.clientHeight);
50 |
51 | const scrollHandler = this.createOnScroll();
52 | dom.onscroll = (evt) => {
53 | scrollHandler({
54 | client: dom.clientHeight,
55 | full: (evt.currentTarget as HTMLDivElement).clientHeight,
56 | top: (evt.currentTarget as HTMLDivElement).scrollTop,
57 | });
58 | };
59 | }
60 | }
61 |
62 | setPanel = (dom: HTMLDivElement) => {
63 | this.panel = dom;
64 | }
65 |
66 | // tslint:disable-next-line:member-ordering
67 | touchHandler = (() => {
68 | const initDelta = 0;
69 | let lastY = 0;
70 | let delta = initDelta;
71 |
72 | return {
73 | onTouchStart: (evt: React.TouchEvent) => {
74 | lastY = evt.touches[0].screenY;
75 | delta = initDelta;
76 | },
77 | onTouchMove: (evt: React.TouchEvent) => {
78 | const ele = evt.currentTarget;
79 | const isReachTop = ele.scrollTop === 0;
80 |
81 | if (isReachTop) {
82 | delta = evt.touches[0].screenY - lastY;
83 | if (delta > 0) {
84 | evt.preventDefault();
85 | if (delta > 80) {
86 | delta = 80;
87 | }
88 | } else {
89 | delta = 0;
90 | }
91 | this.setTransform(this.panel.style, `translate3d(0,${delta}px,0)`);
92 | }
93 | },
94 |
95 | onTouchEnd: () => {
96 | this.touchHandler.onFinish();
97 | },
98 |
99 | onTouchCancel: () => {
100 | this.touchHandler.onFinish();
101 | },
102 |
103 | onFinish: () => {
104 | if (delta > 40 && this.canLoadPrev()) {
105 | this.genMonthData(this.state.months[0].firstDate, -1);
106 |
107 | this.visibleMonth = this.state.months.slice(0, this.props.initalMonths);
108 |
109 | this.state.months.forEach((m) => {
110 | m.updateLayout && m.updateLayout();
111 | });
112 | this.forceUpdate();
113 | }
114 | this.setTransform(this.panel.style, `translate3d(0,0,0)`);
115 | this.setTransition(this.panel.style, '.3s');
116 | setTimeout(() => {
117 | this.panel && this.setTransition(this.panel.style, '');
118 | }, 300);
119 | }
120 | };
121 | })();
122 |
123 | setTransform(nodeStyle: CSSStyleDeclaration, value: any) {
124 | this.transform = value;
125 | nodeStyle.transform = value;
126 | nodeStyle.webkitTransform = value;
127 | }
128 |
129 | setTransition(nodeStyle: CSSStyleDeclaration, value: any) {
130 | nodeStyle.transition = value;
131 | nodeStyle.webkitTransition = value;
132 | }
133 |
134 | render() {
135 | const { prefixCls = '', locale = {} as Models.Locale } = this.props;
136 | const style: any = {
137 | transform: this.transform,
138 | };
139 |
140 | return (
141 |
142 |
143 |
151 |
152 | {
153 | this.canLoadPrev() &&
{locale.loadPrevMonth}
154 | }
155 |
156 | {
157 | this.state.months.map((m) => {
158 | const hidden = m.height && this.visibleMonth.indexOf(m) < 0;
159 | if (hidden) {
160 | return
;
161 | }
162 | return m.component;
163 | })
164 | }
165 |
166 |
167 |
168 |
169 | );
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/DatePickerProps.ts:
--------------------------------------------------------------------------------
1 | import { Models } from './date/DataTypes';
2 |
3 | export default interface PropsType {
4 | /** 默认日期,default: today */
5 | defaultDate?: Date;
6 | /** 选择值 */
7 | startDate?: Date;
8 | /** 选择值 */
9 | endDate?: Date;
10 | /** 日期扩展数据 */
11 | getDateExtra?: (date: Date) => Models.ExtraData;
12 | /** 无限滚动优化(大范围选择),default: false */
13 | infiniteOpt?: boolean;
14 | /** 初始化月个数,default: 6 */
15 | initalMonths?: number;
16 | /** 本地化 */
17 | locale?: Models.Locale;
18 | /** 最大日期 */
19 | maxDate?: Date;
20 | /** 最小日期 */
21 | minDate?: Date;
22 | /** 日期点击回调 */
23 | onCellClick?: (date: Date) => void;
24 | onLayout?: (clientHight: number) => void;
25 | /** 选择区间包含不可用日期 */
26 | onSelectHasDisableDate?: (date: Date[]) => void;
27 | /** (web only) 样式前缀 */
28 | prefixCls?: string;
29 | /** 行大小 */
30 | rowSize?: 'normal' | 'xl';
31 | /** 选择类型,default: range,one: 单日,range: 日期区间 */
32 | type?: 'one' | 'range';
33 | }
34 |
--------------------------------------------------------------------------------
/src/TimePicker.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import DateTimePicker from 'rmc-date-picker';
3 | import { Models } from './date/DataTypes';
4 |
5 | export interface PropsType {
6 | locale: Models.Locale;
7 | prefixCls?: string;
8 | pickerPrefixCls?: string;
9 | title?: string;
10 | defaultValue?: Date;
11 | value?: Date;
12 | onValueChange?: (time: Date) => void;
13 |
14 | minDate?: Date;
15 | maxDate?: Date;
16 | clientHeight?: number;
17 | }
18 | export interface StateType {
19 | }
20 | export default class TimePicker extends React.PureComponent {
21 | static defaultProps = {
22 | minDate: new Date(0, 0, 0, 0, 0),
23 | maxDate: new Date(9999, 11, 31, 23, 59, 59),
24 | defaultValue: new Date(2000, 1, 1, 8),
25 | } as PropsType;
26 |
27 | onDateChange = (date: Date) => {
28 | const { onValueChange } = this.props;
29 | onValueChange && onValueChange(date);
30 | }
31 |
32 | getMinTime(date?: Date) {
33 | const minDate = this.props.minDate as Date;
34 | if (!date ||
35 | date.getFullYear() > minDate.getFullYear() ||
36 | date.getMonth() > minDate.getMonth() ||
37 | date.getDate() > minDate.getDate()
38 | ) {
39 | return TimePicker.defaultProps.minDate;
40 | }
41 | return minDate;
42 | }
43 |
44 | getMaxTime(date?: Date) {
45 | const maxDate = this.props.maxDate as Date;
46 | if (!date ||
47 | date.getFullYear() < maxDate.getFullYear() ||
48 | date.getMonth() < maxDate.getMonth() ||
49 | date.getDate() < maxDate.getDate()
50 | ) {
51 | return TimePicker.defaultProps.maxDate;
52 | }
53 | return maxDate;
54 | }
55 |
56 | render() {
57 | const { locale, title, value, defaultValue, prefixCls, pickerPrefixCls, clientHeight } = this.props;
58 | const date = value || defaultValue || undefined;
59 | const height = (clientHeight && clientHeight * 3 / 8 - 52) || Number.POSITIVE_INFINITY;
60 |
61 | return (
62 |
63 |
{title}
64 |
164 || height < 0 ? 164 : height,
69 | overflow: 'hidden'
70 | } as React.CSSProperties}
71 | mode="time"
72 | date={date}
73 | locale={locale}
74 | minDate={this.getMinTime(date)}
75 | maxDate={this.getMaxTime(date)}
76 | onDateChange={this.onDateChange}
77 | use12Hours
78 | />
79 |
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/calendar/AnimateWrapper.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export interface PropsType {
4 | visible: boolean;
5 | className?: string;
6 | displayType?: string;
7 | }
8 | export default class AnimateWrapper extends React.PureComponent {
9 | static defaultProps = {
10 | className: '',
11 | displayType: 'flex',
12 | } as PropsType;
13 |
14 | render() {
15 | const { className, displayType, visible } = this.props;
16 |
17 | return
20 | {visible && this.props.children}
21 |
;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/calendar/ConfirmPanel.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { formatDate } from '../util';
3 | import { Models } from '../date/DataTypes';
4 |
5 | export interface ConfirmPanelPropsType {
6 | type?: 'one' | 'range';
7 | locale: Models.Locale;
8 | onlyConfirm?: boolean;
9 | disableBtn?: boolean;
10 | startDateTime?: Date;
11 | endDateTime?: Date;
12 | formatStr?: string;
13 | onConfirm: () => void;
14 | }
15 | export default class ConfirmPanel extends React.PureComponent {
16 | static defaultProps = {
17 | formatStr: 'yyyy-MM-dd hh:mm'
18 | } as ConfirmPanelPropsType;
19 |
20 |
21 | onConfirm = () => {
22 | const { onConfirm, disableBtn } = this.props;
23 | !disableBtn && onConfirm();
24 | }
25 |
26 | formatDate(date: Date) {
27 | const { formatStr = '', locale } = this.props;
28 | return formatDate(date, formatStr, locale);
29 | }
30 |
31 | render() {
32 | const { type, locale, disableBtn } = this.props;
33 | let { startDateTime, endDateTime } = this.props;
34 | if (startDateTime && endDateTime && +startDateTime > +endDateTime) {
35 | const tmp = startDateTime;
36 | startDateTime = endDateTime;
37 | endDateTime = tmp;
38 | }
39 |
40 | const startTimeStr = startDateTime ? this.formatDate(startDateTime) : locale.noChoose;
41 | const endTimeStr = endDateTime ? this.formatDate(endDateTime) : locale.noChoose;
42 | let btnCls = disableBtn ? 'button button-disable' : 'button';
43 | if (type === 'one') {
44 | btnCls += ' button-full';
45 | }
46 |
47 | return (
48 |
49 | {
50 | type === 'range' &&
51 |
52 |
{locale.start}: {startTimeStr}
53 |
{locale.end}: {endTimeStr}
54 |
55 | }
56 |
57 | {locale.confirm}
58 |
59 |
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/calendar/Header.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Models } from '../date/DataTypes';
3 |
4 | export interface PropsType {
5 | title?: string;
6 | locale?: Models.Locale;
7 | showClear?: boolean;
8 | onCancel?: () => void;
9 | onClear?: () => void;
10 | closeIcon?: React.ReactNode;
11 | clearIcon?: React.ReactNode;
12 | }
13 |
14 | export default class Header extends React.PureComponent {
15 | static defaultProps = {
16 | closeIcon: 'X',
17 | };
18 |
19 | render() {
20 | const {
21 | title,
22 | locale = {} as Models.Locale,
23 | onCancel,
24 | onClear,
25 | showClear,
26 | closeIcon,
27 | clearIcon,
28 | } = this.props;
29 |
30 | return (
31 |
32 | onCancel && onCancel()}>{closeIcon}
33 | {title || locale.title}
34 | {
35 | showClear &&
36 | onClear && onClear()}
38 | >{clearIcon || locale.clear}
39 | }
40 |
41 | );
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/calendar/ShortcutPanel.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Models } from '../date/DataTypes';
3 |
4 | export interface PropsType {
5 | locale: Models.Locale;
6 | onSelect: (startDate?: Date, endDate?: Date) => void;
7 | }
8 | export default class ShortcutPanel extends React.PureComponent {
9 |
10 | onClick = (type: string) => {
11 | const { onSelect } = this.props;
12 | const today = new Date;
13 |
14 | switch (type) {
15 | case 'today':
16 | onSelect(
17 | new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0),
18 | new Date(today.getFullYear(), today.getMonth(), today.getDate(), 12)
19 | );
20 | break;
21 |
22 | case 'yesterday':
23 | onSelect(
24 | new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1, 0),
25 | new Date(today.getFullYear(), today.getMonth(), today.getDate() - 1, 12)
26 | );
27 | break;
28 |
29 | case 'lastweek':
30 | onSelect(
31 | new Date(today.getFullYear(), today.getMonth(), today.getDate() - 6, 0),
32 | new Date(today.getFullYear(), today.getMonth(), today.getDate(), 12)
33 | );
34 | break;
35 |
36 | case 'lastmonth':
37 | onSelect(
38 | new Date(today.getFullYear(), today.getMonth(), today.getDate() - 29, 0),
39 | new Date(today.getFullYear(), today.getMonth(), today.getDate(), 12)
40 | );
41 | break;
42 | }
43 | }
44 |
45 | render() {
46 | const { locale } = this.props;
47 |
48 | return (
49 |
50 |
this.onClick('today')}>{locale.today}
51 |
this.onClick('yesterday')}>{locale.yesterday}
52 |
this.onClick('lastweek')}>{locale.lastWeek}
53 |
this.onClick('lastmonth')}>{locale.lastMonth}
54 |
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/date/DataTypes.ts:
--------------------------------------------------------------------------------
1 | import SingleMonth from './SingleMonth';
2 |
3 | export namespace Models {
4 | export enum SelectType {
5 | None,
6 | /** 单选 */
7 | Single,
8 | /** 起/止 */
9 | All,
10 | /** 区间仅选择了 起 */
11 | Only,
12 | /** 区间起 */
13 | Start,
14 | /** 区间中 */
15 | Middle,
16 | /** 区间止 */
17 | End,
18 | }
19 |
20 | export interface CellData {
21 | tick: number;
22 | dayOfMonth: number;
23 | selected: SelectType;
24 | isFirstOfMonth: boolean;
25 | isLastOfMonth: boolean;
26 | outOfDate: boolean;
27 | }
28 |
29 | export interface ExtraData {
30 | /** 扩展信息 */
31 | info?: string;
32 | /** 是否禁止选择 */
33 | disable?: boolean;
34 | /** (web only) 附加cell样式 className */
35 | cellCls?: any;
36 | cellRender?: (date: Date) => React.ReactNode;
37 | }
38 |
39 | export interface MonthData {
40 | title: string;
41 | firstDate: Date;
42 | lastDate: Date;
43 | weeks: Models.CellData[][];
44 | component?: React.ReactNode;
45 | height?: number;
46 | y?: number;
47 | updateLayout?: Function;
48 | componentRef?: SingleMonth;
49 | }
50 |
51 | export interface Locale {
52 | title: string;
53 | today: string;
54 | month: string;
55 | year: string;
56 | am: string;
57 | pm: string;
58 | dateFormat: string;
59 | dateTimeFormat: string;
60 | noChoose: string;
61 | week: string[];
62 | clear: string;
63 | selectTime: string;
64 | selectStartTime: string;
65 | selectEndTime: string;
66 | start: string;
67 | end: string;
68 | begin: string;
69 | over: string;
70 | begin_over: string;
71 | confirm: string;
72 | monthTitle: string;
73 | loadPrevMonth: string;
74 | yesterday: string;
75 | lastWeek: string;
76 | lastMonth: string;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/date/SingleMonth.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Models } from './DataTypes';
3 |
4 | export interface PropsType {
5 | locale: Models.Locale;
6 | monthData: Models.MonthData;
7 | rowSize?: 'normal' | 'xl';
8 | getDateExtra?: (date: Date) => Models.ExtraData;
9 | onCellClick?: (data: Models.CellData, monthData: Models.MonthData) => void;
10 | }
11 | export default class SingleMonth extends React.PureComponent {
14 | static defaultProps = {
15 | rowSize: 'normal',
16 | } as PropsType;
17 |
18 | public wrapperDivDOM: HTMLDivElement | null;
19 |
20 | constructor(props: PropsType) {
21 | super(props);
22 |
23 | this.state = {
24 | weekComponents: [],
25 | };
26 | }
27 |
28 | componentWillMount() {
29 | this.props.monthData.weeks.forEach((week, index) => {
30 | this.genWeek(week, index);
31 | });
32 | }
33 |
34 | genWeek = (weeksData: Models.CellData[], index: number) => {
35 | const { getDateExtra, monthData, onCellClick, locale, rowSize } = this.props;
36 | let rowCls = 'row';
37 | if (rowSize === 'xl') {
38 | rowCls += ' row-xl';
39 | }
40 | this.state.weekComponents[index] = (
41 |
42 | {
43 | weeksData.map((day, dayOfWeek) => {
44 | const extra = (getDateExtra && getDateExtra(new Date(day.tick))) || {};
45 | let info = extra.info;
46 | const disable = extra.disable || day.outOfDate;
47 |
48 | let cls = 'date';
49 | let lCls = 'left';
50 | let rCls = 'right';
51 | let infoCls = 'info';
52 |
53 | if (dayOfWeek === 0 || dayOfWeek === 6) {
54 | cls += ' grey';
55 | }
56 |
57 | if (disable) {
58 | cls += ' disable';
59 | } else if (info) {
60 | cls += ' important';
61 | }
62 |
63 | if (day.selected) {
64 | cls += ' date-selected';
65 | let styleType = day.selected;
66 | switch (styleType) {
67 | case Models.SelectType.Only:
68 | info = locale.begin;
69 | infoCls += ' date-selected';
70 | break;
71 | case Models.SelectType.All:
72 | info = locale.begin_over;
73 | infoCls += ' date-selected';
74 | break;
75 |
76 | case Models.SelectType.Start:
77 | info = locale.begin;
78 | infoCls += ' date-selected';
79 | if (dayOfWeek === 6 || day.isLastOfMonth) {
80 | styleType = Models.SelectType.All;
81 | }
82 | break;
83 | case Models.SelectType.Middle:
84 | if (dayOfWeek === 0 || day.isFirstOfMonth) {
85 | if (day.isLastOfMonth || dayOfWeek === 6) {
86 | styleType = Models.SelectType.All;
87 | } else {
88 | styleType = Models.SelectType.Start;
89 | }
90 | } else if (dayOfWeek === 6 || day.isLastOfMonth) {
91 | styleType = Models.SelectType.End;
92 | }
93 | break;
94 | case Models.SelectType.End:
95 | info = locale.over;
96 | infoCls += ' date-selected';
97 | if (dayOfWeek === 0 || day.isFirstOfMonth) {
98 | styleType = Models.SelectType.All;
99 | }
100 | break;
101 | }
102 |
103 | switch (styleType) {
104 | case Models.SelectType.Single:
105 | case Models.SelectType.Only:
106 | case Models.SelectType.All:
107 | cls += ' selected-single';
108 | break;
109 | case Models.SelectType.Start:
110 | cls += ' selected-start';
111 | rCls += ' date-selected';
112 | break;
113 | case Models.SelectType.Middle:
114 | cls += ' selected-middle';
115 | lCls += ' date-selected';
116 | rCls += ' date-selected';
117 | break;
118 | case Models.SelectType.End:
119 | cls += ' selected-end';
120 | lCls += ' date-selected';
121 | break;
122 | }
123 | }
124 |
125 | const defaultContent = [
126 |
127 |
128 |
129 | {day.dayOfMonth}
130 |
131 |
132 |
133 | ,
134 |
{info}
135 | ];
136 |
137 | return (
138 |
{
139 | !disable && onCellClick && onCellClick(day, monthData);
140 | }}>
141 | {
142 | extra.cellRender ?
143 | extra.cellRender(new Date(day.tick))
144 | :
145 | defaultContent
146 | }
147 |
148 | );
149 | })
150 | }
151 |
152 | );
153 | }
154 |
155 | updateWeeks = (monthData?: Models.MonthData) => {
156 | (monthData || this.props.monthData).weeks.forEach((week, index) => {
157 | this.genWeek(week, index);
158 | });
159 | }
160 |
161 | componentWillReceiveProps(nextProps: PropsType) {
162 | if (this.props.monthData !== nextProps.monthData) {
163 | this.updateWeeks(nextProps.monthData);
164 | }
165 | }
166 |
167 | setWarpper = (dom: HTMLDivElement) => {
168 | this.wrapperDivDOM = dom;
169 | }
170 |
171 | render() {
172 | const { title } = this.props.monthData;
173 | const { weekComponents } = this.state;
174 |
175 | return (
176 |
177 |
178 | {title}
179 |
180 |
181 | {weekComponents}
182 |
183 |
184 | );
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/src/date/WeekPanel.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import { Models } from './DataTypes';
3 |
4 | export interface PropsType {
5 | locale: Models.Locale;
6 | }
7 |
8 | export default class WeekPanel extends React.PureComponent {
9 | constructor(props: PropsType) {
10 | super(props);
11 | }
12 |
13 | render() {
14 | const { locale } = this.props;
15 | const { week } = locale;
16 | return (
17 |
18 |
{week[0]}
19 |
{week[1]}
20 |
{week[2]}
21 |
{week[3]}
22 |
{week[4]}
23 |
{week[5]}
24 |
{week[6]}
25 |
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Models } from './date/DataTypes';
2 |
3 | export { default as Calendar, ExtraData, PropsType as CalendarPropsType } from './Calendar';
4 | export { default as DatePicker, PropsType as DatePickerPropsType } from './DatePicker';
5 |
6 | import zhCN from './locale/zh_CN';
7 | import enUS from './locale/en_US';
8 | const Locale = { zhCN, enUS };
9 |
10 | type LocaleType = Models.Locale;
11 | export { Locale, LocaleType };
12 |
--------------------------------------------------------------------------------
/src/locale/en_US.ts:
--------------------------------------------------------------------------------
1 | import { Models } from '../date/DataTypes';
2 |
3 | const locale: Models.Locale = {
4 | title: 'Calendar',
5 | today: 'Today',
6 | month: 'Month',
7 | year: 'Year',
8 | am: 'AM',
9 | pm: 'PM',
10 | dateTimeFormat: 'MM/dd/yyyy w hh:mm',
11 | dateFormat: 'yyyy/MM/dd w',
12 | noChoose: 'No Choose',
13 | week: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fir', 'Sat'],
14 | clear: 'Clear',
15 | selectTime: 'Select Time',
16 | selectStartTime: 'Select Start Time',
17 | selectEndTime: 'Select End Time',
18 | start: 'Start',
19 | end: 'End',
20 | begin: 'Start',
21 | over: 'End',
22 | begin_over: 'S/E',
23 | confirm: 'Confirm',
24 | monthTitle: 'yyyy/MM',
25 | loadPrevMonth: 'Load Prev Month',
26 | yesterday: 'Yesterday',
27 | lastWeek: 'Last Week',
28 | lastMonth: 'Last Month',
29 | };
30 | export default locale;
31 |
--------------------------------------------------------------------------------
/src/locale/pt_BR.ts:
--------------------------------------------------------------------------------
1 | import { Models } from '../date/DataTypes';
2 |
3 | const locale: Models.Locale = {
4 | title: 'Calendário',
5 | today: 'Hoje',
6 | month: 'Mês',
7 | year: 'Ano',
8 | am: 'AM',
9 | pm: 'PM',
10 | dateTimeFormat: 'dd/MM/yyyy w hh:mm',
11 | dateFormat: 'dd/MM/yyyy w',
12 | noChoose: 'Não Escolhido',
13 | week: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sab'],
14 | clear: 'Limpar',
15 | selectTime: 'Selecionar Tempo',
16 | selectStartTime: 'Selecione Tempo Inicial',
17 | selectEndTime: 'Selecione Tempo Final',
18 | start: 'Início',
19 | end: 'Fim',
20 | begin: 'Início',
21 | over: 'Fim',
22 | begin_over: 'S/E',
23 | confirm: 'Confirmar',
24 | monthTitle: 'MM/yyyy',
25 | loadPrevMonth: 'Carregar Mês Anterior',
26 | yesterday: 'Ontem',
27 | lastWeek: 'Última Semana',
28 | lastMonth: 'Último Mês',
29 | };
30 | export default locale;
31 |
--------------------------------------------------------------------------------
/src/locale/zh_CN.ts:
--------------------------------------------------------------------------------
1 | import { Models } from '../date/DataTypes';
2 |
3 | const locale: Models.Locale = {
4 | title: '日期选择',
5 | today: '今天',
6 | month: '月',
7 | year: '年',
8 | am: '上午',
9 | pm: '下午',
10 | dateTimeFormat: 'yyyy年MM月dd日 星期w hh:mm',
11 | dateFormat: 'yyyy年MM月dd日 星期w',
12 | noChoose: '未选择',
13 | week: ['日', '一', '二', '三', '四', '五', '六'],
14 | clear: '清除',
15 | selectTime: '选择时间',
16 | selectStartTime: '选择开始时间',
17 | selectEndTime: '选择结束时间',
18 | start: '开始',
19 | end: '结束',
20 | begin: '起',
21 | over: '止',
22 | begin_over: '起/止',
23 | confirm: '确认',
24 | monthTitle: 'yyyy年MM月',
25 | loadPrevMonth: '加载上一个月',
26 | yesterday: '昨天',
27 | lastWeek: '近一周',
28 | lastMonth: '近一个月',
29 | };
30 | export default locale;
31 |
--------------------------------------------------------------------------------
/src/util/index.ts:
--------------------------------------------------------------------------------
1 | import { Models } from '../date/DataTypes';
2 |
3 | export const mergeDateTime = (date?: Date, time?: Date) => {
4 | date = date || new Date;
5 | if (!time) return date;
6 | return new Date(
7 | date.getFullYear(),
8 | date.getMonth(),
9 | date.getDate(),
10 | time.getHours(),
11 | time.getMinutes(),
12 | time.getSeconds()
13 | );
14 | };
15 |
16 | export const formatDate = (date: Date, format: string, locale?: Models.Locale) => {
17 | const week = locale && locale.week;
18 |
19 | let o: { [key: string]: any } = {
20 | 'M+': date.getMonth() + 1,
21 | 'd+': date.getDate(),
22 | 'h+': date.getHours(),
23 | 'm+': date.getMinutes(),
24 | 's+': date.getSeconds(),
25 | 'q+': Math.floor((date.getMonth() + 3) / 3),
26 | 'w+': week && week[date.getDay()],
27 | 'S': date.getMilliseconds(),
28 | };
29 | if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
30 | for (let k in o) {
31 | if (new RegExp('(' + k + ')').test(format)) {
32 | format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length));
33 | }
34 | }
35 | return format;
36 | };
37 |
38 | const hasOwnProperty = Object.prototype.hasOwnProperty;
39 | function is(x: any, y: any): boolean {
40 | if (x === y) {
41 | return x !== 0 || y !== 0 || 1 / x === 1 / y;
42 | } else {
43 | return x !== x && y !== y;
44 | }
45 | }
46 |
47 | export function shallowEqual(objA: any, objB: any, exclude: string[] = []): boolean {
48 | if (is(objA, objB)) {
49 | return true;
50 | }
51 |
52 | if (typeof objA !== 'object' || objA === null ||
53 | typeof objB !== 'object' || objB === null) {
54 | return false;
55 | }
56 |
57 | const keysA = Object.keys(objA);
58 | const keysB = Object.keys(objB);
59 |
60 | if (keysA.length !== keysB.length) {
61 | return false;
62 | }
63 |
64 | for (let i = 0; i < keysA.length; i++) {
65 | if (exclude.indexOf(keysA[i]) >= 0) continue;
66 |
67 | if (
68 | !hasOwnProperty.call(objB, keysA[i]) ||
69 | !is(objA[keysA[i]], objB[keysA[i]])
70 | ) {
71 | return false;
72 | }
73 | }
74 |
75 | return true;
76 | }
77 |
--------------------------------------------------------------------------------
/tests/Calendar.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Adapter from 'enzyme-adapter-react-15';
3 | import { default as Enzyme, render } from 'enzyme';
4 | import { renderToJson } from 'enzyme-to-json';
5 | import { Calendar, Locale } from '../src';
6 |
7 | Enzyme.configure({ adapter: new Adapter() });
8 |
9 | describe('Calendar', () => {
10 | it('base.', () => {
11 | const wrapper = render(
12 |
15 | );
16 | expect(renderToJson(wrapper)).toMatchSnapshot();
17 | });
18 |
19 | it('show shortcut.', () => {
20 | const wrapper = render(
21 |
26 | );
27 | expect(renderToJson(wrapper)).toMatchSnapshot();
28 | });
29 | });
30 |
31 | describe('Calendar english.', () => {
32 | it('renders correctly', () => {
33 | const wrapper = render(
34 |
39 | );
40 | expect(renderToJson(wrapper)).toMatchSnapshot();
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/tests/DatePicker.spec.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Adapter from 'enzyme-adapter-react-15';
3 | import { default as Enzyme, render } from 'enzyme';
4 | import { renderToJson } from 'enzyme-to-json';
5 | import { DatePicker } from '../src';
6 |
7 | Enzyme.configure({ adapter: new Adapter() });
8 |
9 | describe('DatePicker', () => {
10 | it('renders correctly', () => {
11 | const wrapper = render(
12 |
14 | );
15 | expect(renderToJson(wrapper)).toMatchSnapshot();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/tests/__snapshots__/DatePicker.spec.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`DatePicker renders correctly 1`] = `
4 |
7 |
10 |
13 | 日
14 |
15 |
18 | 一
19 |
20 |
23 | 二
24 |
25 |
28 | 三
29 |
30 |
33 | 四
34 |
35 |
38 | 五
39 |
40 |
43 | 六
44 |
45 |
46 |
50 |
53 |
56 | 加载上一个月
57 |
58 |
61 |
64 |
67 | 2018年06月
68 |
69 |
72 |
75 |
95 |
115 |
135 |
155 |
175 |
178 |
181 |
184 |
187 | 1
188 |
189 |
192 |
193 |
196 |
197 |
200 |
203 |
206 |
209 | 2
210 |
211 |
214 |
215 |
218 |
219 |
220 |
223 |
226 |
229 |
232 |
235 | 3
236 |
237 |
240 |
241 |
244 |
245 |
248 |
251 |
254 |
257 | 4
258 |
259 |
262 |
263 |
266 |
267 |
270 |
273 |
276 |
279 | 5
280 |
281 |
284 |
285 |
288 |
289 |
292 |
295 |
298 |
301 | 6
302 |
303 |
306 |
307 |
310 |
311 |
314 |
317 |
320 |
323 | 7
324 |
325 |
328 |
329 |
332 |
333 |
336 |
339 |
342 |
345 | 8
346 |
347 |
350 |
351 |
354 |
355 |
358 |
361 |
364 |
367 | 9
368 |
369 |
372 |
373 |
376 |
377 |
378 |
381 |
384 |
387 |
390 |
393 | 10
394 |
395 |
398 |
399 |
402 |
403 |
406 |
409 |
412 |
415 | 11
416 |
417 |
420 |
421 |
424 |
425 |
428 |
431 |
434 |
437 | 12
438 |
439 |
442 |
443 |
446 |
447 |
450 |
453 |
456 |
459 | 13
460 |
461 |
464 |
465 |
468 |
469 |
472 |
475 |
478 |
481 | 14
482 |
483 |
486 |
487 |
490 |
491 |
494 |
497 |
500 |
503 | 15
504 |
505 |
508 |
509 |
512 |
513 |
516 |
519 |
522 |
525 | 16
526 |
527 |
530 |
531 |
534 |
535 |
536 |
539 |
542 |
545 |
548 |
551 | 17
552 |
553 |
556 |
557 |
560 |
561 |
564 |
567 |
570 |
573 | 18
574 |
575 |
578 |
579 |
582 |
583 |
586 |
589 |
592 |
595 | 19
596 |
597 |
600 |
601 |
604 |
605 |
608 |
611 |
614 |
617 | 20
618 |
619 |
622 |
623 |
626 |
627 |
630 |
633 |
636 |
639 | 21
640 |
641 |
644 |
645 |
648 |
649 |
652 |
655 |
658 |
661 | 22
662 |
663 |
666 |
667 |
670 |
671 |
674 |
677 |
680 |
683 | 23
684 |
685 |
688 |
689 |
692 |
693 |
694 |
697 |
700 |
703 |
706 |
709 | 24
710 |
711 |
714 |
715 |
718 |
719 |
722 |
725 |
728 |
731 | 25
732 |
733 |
736 |
737 |
740 |
741 |
744 |
747 |
750 |
753 | 26
754 |
755 |
758 |
759 |
762 |
763 |
766 |
769 |
772 |
775 | 27
776 |
777 |
780 |
781 |
784 |
785 |
788 |
791 |
794 |
797 | 28
798 |
799 |
802 |
803 |
806 |
807 |
810 |
813 |
816 |
819 | 29
820 |
821 |
824 |
825 |
828 |
829 |
832 |
835 |
838 |
841 | 30
842 |
843 |
846 |
847 |
850 |
851 |
852 |
853 |
854 |
857 |
860 | 2018年07月
861 |
862 |
865 |
868 |
871 |
874 |
877 |
880 | 1
881 |
882 |
885 |
886 |
889 |
890 |
893 |
896 |
899 |
902 | 2
903 |
904 |
907 |
908 |
911 |
912 |
915 |
918 |
921 |
924 | 3
925 |
926 |
929 |
930 |
933 |
934 |
937 |
940 |
943 |
946 | 4
947 |
948 |
951 |
952 |
955 |
956 |
959 |
962 |
965 |
968 | 5
969 |
970 |
973 |
974 |
977 |
978 |
981 |
984 |
987 |
990 | 6
991 |
992 |
995 |
996 |
999 |
1000 |
1003 |
1006 |
1009 |
1012 | 7
1013 |
1014 |
1017 |
1018 |
1021 |
1022 |
1023 |
1026 |
1029 |
1032 |
1035 |
1038 | 8
1039 |
1040 |
1043 |
1044 |
1047 |
1048 |
1051 |
1054 |
1057 |
1060 | 9
1061 |
1062 |
1065 |
1066 |
1069 |
1070 |
1073 |
1076 |
1079 |
1082 | 10
1083 |
1084 |
1087 |
1088 |
1091 |
1092 |
1095 |
1098 |
1101 |
1104 | 11
1105 |
1106 |
1109 |
1110 |
1113 |
1114 |
1117 |
1120 |
1123 |
1126 | 12
1127 |
1128 |
1131 |
1132 |
1135 |
1136 |
1139 |
1142 |
1145 |
1148 | 13
1149 |
1150 |
1153 |
1154 |
1157 |
1158 |
1161 |
1164 |
1167 |
1170 | 14
1171 |
1172 |
1175 |
1176 |
1179 |
1180 |
1181 |
1184 |
1187 |
1190 |
1193 |
1196 | 15
1197 |
1198 |
1201 |
1202 |
1205 |
1206 |
1209 |
1212 |
1215 |
1218 | 16
1219 |
1220 |
1223 |
1224 |
1227 |
1228 |
1231 |
1234 |
1237 |
1240 | 17
1241 |
1242 |
1245 |
1246 |
1249 |
1250 |
1253 |
1256 |
1259 |
1262 | 18
1263 |
1264 |
1267 |
1268 |
1271 |
1272 |
1275 |
1278 |
1281 |
1284 | 19
1285 |
1286 |
1289 |
1290 |
1293 |
1294 |
1297 |
1300 |
1303 |
1306 | 20
1307 |
1308 |
1311 |
1312 |
1315 |
1316 |
1319 |
1322 |
1325 |
1328 | 21
1329 |
1330 |
1333 |
1334 |
1337 |
1338 |
1339 |
1342 |
1345 |
1348 |
1351 |
1354 | 22
1355 |
1356 |
1359 |
1360 |
1363 |
1364 |
1367 |
1370 |
1373 |
1376 | 23
1377 |
1378 |
1381 |
1382 |
1385 |
1386 |
1389 |
1392 |
1395 |
1398 | 24
1399 |
1400 |
1403 |
1404 |
1407 |
1408 |
1411 |
1414 |
1417 |
1420 | 25
1421 |
1422 |
1425 |
1426 |
1429 |
1430 |
1433 |
1436 |
1439 |
1442 | 26
1443 |
1444 |
1447 |
1448 |
1451 |
1452 |
1455 |
1458 |
1461 |
1464 | 27
1465 |
1466 |
1469 |
1470 |
1473 |
1474 |
1477 |
1480 |
1483 |
1486 | 28
1487 |
1488 |
1491 |
1492 |
1495 |
1496 |
1497 |
1500 |
1503 |
1506 |
1509 |
1512 | 29
1513 |
1514 |
1517 |
1518 |
1521 |
1522 |
1525 |
1528 |
1531 |
1534 | 30
1535 |
1536 |
1539 |
1540 |
1543 |
1544 |
1547 |
1550 |
1553 |
1556 | 31
1557 |
1558 |
1561 |
1562 |
1565 |
1566 |
1567 |
1568 |
1569 |
1572 |
1575 | 2018年08月
1576 |
1577 |
1580 |
1583 |
1586 |
1589 |
1592 |
1595 |
1598 |
1599 |
1602 |
1603 |
1606 |
1609 |
1612 |
1615 |
1618 |
1619 |
1622 |
1623 |
1626 |
1629 |
1632 |
1635 |
1638 |
1639 |
1642 |
1643 |
1646 |
1649 |
1652 |
1655 | 1
1656 |
1657 |
1660 |
1661 |
1664 |
1665 |
1668 |
1671 |
1674 |
1677 | 2
1678 |
1679 |
1682 |
1683 |
1686 |
1687 |
1690 |
1693 |
1696 |
1699 | 3
1700 |
1701 |
1704 |
1705 |
1708 |
1709 |
1712 |
1715 |
1718 |
1721 | 4
1722 |
1723 |
1726 |
1727 |
1730 |
1731 |
1732 |
1735 |
1738 |
1741 |
1744 |
1747 | 5
1748 |
1749 |
1752 |
1753 |
1756 |
1757 |
1760 |
1763 |
1766 |
1769 | 6
1770 |
1771 |
1774 |
1775 |
1778 |
1779 |
1782 |
1785 |
1788 |
1791 | 7
1792 |
1793 |
1796 |
1797 |
1800 |
1801 |
1804 |
1807 |
1810 |
1813 | 8
1814 |
1815 |
1818 |
1819 |
1822 |
1823 |
1826 |
1829 |
1832 |
1835 | 9
1836 |
1837 |
1840 |
1841 |
1844 |
1845 |
1848 |
1851 |
1854 |
1857 | 10
1858 |
1859 |
1862 |
1863 |
1866 |
1867 |
1870 |
1873 |
1876 |
1879 | 11
1880 |
1881 |
1884 |
1885 |
1888 |
1889 |
1890 |
1893 |
1896 |
1899 |
1902 |
1905 | 12
1906 |
1907 |
1910 |
1911 |
1914 |
1915 |
1918 |
1921 |
1924 |
1927 | 13
1928 |
1929 |
1932 |
1933 |
1936 |
1937 |
1940 |
1943 |
1946 |
1949 | 14
1950 |
1951 |
1954 |
1955 |
1958 |
1959 |
1962 |
1965 |
1968 |
1971 | 15
1972 |
1973 |
1976 |
1977 |
1980 |
1981 |
1984 |
1987 |
1990 |
1993 | 16
1994 |
1995 |
1998 |
1999 |
2002 |
2003 |
2006 |
2009 |
2012 |
2015 | 17
2016 |
2017 |
2020 |
2021 |
2024 |
2025 |
2028 |
2031 |
2034 |
2037 | 18
2038 |
2039 |
2042 |
2043 |
2046 |
2047 |
2048 |
2051 |
2054 |
2057 |
2060 |
2063 | 19
2064 |
2065 |
2068 |
2069 |
2072 |
2073 |
2076 |
2079 |
2082 |
2085 | 20
2086 |
2087 |
2090 |
2091 |
2094 |
2095 |
2098 |
2101 |
2104 |
2107 | 21
2108 |
2109 |
2112 |
2113 |
2116 |
2117 |
2120 |
2123 |
2126 |
2129 | 22
2130 |
2131 |
2134 |
2135 |
2138 |
2139 |
2142 |
2145 |
2148 |
2151 | 23
2152 |
2153 |
2156 |
2157 |
2160 |
2161 |
2164 |
2167 |
2170 |
2173 | 24
2174 |
2175 |
2178 |
2179 |
2182 |
2183 |
2186 |
2189 |
2192 |
2195 | 25
2196 |
2197 |
2200 |
2201 |
2204 |
2205 |
2206 |
2209 |
2212 |
2215 |
2218 |
2221 | 26
2222 |
2223 |
2226 |
2227 |
2230 |
2231 |
2234 |
2237 |
2240 |
2243 | 27
2244 |
2245 |
2248 |
2249 |
2252 |
2253 |
2256 |
2259 |
2262 |
2265 | 28
2266 |
2267 |
2270 |
2271 |
2274 |
2275 |
2278 |
2281 |
2284 |
2287 | 29
2288 |
2289 |
2292 |
2293 |
2296 |
2297 |
2300 |
2303 |
2306 |
2309 | 30
2310 |
2311 |
2314 |
2315 |
2318 |
2319 |
2322 |
2325 |
2328 |
2331 | 31
2332 |
2333 |
2336 |
2337 |
2340 |
2341 |
2342 |
2343 |
2344 |
2347 |
2350 | 2018年09月
2351 |
2352 |
2355 |
2358 |
2361 |
2364 |
2367 |
2370 |
2373 |
2374 |
2377 |
2378 |
2381 |
2384 |
2387 |
2390 |
2393 |
2394 |
2397 |
2398 |
2401 |
2404 |
2407 |
2410 |
2413 |
2414 |
2417 |
2418 |
2421 |
2424 |
2427 |
2430 |
2433 |
2434 |
2437 |
2438 |
2441 |
2444 |
2447 |
2450 |
2453 |
2454 |
2457 |
2458 |
2461 |
2464 |
2467 |
2470 |
2473 |
2474 |
2477 |
2478 |
2481 |
2484 |
2487 |
2490 | 1
2491 |
2492 |
2495 |
2496 |
2499 |
2500 |
2501 |
2504 |
2507 |
2510 |
2513 |
2516 | 2
2517 |
2518 |
2521 |
2522 |
2525 |
2526 |
2529 |
2532 |
2535 |
2538 | 3
2539 |
2540 |
2543 |
2544 |
2547 |
2548 |
2551 |
2554 |
2557 |
2560 | 4
2561 |
2562 |
2565 |
2566 |
2569 |
2570 |
2573 |
2576 |
2579 |
2582 | 5
2583 |
2584 |
2587 |
2588 |
2591 |
2592 |
2595 |
2598 |
2601 |
2604 | 6
2605 |
2606 |
2609 |
2610 |
2613 |
2614 |
2617 |
2620 |
2623 |
2626 | 7
2627 |
2628 |
2631 |
2632 |
2635 |
2636 |
2639 |
2642 |
2645 |
2648 | 8
2649 |
2650 |
2653 |
2654 |
2657 |
2658 |
2659 |
2662 |
2665 |
2668 |
2671 |
2674 | 9
2675 |
2676 |
2679 |
2680 |
2683 |
2684 |
2687 |
2690 |
2693 |
2696 | 10
2697 |
2698 |
2701 |
2702 |
2705 |
2706 |
2709 |
2712 |
2715 |
2718 | 11
2719 |
2720 |
2723 |
2724 |
2727 |
2728 |
2731 |
2734 |
2737 |
2740 | 12
2741 |
2742 |
2745 |
2746 |
2749 |
2750 |
2753 |
2756 |
2759 |
2762 | 13
2763 |
2764 |
2767 |
2768 |
2771 |
2772 |
2775 |
2778 |
2781 |
2784 | 14
2785 |
2786 |
2789 |
2790 |
2793 |
2794 |
2797 |
2800 |
2803 |
2806 | 15
2807 |
2808 |
2811 |
2812 |
2815 |
2816 |
2817 |
2820 |
2823 |
2826 |
2829 |
2832 | 16
2833 |
2834 |
2837 |
2838 |
2841 |
2842 |
2845 |
2848 |
2851 |
2854 | 17
2855 |
2856 |
2859 |
2860 |
2863 |
2864 |
2867 |
2870 |
2873 |
2876 | 18
2877 |
2878 |
2881 |
2882 |
2885 |
2886 |
2889 |
2892 |
2895 |
2898 | 19
2899 |
2900 |
2903 |
2904 |
2907 |
2908 |
2911 |
2914 |
2917 |
2920 | 20
2921 |
2922 |
2925 |
2926 |
2929 |
2930 |
2933 |
2936 |
2939 |
2942 | 21
2943 |
2944 |
2947 |
2948 |
2951 |
2952 |
2955 |
2958 |
2961 |
2964 | 22
2965 |
2966 |
2969 |
2970 |
2973 |
2974 |
2975 |
2978 |
2981 |
2984 |
2987 |
2990 | 23
2991 |
2992 |
2995 |
2996 |
2999 |
3000 |
3003 |
3006 |
3009 |
3012 | 24
3013 |
3014 |
3017 |
3018 |
3021 |
3022 |
3025 |
3028 |
3031 |
3034 | 25
3035 |
3036 |
3039 |
3040 |
3043 |
3044 |
3047 |
3050 |
3053 |
3056 | 26
3057 |
3058 |
3061 |
3062 |
3065 |
3066 |
3069 |
3072 |
3075 |
3078 | 27
3079 |
3080 |
3083 |
3084 |
3087 |
3088 |
3091 |
3094 |
3097 |
3100 | 28
3101 |
3102 |
3105 |
3106 |
3109 |
3110 |
3113 |
3116 |
3119 |
3122 | 29
3123 |
3124 |
3127 |
3128 |
3131 |
3132 |
3133 |
3136 |
3139 |
3142 |
3145 |
3148 | 30
3149 |
3150 |
3153 |
3154 |
3157 |
3158 |
3159 |
3160 |
3161 |
3164 |
3167 | 2018年10月
3168 |
3169 |
3172 |
3175 |
3178 |
3181 |
3184 |
3187 |
3190 |
3191 |
3194 |
3195 |
3198 |
3201 |
3204 |
3207 | 1
3208 |
3209 |
3212 |
3213 |
3216 |
3217 |
3220 |
3223 |
3226 |
3229 | 2
3230 |
3231 |
3234 |
3235 |
3238 |
3239 |
3242 |
3245 |
3248 |
3251 | 3
3252 |
3253 |
3256 |
3257 |
3260 |
3261 |
3264 |
3267 |
3270 |
3273 | 4
3274 |
3275 |
3278 |
3279 |
3282 |
3283 |
3286 |
3289 |
3292 |
3295 | 5
3296 |
3297 |
3300 |
3301 |
3304 |
3305 |
3308 |
3311 |
3314 |
3317 | 6
3318 |
3319 |
3322 |
3323 |
3326 |
3327 |
3328 |
3331 |
3334 |
3337 |
3340 |
3343 | 7
3344 |
3345 |
3348 |
3349 |
3352 |
3353 |
3356 |
3359 |
3362 |
3365 | 8
3366 |
3367 |
3370 |
3371 |
3374 |
3375 |
3378 |
3381 |
3384 |
3387 | 9
3388 |
3389 |
3392 |
3393 |
3396 |
3397 |
3400 |
3403 |
3406 |
3409 | 10
3410 |
3411 |
3414 |
3415 |
3418 |
3419 |
3422 |
3425 |
3428 |
3431 | 11
3432 |
3433 |
3436 |
3437 |
3440 |
3441 |
3444 |
3447 |
3450 |
3453 | 12
3454 |
3455 |
3458 |
3459 |
3462 |
3463 |
3466 |
3469 |
3472 |
3475 | 13
3476 |
3477 |
3480 |
3481 |
3484 |
3485 |
3486 |
3489 |
3492 |
3495 |
3498 |
3501 | 14
3502 |
3503 |
3506 |
3507 |
3510 |
3511 |
3514 |
3517 |
3520 |
3523 | 15
3524 |
3525 |
3528 |
3529 |
3532 |
3533 |
3536 |
3539 |
3542 |
3545 | 16
3546 |
3547 |
3550 |
3551 |
3554 |
3555 |
3558 |
3561 |
3564 |
3567 | 17
3568 |
3569 |
3572 |
3573 |
3576 |
3577 |
3580 |
3583 |
3586 |
3589 | 18
3590 |
3591 |
3594 |
3595 |
3598 |
3599 |
3602 |
3605 |
3608 |
3611 | 19
3612 |
3613 |
3616 |
3617 |
3620 |
3621 |
3624 |
3627 |
3630 |
3633 | 20
3634 |
3635 |
3638 |
3639 |
3642 |
3643 |
3644 |
3647 |
3650 |
3653 |
3656 |
3659 | 21
3660 |
3661 |
3664 |
3665 |
3668 |
3669 |
3672 |
3675 |
3678 |
3681 | 22
3682 |
3683 |
3686 |
3687 |
3690 |
3691 |
3694 |
3697 |
3700 |
3703 | 23
3704 |
3705 |
3708 |
3709 |
3712 |
3713 |
3716 |
3719 |
3722 |
3725 | 24
3726 |
3727 |
3730 |
3731 |
3734 |
3735 |
3738 |
3741 |
3744 |
3747 | 25
3748 |
3749 |
3752 |
3753 |
3756 |
3757 |
3760 |
3763 |
3766 |
3769 | 26
3770 |
3771 |
3774 |
3775 |
3778 |
3779 |
3782 |
3785 |
3788 |
3791 | 27
3792 |
3793 |
3796 |
3797 |
3800 |
3801 |
3802 |
3805 |
3808 |
3811 |
3814 |
3817 | 28
3818 |
3819 |
3822 |
3823 |
3826 |
3827 |
3830 |
3833 |
3836 |
3839 | 29
3840 |
3841 |
3844 |
3845 |
3848 |
3849 |
3852 |
3855 |
3858 |
3861 | 30
3862 |
3863 |
3866 |
3867 |
3870 |
3871 |
3874 |
3877 |
3880 |
3883 | 31
3884 |
3885 |
3888 |
3889 |
3892 |
3893 |
3894 |
3895 |
3896 |
3899 |
3902 | 2018年11月
3903 |
3904 |
3907 |
3910 |
3913 |
3916 |
3919 |
3922 |
3925 |
3926 |
3929 |
3930 |
3933 |
3936 |
3939 |
3942 |
3945 |
3946 |
3949 |
3950 |
3953 |
3956 |
3959 |
3962 |
3965 |
3966 |
3969 |
3970 |
3973 |
3976 |
3979 |
3982 |
3985 |
3986 |
3989 |
3990 |
3993 |
3996 |
3999 |
4002 | 1
4003 |
4004 |
4007 |
4008 |
4011 |
4012 |
4015 |
4018 |
4021 |
4024 | 2
4025 |
4026 |
4029 |
4030 |
4033 |
4034 |
4037 |
4040 |
4043 |
4046 | 3
4047 |
4048 |
4051 |
4052 |
4055 |
4056 |
4057 |
4060 |
4063 |
4066 |
4069 |
4072 | 4
4073 |
4074 |
4077 |
4078 |
4081 |
4082 |
4085 |
4088 |
4091 |
4094 | 5
4095 |
4096 |
4099 |
4100 |
4103 |
4104 |
4107 |
4110 |
4113 |
4116 | 6
4117 |
4118 |
4121 |
4122 |
4125 |
4126 |
4129 |
4132 |
4135 |
4138 | 7
4139 |
4140 |
4143 |
4144 |
4147 |
4148 |
4151 |
4154 |
4157 |
4160 | 8
4161 |
4162 |
4165 |
4166 |
4169 |
4170 |
4173 |
4176 |
4179 |
4182 | 9
4183 |
4184 |
4187 |
4188 |
4191 |
4192 |
4195 |
4198 |
4201 |
4204 | 10
4205 |
4206 |
4209 |
4210 |
4213 |
4214 |
4215 |
4218 |
4221 |
4224 |
4227 |
4230 | 11
4231 |
4232 |
4235 |
4236 |
4239 |
4240 |
4243 |
4246 |
4249 |
4252 | 12
4253 |
4254 |
4257 |
4258 |
4261 |
4262 |
4265 |
4268 |
4271 |
4274 | 13
4275 |
4276 |
4279 |
4280 |
4283 |
4284 |
4287 |
4290 |
4293 |
4296 | 14
4297 |
4298 |
4301 |
4302 |
4305 |
4306 |
4309 |
4312 |
4315 |
4318 | 15
4319 |
4320 |
4323 |
4324 |
4327 |
4328 |
4331 |
4334 |
4337 |
4340 | 16
4341 |
4342 |
4345 |
4346 |
4349 |
4350 |
4353 |
4356 |
4359 |
4362 | 17
4363 |
4364 |
4367 |
4368 |
4371 |
4372 |
4373 |
4376 |
4379 |
4382 |
4385 |
4388 | 18
4389 |
4390 |
4393 |
4394 |
4397 |
4398 |
4401 |
4404 |
4407 |
4410 | 19
4411 |
4412 |
4415 |
4416 |
4419 |
4420 |
4423 |
4426 |
4429 |
4432 | 20
4433 |
4434 |
4437 |
4438 |
4441 |
4442 |
4445 |
4448 |
4451 |
4454 | 21
4455 |
4456 |
4459 |
4460 |
4463 |
4464 |
4467 |
4470 |
4473 |
4476 | 22
4477 |
4478 |
4481 |
4482 |
4485 |
4486 |
4489 |
4492 |
4495 |
4498 | 23
4499 |
4500 |
4503 |
4504 |
4507 |
4508 |
4511 |
4514 |
4517 |
4520 | 24
4521 |
4522 |
4525 |
4526 |
4529 |
4530 |
4531 |
4534 |
4537 |
4540 |
4543 |
4546 | 25
4547 |
4548 |
4551 |
4552 |
4555 |
4556 |
4559 |
4562 |
4565 |
4568 | 26
4569 |
4570 |
4573 |
4574 |
4577 |
4578 |
4581 |
4584 |
4587 |
4590 | 27
4591 |
4592 |
4595 |
4596 |
4599 |
4600 |
4603 |
4606 |
4609 |
4612 | 28
4613 |
4614 |
4617 |
4618 |
4621 |
4622 |
4625 |
4628 |
4631 |
4634 | 29
4635 |
4636 |
4639 |
4640 |
4643 |
4644 |
4647 |
4650 |
4653 |
4656 | 30
4657 |
4658 |
4661 |
4662 |
4665 |
4666 |
4667 |
4668 |
4669 |
4670 |
4671 |
4672 |
4673 | `;
4674 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strictNullChecks": true,
4 | "moduleResolution": "node",
5 | "jsx": "preserve",
6 | "allowSyntheticDefaultImports": true,
7 | "target": "es6",
8 | "noImplicitAny": true,
9 | "noUnusedLocals": true,
10 | "rootDir": "src",
11 | "outDir": "lib"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "class-name": true,
4 | "comment-format": [
5 | true,
6 | "check-space"
7 | ],
8 | "indent": [
9 | true,
10 | "spaces"
11 | ],
12 | "member-ordering": [
13 | true,
14 | "public-before-private",
15 | "static-before-instance",
16 | "variables-before-functions"
17 | ],
18 | "no-conditional-assignment": true,
19 | "no-duplicate-variable": true,
20 | "no-eval": true,
21 | "no-internal-module": true,
22 | "no-trailing-whitespace": true,
23 | "no-unused-variable": false,
24 | "no-var-keyword": true,
25 | "one-line": [
26 | true,
27 | "check-open-brace",
28 | "check-whitespace"
29 | ],
30 | "quotemark": [
31 | true,
32 | "single",
33 | "jsx-double"
34 | ],
35 | "semicolon": [
36 | true,
37 | "always"
38 | ],
39 | "typedef-whitespace": [
40 | true,
41 | {
42 | "call-signature": "nospace",
43 | "index-signature": "nospace",
44 | "parameter": "nospace",
45 | "property-declaration": "nospace",
46 | "variable-declaration": "nospace"
47 | }
48 | ],
49 | "variable-name": [
50 | true,
51 | "ban-keywords"
52 | ],
53 | "whitespace": [
54 | true,
55 | "check-branch",
56 | "check-decl",
57 | "check-operator",
58 | "check-separator",
59 | "check-type"
60 | ]
61 | }
62 | }
--------------------------------------------------------------------------------
/typings/models.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'rc-animate';
2 | declare module 'zscroller/lib/DOMScroller';
3 |
--------------------------------------------------------------------------------