├── .babelrc
├── .eslintignore
├── .eslintrc
├── .github
├── android-dark.png
├── android.png
├── caption.png
├── dark.png
├── default.png
├── hour-minute-second.png
├── ios.png
├── preview.gif
├── year-custom_month-day.png
└── year-month-day-hour-minute.png
├── .gitignore
├── .npmignore
├── .storybook
├── config.js
└── webpack.config.js
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── examples
├── basic
│ ├── index.js
│ └── main.css
└── index.html
├── lib
├── DatePicker.js
├── DatePickerItem.js
├── Modal.js
├── dataSource.js
├── index.css
├── index.js
├── prefix.js
├── pureRender.js
└── time.js
├── package.json
├── postcss.config.js
├── rollup.config.js
├── stories
├── index.css
└── index.js
├── storybook-static
└── favicon.ico
├── test
├── event_helper.js
├── functional
│ ├── DatePickerItem_spec.js
│ ├── DatePicker_spec.js
│ ├── prefix.spec.js
│ └── time_spec.js
└── helper.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react", "stage-0"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist/*
2 | node_modules/*
3 | test/*
4 | stories/*
5 | postcss.config.js
6 | rollup.config.js
7 | examples/*
8 | coverage/*
9 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["cqaso-kit"],
3 | "rules": {
4 | "flowtype/define-flow-type": 1,
5 | "flowtype/use-flow-type": 1
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.github/android-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/android-dark.png
--------------------------------------------------------------------------------
/.github/android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/android.png
--------------------------------------------------------------------------------
/.github/caption.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/caption.png
--------------------------------------------------------------------------------
/.github/dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/dark.png
--------------------------------------------------------------------------------
/.github/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/default.png
--------------------------------------------------------------------------------
/.github/hour-minute-second.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/hour-minute-second.png
--------------------------------------------------------------------------------
/.github/ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/ios.png
--------------------------------------------------------------------------------
/.github/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/preview.gif
--------------------------------------------------------------------------------
/.github/year-custom_month-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/year-custom_month-day.png
--------------------------------------------------------------------------------
/.github/year-month-day-hour-minute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lanjingling0510/react-mobile-datepicker/279fb4b62c119cdadd8000b48e594ddd96e9ab46/.github/year-month-day-hour-minute.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | examples/__build__/
3 | .nyc_output/
4 | coverage/
5 | dist
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test
2 | examples
3 | .nyc_output
4 | coverage/
5 |
--------------------------------------------------------------------------------
/.storybook/config.js:
--------------------------------------------------------------------------------
1 | import { configure, setAddon } from '@storybook/react';
2 | import infoAddon, {setDefaults} from '@storybook/addon-info';
3 |
4 | // addon-info
5 | setDefaults({
6 | inline: true,
7 | maxPropsIntoLine: 1,
8 | maxPropObjectKeys: 10,
9 | maxPropArrayLength: 10,
10 | maxPropStringLength: 100,
11 | source: true,
12 | });
13 |
14 | setAddon(infoAddon);
15 |
16 | function loadStories() {
17 | require('../stories');
18 | }
19 |
20 | configure(loadStories, module);
21 |
--------------------------------------------------------------------------------
/.storybook/webpack.config.js:
--------------------------------------------------------------------------------
1 | // you can use this file to add your custom webpack plugins, loaders and anything you like.
2 | // This is just the basic way to add additional webpack configurations.
3 | // For more information refer the docs: https://storybook.js.org/docs/react-storybook/configurations/custom-webpack-config
4 |
5 | // IMPORTANT
6 | // When you add this file, we won't add the default configurations which is similar
7 | // to "React Create App". This only has babel loader to load JavaScript.
8 | const path = require('path');
9 | const ROOT_PATH = process.cwd();
10 |
11 | module.exports = {
12 | plugins: [
13 | // your custom plugins
14 | ],
15 |
16 | module: {
17 | rules: [
18 | // add your custom loaders.
19 | {
20 | test: /\.css$/,
21 | use: [
22 | {loader: 'style-loader'},
23 | {loader: 'css-loader'},
24 | {
25 | loader: 'postcss-loader',
26 | options: {config: {path: path.join(ROOT_PATH, 'postcss.config.js')}},
27 | },
28 | ],
29 | },
30 | ],
31 | },
32 | };
33 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - stable
5 | cache:
6 | directories:
7 | - node_modules
8 | before_install:
9 | - export CHROME_BIN=chromium-browser
10 | - export DISPLAY=:99.0
11 | - sh -e /etc/init.d/xvfb start
12 | after_success: npm run coverage
13 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | [4.0.2](../../releases/tag/4.0.2) 2019-07-21 20:57:09
2 | ---------------------------------------------------------
3 |
4 | - [01a3ec4](../../commit/01a3ec4) ✨ [feature] add characteristic of onchange and showFooter
5 |
6 |
7 | [4.0.1](../../releases/tag/4.0.1) 2019-02-21 11:11:09
8 | ---------------------------------------------------------
9 |
10 | -
11 |
12 |
13 | [4.0.0](../../releases/tag/4.0.0) 2018-10-26 12:39:53
14 | ---------------------------------------------------------
15 |
16 | - [88e8b68](../../commit/88e8b68) 📚 [document] edit readme document.
17 | - [daf0b98](../../commit/daf0b98) ✨ [feature] add feature of caption customized(https://github.com/lanjingling0510/react-mobile-datepicker/issues/31)
18 |
19 |
20 | [3.0.12](../../releases/tag/3.0.12) 2018-05-17 21:29:57
21 | -----------------------------------------------------------
22 |
23 | - [a6ec20b](../../commit/a6ec20b) 🐛 [bug]fix when scrolling 2 wheels at the same it is possible to set a date outside min/maxDate(https://github.com/lanjingling0510/react-mobile-datepicker/issues/27)
24 | - [3f6ef0d](../../commit/3f6ef0d) 🐛 [bug]fix readme.md error
25 | - [f4f3f01](../../commit/f4f3f01) 📚 [document] update readme.md
26 |
27 |
28 | [3.0.11](../../releases/tag/3.0.11) 2018-05-12 10:57:46
29 | -----------------------------------------------------------
30 |
31 | - [b6d6b5e](../../commit/b6d6b5e) ✨ [feature] map month number to month name(https://github.com/lanjingling0510/react-mobile-datepicker/issues/26)
32 |
33 |
34 | [3.0.10](../../releases/tag/3.0.10) 2018-04-24 22:50:29
35 | -----------------------------------------------------------
36 |
37 | - [99ce5da](../../commit/99ce5da) 🐛 [bug]fix the current date is not between the maximum and the minimum date.(https://github.com/lanjingling0510/react-mobile-datepicker/pull/17)
38 | - [8582c7e](../../commit/8582c7e) ✨ [feature] add test case for dateSteps(https://github.com/lanjingling0510/react-mobile-datepicker/issues/21)
39 |
40 |
41 | [3.0.9](../../releases/tag/3.0.9) 2018-04-23 10:48:04
42 | ---------------------------------------------------------
43 |
44 | - [8508a7e](../../commit/8508a7e) ✨ [feature] add test case for dateSteps
45 | - [34ff8c6](../../commit/34ff8c6) ✨ [feature] Add characteristics of set a time step
46 |
47 |
48 | [3.0.8](../../releases/tag/3.0.8) 2017-10-15 23:17:08
49 | ---------------------------------------------------------
50 |
51 | - [3297180](../../commit/3297180) ✨ [feature] Add a function: Customize the header(https://github.com/lanjingling0510/react-mobile-datepicker/pull/16)
52 |
53 |
54 | [3.0.7](../../releases/tag/3.0.7) 2017-10-12 08:11:11
55 | ---------------------------------------------------------
56 |
57 | - [9155be0](../../commit/9155be0) ✨ [feature] update some depend packages(https://github.com/lanjingling0510/react-mobile-datepicker/issues/15)
58 | - [1feaa24](../../commit/1feaa24) 🔧 [config] add react storybook.
59 | - [81dd701](../../commit/81dd701) 🔧 [config] add package-lock.json file.
60 | - [e925080](../../commit/e925080) 📚 [document] edit readme document.
61 |
62 |
63 | [3.0.6](../../releases/tag/3.0.6) 2017-07-08 20:21:30
64 | ---------------------------------------------------------
65 |
66 | - [1d847d8](../../commit/1d847d8) 📚 [document] edit readme document.
67 | - [d2c3372](../../commit/d2c3372) ✨ [feature] Add characteristics of automatic configuration Year, Month, Day, Hour, Minute, Second.
68 |
69 |
70 | [3.0.5](../../releases/tag/3.0.5) 2017-07-03 20:47:51
71 | ---------------------------------------------------------
72 |
73 | - [cec3c8a](../../commit/cec3c8a) ✨ [feature] add feature of button customized.(https://github.com/lanjingling0510/react-mobile-datepicker/issues/3)
74 | - [49d2fc7](../../commit/49d2fc7) 🐛 [bug]fix android4.4 TouchEvent has PageY propery (https://github.com/lanjingling0510/react-mobile-datepicker/issues/9)
75 | - [436906c](../../commit/436906c) 📚 [document] edit LICENSE.md fullname.
76 |
77 |
78 | [3.0.4](../../releases/tag/3.0.4) 2017-04-09 17:12:18
79 | ---------------------------------------------------------
80 |
81 | - [e2a2935](../../commit/e2a2935) 📦 [refact] edit test code for simulate event.
82 | - [b743448](../../commit/b743448) 🐛 [bug]fix scrolling up will refresh page.
83 |
84 |
85 | [3.0.3](../../releases/tag/3.0.3) 2017-01-05 15:06:48
86 | ---------------------------------------------------------
87 |
88 | - [61d569f](../../commit/61d569f) ✨ [feature] Support server rendering (https://github.com/lanjingling0510/react-mobile-datepicker/issues/4)
89 |
90 |
91 | [3.0.2](../../releases/tag/3.0.2) 2016-12-18 14:45:39
92 | ---------------------------------------------------------
93 |
94 | - [45bcd3e](../../commit/45bcd3e) 🐛 [bug]fix Cannot find module jsdom
95 | - [a167120](../../commit/a167120) ✨ [feature] Added an option (isPopup) [(#2)](https://github.com/lanjingling0510/react-mobile-datepicker/issues/2)
96 |
97 |
98 | v3.0.1 - Sun, 18 Sep 2016 09:37:34 GMT
99 | --------------------------------------
100 |
101 | -
102 |
103 |
104 | v3.0.0 - Sun, 18 Sep 2016 09:28:28 GMT
105 | --------------------------------------
106 |
107 | - [14b868c](../../commit/14b868c) [changed] ✅ update version,An increase of five theme,A slide can move multiple dates.
108 |
109 |
110 | v2.0.7 - Tue, 13 Sep 2016 04:44:41 GMT
111 | --------------------------------------
112 |
113 | -
114 |
115 |
116 | v2.0.7 - Sat, 10 Sep 2016 15:52:02 GMT
117 | --------------------------------------
118 |
119 | -
120 |
121 |
122 | v2.0.6 - Sat, 10 Sep 2016 10:23:41 GMT
123 | --------------------------------------
124 |
125 | -
126 |
127 |
128 | v2.0.5 - Sat, 10 Sep 2016 10:16:55 GMT
129 | --------------------------------------
130 |
131 | - [9e2df2f](../../commit/9e2df2f) [changed] add modal layer and add rollup for production
132 |
133 |
134 | v2.0.4 - Tue, 12 Jul 2016 09:16:42 GMT
135 | --------------------------------------
136 |
137 | -
138 |
139 |
140 | v2.0.3 - Tue, 12 Jul 2016 09:15:00 GMT
141 | --------------------------------------
142 |
143 | -
144 |
145 |
146 | v2.0.2 - Tue, 05 Jul 2016 00:48:26 GMT
147 | --------------------------------------
148 |
149 | -
150 |
151 |
152 | v2.0.1 - Mon, 04 Jul 2016 14:45:41 GMT
153 | --------------------------------------
154 |
155 | -
156 |
157 |
158 | v2.0.0 - Mon, 04 Jul 2016 10:48:22 GMT
159 | --------------------------------------
160 |
161 | -
162 |
163 |
164 | v1.0.16 - Mon, 27 Jun 2016 09:08:47 GMT
165 | ---------------------------------------
166 |
167 | - [4516b14](../../commit/4516b14) [changed] 修改finish-btn行高
168 |
169 |
170 | v1.0.15 - Sun, 26 Jun 2016 04:09:49 GMT
171 | ---------------------------------------
172 |
173 | -
174 |
175 |
176 | v1.0.14 - Sun, 26 Jun 2016 03:38:28 GMT
177 | ---------------------------------------
178 |
179 | - [2025c43](../../commit/2025c43) [added] 添加README.md关键词
180 |
181 |
182 | v1.0.13 - Sun, 26 Jun 2016 03:20:53 GMT
183 | ---------------------------------------
184 |
185 | -
186 |
187 |
188 | v1.0.12 - Sun, 26 Jun 2016 02:20:39 GMT
189 | ---------------------------------------
190 |
191 | - [37441d7](../../commit/37441d7) [added] 添加注释, 测试用例
192 |
193 |
194 | v1.0.11 - Fri, 24 Jun 2016 02:35:43 GMT
195 | ---------------------------------------
196 |
197 | -
198 |
199 |
200 | v1.0.10 - Fri, 24 Jun 2016 01:55:02 GMT
201 | ---------------------------------------
202 |
203 | - [1687e8e](../../commit/1687e8e) [fixed] 取消isOpen, onCancel属性
204 |
205 |
206 | v1.0.9 - Fri, 24 Jun 2016 01:33:47 GMT
207 | --------------------------------------
208 |
209 | -
210 |
211 |
212 | v1.0.8 - Fri, 24 Jun 2016 01:32:53 GMT
213 | --------------------------------------
214 |
215 | -
216 |
217 |
218 | v1.0.7 - Fri, 24 Jun 2016 01:29:41 GMT
219 | --------------------------------------
220 |
221 | - [305fb68](../../commit/305fb68) [changed] 修改README
222 |
223 |
224 | v1.0.6 - Fri, 24 Jun 2016 01:25:40 GMT
225 | --------------------------------------
226 |
227 | - [a1f1db9](../../commit/a1f1db9) [fixed] 修复滚动快速出现的bug
228 |
229 |
230 | v1.0.5 - Thu, 23 Jun 2016 13:37:16 GMT
231 | --------------------------------------
232 |
233 | -
234 |
235 |
236 | v1.0.4 - Thu, 23 Jun 2016 13:34:36 GMT
237 | --------------------------------------
238 |
239 | -
240 |
241 |
242 | v1.0.3 - Thu, 23 Jun 2016 13:22:13 GMT
243 | --------------------------------------
244 |
245 | - [5a93fe9](../../commit/5a93fe9) [changed] 更新了READEME
246 |
247 |
248 | v1.0.2 - Thu, 23 Jun 2016 13:12:08 GMT
249 | --------------------------------------
250 |
251 | -
252 |
253 |
254 | v1.0.14 - Fri, 17 Jun 2016 07:30:27 GMT
255 | ---------------------------------------
256 |
257 | -
258 |
259 |
260 | v1.0.13 - Fri, 17 Jun 2016 06:26:17 GMT
261 | ---------------------------------------
262 |
263 | -
264 |
265 |
266 | v1.0.12 - Thu, 16 Jun 2016 15:42:47 GMT
267 | ---------------------------------------
268 |
269 | -
270 |
271 |
272 | v1.0.11 - Thu, 16 Jun 2016 14:15:13 GMT
273 | ---------------------------------------
274 |
275 | -
276 |
277 |
278 | v1.0.9 - Thu, 16 Jun 2016 12:47:16 GMT
279 | --------------------------------------
280 |
281 | -
282 |
283 |
284 | v1.0.8 - Thu, 16 Jun 2016 12:10:32 GMT
285 | --------------------------------------
286 |
287 | -
288 |
289 |
290 | v1.0.7 - Thu, 16 Jun 2016 09:09:24 GMT
291 | --------------------------------------
292 |
293 | - [6d2a00b](../../commit/6d2a00b) [added] 添加README.md
294 |
295 |
296 | v1.0.6 - Thu, 16 Jun 2016 08:54:53 GMT
297 | --------------------------------------
298 |
299 | - [9be9fe6](../../commit/9be9fe6) [added] 添加.travis.yml
300 |
301 |
302 | v1.0.5 - Thu, 16 Jun 2016 08:01:06 GMT
303 | --------------------------------------
304 |
305 | - [a2cd387](../../commit/a2cd387) [fixed] 解决changlog无效的问题
306 |
307 |
308 | v1.0.4 - Thu, 16 Jun 2016 07:40:50 GMT
309 | --------------------------------------
310 |
311 | -
312 |
313 |
314 | v1.0.3 - Thu, 16 Jun 2016 07:40:47 GMT
315 | --------------------------------------
316 |
317 | -
318 |
319 |
320 | 1.0.3 - Thu, 16 Jun 2016 07:40:35 GMT
321 | -------------------------------------
322 |
323 | -
324 |
325 |
326 | v1.0.4 - Thu, 16 Jun 2016 07:21:51 GMT
327 | --------------------------------------
328 |
329 | -
330 |
331 |
332 | v1.0.3 - Thu, 16 Jun 2016 06:20:24 GMT
333 | --------------------------------------
334 |
335 | -
336 |
337 |
338 | v1.0.2 - Thu, 16 Jun 2016 06:20:14 GMT
339 | --------------------------------------
340 |
341 | -
342 |
343 |
344 | v1.0.2 - Thu, 16 Jun 2016 01:29:56 GMT
345 | --------------------------------------
346 |
347 | -
348 |
349 |
350 | v1.0.1 - Thu, 16 Jun 2016 01:12:11 GMT
351 | --------------------------------------
352 |
353 | -
354 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2016 lanjingling0510, Inc.
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 |
6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-mobile-datepicker
2 | [![Travis][build-badge]][build] [![npm package][npm-badge]][npm] [![Coveralls][coveralls-badge]][coveralls]
3 |
4 |
5 | **a lightweight react date picker for mobile, Not more than 4k**
6 |
7 | react-mobile-datepicker provides a component that can set year, month, day, hour, minute and second by sliding up or down.
8 |
9 | ## Features
10 | - is only 4k.
11 | - It does not depend on moment.js
12 |
13 | ## Theme
14 |
15 | ### default
16 |
17 |

18 |
19 |
20 | ### dark
21 |
22 |

23 |
24 |
25 | ### ios
26 |
27 |

28 |
29 |
30 | ### android
31 |
32 |

33 |
34 |
35 | ### android-dark
36 |
37 |

38 |
39 |
40 | ## Custom date unit
41 |
42 | set `dateConfig` to configure year, month, day, hour, minute.
43 |
44 | ```javascript
45 | {
46 | 'year': {
47 | format: 'YYYY',
48 | caption: 'Year',
49 | step: 1,
50 | },
51 | 'month': {
52 | format: 'MM',
53 | caption: 'Mon',
54 | step: 1,
55 | },
56 | 'date': {
57 | format: 'DD',
58 | caption: 'Day',
59 | step: 1,
60 | },
61 | 'hour': {
62 | format: 'hh',
63 | caption: 'Hour',
64 | step: 1,
65 | },
66 | 'minute': {
67 | format: 'mm',
68 | caption: 'Min',
69 | step: 1,
70 | },
71 | 'second': {
72 | format: 'hh',
73 | caption: 'Sec',
74 | step: 1,
75 | },
76 | }
77 | ```
78 |
79 |
80 |

81 |
82 |
83 |
84 | set `dateConfig` to configure hour, minute and second.
85 |
86 | ```javascript
87 | {
88 | 'hour': {
89 | format: 'hh',
90 | caption: 'Hour',
91 | step: 1,
92 | },
93 | 'minute': {
94 | format: 'mm',
95 | caption: 'Min',
96 | step: 1,
97 | },
98 | 'second': {
99 | format: 'hh',
100 | caption: 'Sec',
101 | step: 1,
102 | },
103 | }
104 | ```
105 |
106 |
107 |

108 |
109 |
110 | customize the content mapping shown in the month.
111 |
112 | ```javascript
113 |
114 | const monthMap = {
115 | '1': 'Jan',
116 | '2': 'Feb',
117 | '3': 'Mar',
118 | '4': 'Apr',
119 | '5': 'May',
120 | '6': 'Jun',
121 | '7': 'Jul',
122 | '8': 'Aug',
123 | '9': 'Sep',
124 | '10': 'Oct',
125 | '11': 'Nov',
126 | '12': 'Dec',
127 | };
128 |
129 | const dateConfig = {
130 | 'year': {
131 | format: 'YYYY',
132 | caption: 'Year',
133 | step: 1,
134 | },
135 | 'month': {
136 | format: value => monthMap[value.getMonth() + 1],
137 | caption: 'Mon',
138 | step: 1,
139 | },
140 | 'date': {
141 | format: 'DD',
142 | caption: 'Day',
143 | step: 1,
144 | },
145 | };
146 |
147 |
150 |
151 | ```
152 |
153 |

154 |
155 |
156 | set `showCaption` to display date captions, matches the dateConfig property's caption.
157 |
158 | ```javascript
159 | const dateConfig = {
160 | 'hour': {
161 | format: 'hh',
162 | caption: 'Hour',
163 | step: 1,
164 | },
165 | 'minute': {
166 | format: 'mm',
167 | caption: 'Min',
168 | step: 1,
169 | },
170 | 'second': {
171 | format: 'hh',
172 | caption: 'Sec',
173 | step: 1,
174 | },
175 | }
176 |
177 |
181 | ```
182 |
183 |
184 |

185 |
186 |
187 |
188 | ## Getting Started
189 |
190 | ### Install
191 |
192 | Using [npm](https://www.npmjs.com/):
193 |
194 | $ npm install react-mobile-datepicker --save
195 |
196 | ### Import what you need
197 | The following guide assumes you have some sort of ES2015 build set up using babel and/or webpack/browserify/gulp/grunt/etc.
198 |
199 |
200 | ```javascript
201 | // Using an ES6 transpiler like Babel
202 | import React from 'react';
203 | import ReactDOM from 'react-dom';
204 | import DatePicker from 'react-mobile-datepicker';
205 | ```
206 |
207 |
208 | ### Usage Example
209 |
210 |
211 | ```javascript
212 | class App extends React.Component {
213 | state = {
214 | time: new Date(),
215 | isOpen: false,
216 | }
217 |
218 | handleClick = () => {
219 | this.setState({ isOpen: true });
220 | }
221 |
222 | handleCancel = () => {
223 | this.setState({ isOpen: false });
224 | }
225 |
226 | handleSelect = (time) => {
227 | this.setState({ time, isOpen: false });
228 | }
229 |
230 | render() {
231 | return (
232 |
245 | );
246 | }
247 | }
248 |
249 |
250 | ReactDOM.render(, document.getElementById('react-box'));
251 | ```
252 |
253 |
254 | ## PropTypes
255 |
256 | | Property | Type | Default | Description |
257 | |:------------- |:------------- |:-------------- |:---------- |
258 | | isPopup | Boolean | true | whether as popup add a overlay |
259 | | isOpen | Boolean | false | whether to open datepicker |
260 | | theme | String | default | theme of datepicker, include 'default', 'dark', 'ios', 'android', 'android-dark' |
261 | | ~~dateFormat~~(deprecated, use `dateConfig` instead) | Array | ['YYYY', 'M', 'D'] | according to year, month, day, hour, minute, second format specified display text. E.g ['YYYY年', 'MM月', 'DD日']|
262 | | ~~dateSteps~~(deprecated), use `dateConfig` instead | Array | [1, 1, 1] | set step for each time unit |
263 | | dateConfig | Object | [See `DateConfig` format for details](#dateconfig) | configure date unit information |
264 | |~~showFormat~~(deprecated, use `headerFormat` instead) | String | 'YYYY/MM/DD' | customize the format of the display title |
265 | |headerFormat | String | 'YYYY/MM/DD' | customize the format of the display title |
266 | | value | Date | new Date() | date value |
267 | | min | Date | new Date(1970, 0, 1) | minimum date |
268 | | max | Date | new Date(2050, 0, 1) | maximum date |
269 | | showHeader | Boolean | true | whether to show the header |
270 | | showFooter | Boolean | true | whether to show the footer |
271 | | customHeader | ReactElement | undefined | customize the header, if you set this property, it will replace `showFormat`|
272 | | confirmText | String | 完成 | customize the selection time button text |
273 | | cancelText | String | 取消 | customize the cancel button text |
274 | | onSelect | Function | () => {} | the callback function after click button of done, Date object as a parameter |
275 | | onCancel | Function | () => {} | the callback function after click button of cancel |
276 | | onChange | Function | () => {} | the callback function after date be changed |
277 |
278 |
279 | ## DateConfig
280 |
281 | all default date configuration information, as follows
282 |
283 | - format: date unit display format
284 | - caption: date unit caption
285 | - step: date unit change interval
286 |
287 | ```javascript
288 | {
289 | 'year': {
290 | format: 'YYYY',
291 | caption: 'Year',
292 | step: 1,
293 | },
294 | 'month': {
295 | format: 'M',
296 | caption: 'Mon',
297 | step: 1,
298 | },
299 | 'date': {
300 | format: 'D',
301 | caption: 'Day',
302 | step: 1,
303 | },
304 | 'hour': {
305 | format: 'hh',
306 | caption: 'Hour',
307 | step: 1,
308 | },
309 | 'minute': {
310 | format: 'mm',
311 | caption: 'Min',
312 | step: 1,
313 | },
314 | 'second': {
315 | format: 'hh',
316 | caption: 'Sec',
317 | step: 1,
318 | },
319 | }
320 |
321 | ```
322 |
323 |
324 | ## Changelog
325 | * [Changelog](CHANGELOG.md)
326 |
327 | ## How to Contribute
328 |
329 | Anyone and everyone is welcome to contribute to this project. The best way to
330 | start is by checking our [open issues](https://github.com/lanjingling0510/react-mobile-datepicker/issues),
331 | [submit a new issues](https://github.com/lanjingling0510/react-mobile-datepicker/issues/new?labels=bug) or
332 | [feature request](https://github.com/lanjingling0510/react-mobile-datepicker/issues/new?labels=enhancement),
333 | participate in discussions, upvote or downvote the issues you like or dislike.
334 |
335 |
336 |
337 |
338 | [npm-badge]: https://img.shields.io/npm/v/react-mobile-datepicker.svg?style=flat-square
339 | [npm]: https://www.npmjs.com/package/react-mobile-datepicker
340 | [build-badge]: https://img.shields.io/travis/lanjingling0510/react-mobile-datepicker/master.svg?style=flat-square
341 | [build]: https://travis-ci.org/lanjingling0510/react-mobile-datepicker
342 | [coveralls-badge]: https://img.shields.io/coveralls/lanjingling0510/react-mobile-datepicker.svg?style=flat-square
343 | [coveralls]: https://coveralls.io/github/lanjingling0510/react-mobile-datepicker
344 |
--------------------------------------------------------------------------------
/examples/basic/index.js:
--------------------------------------------------------------------------------
1 | import './main.css';
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import { convertDate } from '../../lib/time.js';
5 | import DatePicker from '../../lib/index';
6 |
7 | (function main() {
8 | class App extends React.Component {
9 | state = {
10 | time: new Date(),
11 | isOpen: false,
12 | theme: 'default',
13 | }
14 |
15 | handleToggle = (isOpen) => () => {
16 | this.setState({ isOpen });
17 | }
18 |
19 | handleThemeToggle = (theme) => () => {
20 | this.setState({ theme, isOpen: true });
21 | }
22 |
23 | handleSelect = (time) => {
24 | this.setState({ time, isOpen: false });
25 | }
26 |
27 | render() {
28 | return (
29 |
30 |
31 | {convertDate(this.state.time, 'YYYY-MM-DD')}
32 |
33 |
60 |
85 |
86 | );
87 | }
88 | }
89 |
90 |
91 | ReactDOM.render(, document.getElementById('react-box'));
92 | }());
93 |
--------------------------------------------------------------------------------
/examples/basic/main.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | .container {
7 | position: absolute;
8 | bottom: 0;
9 | left: 0;
10 | top: 0;
11 | right: 0;
12 | text-align: center;
13 | padding:1rem;
14 | background: #eee;
15 | }
16 |
17 | .select-btn {
18 | display: inline-block;
19 | border: 1px solid #ccc;
20 | padding: 1rem;
21 | margin: 1rem 0;
22 | cursor: pointer;
23 | background: #fff;
24 | &.sm {
25 | margin: .5rem .5rem;
26 | padding: .5rem;
27 | }
28 | }
29 |
30 | .select-time {
31 | margin-top: 1rem;
32 | font-size: 2rem;
33 | }
34 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/lib/DatePicker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @module DatePicker Component
3 | */
4 |
5 | import React, { Component } from 'react';
6 | import DatePickerItem from './DatePickerItem.js';
7 | import PureRender from './pureRender.js';
8 | import { convertDate, nextDate } from './time.js';
9 | import { dateConfigMap } from './dataSource';
10 |
11 | type Props = {
12 | theme: string,
13 | value: Object,
14 | min: Object,
15 | max: Object,
16 | customHeader?: React.Element<*>,
17 | showHeader: boolean,
18 | showFooter: boolean,
19 | showCaption: boolean,
20 | dateConfig: Object | Array,
21 | headerFormat: string,
22 | confirmText: string,
23 | cancelText: string,
24 | onChange: Function,
25 | onSelect: Function,
26 | onCancel: Function,
27 | }
28 |
29 | type State = {
30 | value: Date,
31 | }
32 |
33 | /**
34 | * 大写首字母
35 | * @param {String} 字符串
36 | */
37 | const capitalize = ([first, ...rest]) => first.toUpperCase() + rest.join('');
38 |
39 | /**
40 | * 判断数组
41 | * @param {any} val
42 | */
43 | const isArray = val => Object.prototype.toString.apply(val) === '[object Array]';
44 |
45 | /**
46 | * Class DatePicker Component Class
47 | * @extends Component
48 | */
49 | class DatePicker extends Component {
50 | constructor(props) {
51 | super(props);
52 | this.state = {
53 | value: nextDate(this.props.value),
54 | };
55 |
56 | if ('dateFormat' in props) {
57 | console.warn('dateFormat已经被弃用, 请使用dateConfig属性配置');
58 | }
59 |
60 | if ('dateSteps' in props) {
61 | console.warn('dateSteps已经被弃用, 请使用dateConfig属性配置');
62 | }
63 |
64 | if ('showFormat' in props) {
65 | console.warn('headerFormat, 请使用dateConfig属性');
66 | }
67 |
68 | this.handleFinishBtnClick = this.handleFinishBtnClick.bind(this);
69 | this.handleDateSelect = this.handleDateSelect.bind(this);
70 | }
71 |
72 | componentWillReceiveProps(nextProps) {
73 | // update value of state
74 | const date = nextDate(nextProps.value);
75 | if (date.getTime() !== this.state.value.getTime()) {
76 | this.setState({ value: date });
77 | }
78 | }
79 |
80 | /**
81 | * When you swipe two datepickeritems at the same time.
82 | * Prevent dates from going out.
83 | */
84 | componentDidUpdate() {
85 | const value = this.state.value;
86 | const { min, max } = this.props;
87 | if (value.getTime() > max.getTime()) {
88 | this.setState({ value: max });
89 | }
90 |
91 | if (value.getTime() < min.getTime()) {
92 | this.setState({ value: min });
93 | }
94 | }
95 |
96 | /**
97 | * Optimization component, Prevents unnecessary rendering
98 | * Only props or state change or value before re-rendering
99 | *
100 | * @param {Object} nextProps next props
101 | * @param {Object} nextState next state
102 | * @return {Boolean} Whether re-rendering
103 | */
104 | shouldComponentUpdate(nextProps, nextState) {
105 | const date = nextDate(nextState.value);
106 | return date.getTime() !== this.state.value.getTime() ||
107 | PureRender.shouldComponentUpdate(nextProps, nextState, this.props, this.state);
108 | }
109 |
110 | /**
111 | * 点击完成按钮事件
112 | * @return {undefined}
113 | */
114 | handleFinishBtnClick() {
115 | this.props.onSelect(this.state.value);
116 | }
117 |
118 | /**
119 | * 选择下一个日期
120 | * @return {undefined}
121 | */
122 | handleDateSelect(value) {
123 | this.setState({ value }, () => {
124 | this.props.onChange(value);
125 | });
126 | }
127 |
128 | /**
129 | * 格式化dateConfig
130 | * @param {*} dataConfig dateConfig属性
131 | */
132 | normalizeDateConfig(dataConfig) {
133 | const configList = [];
134 | if (isArray(dataConfig)) {
135 | for (let i = 0; i < dataConfig.length; i++) {
136 | const value = dataConfig[i];
137 | if (typeof value === 'string') {
138 | const lowerCaseKey = value.toLocaleLowerCase();
139 | configList.push({
140 | ...dateConfigMap[lowerCaseKey],
141 | type: capitalize(lowerCaseKey),
142 | });
143 | }
144 | }
145 | } else {
146 | for (const key in dataConfig) {
147 | if (dataConfig.hasOwnProperty(key)) {
148 | const lowerCaseKey = key.toLocaleLowerCase();
149 | if (dateConfigMap.hasOwnProperty(lowerCaseKey)) {
150 | configList.push({
151 | ...dateConfigMap[lowerCaseKey],
152 | ...dataConfig[key],
153 | type: capitalize(lowerCaseKey),
154 | });
155 | }
156 | }
157 | }
158 | }
159 |
160 | return configList;
161 | }
162 |
163 | /**
164 | * render函数
165 | * @return {Object} JSX对象
166 | */
167 | render() {
168 | const { min, max, theme, dateConfig, confirmText, cancelText, headerFormat, showHeader, showFooter, customHeader, showCaption } = this.props;
169 | const value = this.state.value;
170 | const themeClassName =
171 | ['default', 'dark', 'ios', 'android', 'android-dark'].indexOf(theme) === -1 ?
172 | 'default' : theme;
173 |
174 | const dataConfigList = this.normalizeDateConfig(dateConfig);
175 |
176 | return (
177 |
179 | {showHeader && (
180 |
181 | {customHeader || convertDate(value, headerFormat)}
182 |
183 | )}
184 | {showCaption && (
185 |
186 | {dataConfigList.map((item, index) => (
187 |
{item.caption}
188 | ))}
189 |
190 | )}
191 |
192 | {dataConfigList.map((item, index) => (
193 |
202 | ))}
203 |
204 | {showFooter &&
}
212 |
213 | );
214 | }
215 | }
216 |
217 | export default DatePicker;
218 |
--------------------------------------------------------------------------------
/lib/DatePickerItem.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * @module Date组件
4 | */
5 | import React, { Component } from 'react';
6 | import * as TimeUtil from './time.js';
7 | import { shallowEqual } from './pureRender.js';
8 | import { addPrefixCss, formatCss } from './prefix.js';
9 |
10 | const DATE_HEIGHT = 40; // 每个日期的高度
11 | const DATE_LENGTH = 10; // 日期的个数
12 | const MIDDLE_INDEX = Math.floor(DATE_LENGTH / 2); // 日期数组中间值的索引
13 | const MIDDLE_Y = - DATE_HEIGHT * MIDDLE_INDEX; // translateY值
14 |
15 | const isUndefined = val => typeof val === 'undefined';
16 | const isFunction = val => Object.prototype.toString.apply(val) === '[object Function]';
17 |
18 | type Props = {
19 | value: Object,
20 | min: Object,
21 | max: Object,
22 | format: string | Array<*>,
23 | step: number,
24 | onSelect: Function,
25 | }
26 |
27 | type State = {
28 | translateY: number,
29 | marginTop: number,
30 | }
31 |
32 | /**
33 | * Class Date组件类
34 | * @extends Component
35 | */
36 | class DatePickerItem extends Component {
37 | constructor(props) {
38 | super(props);
39 | this.animating = false; // 判断是否在transition过渡动画之中
40 | this.touchY = 0; // 保存touchstart的pageY
41 | this.translateY = 0; // 容器偏移的距离
42 | this.currentIndex = MIDDLE_INDEX; // 滑动中当前日期的索引
43 | this.moveDateCount = 0; // 一次滑动移动了多少个时间
44 | this._moveToTimer = null;
45 |
46 | this.state = {
47 | translateY: MIDDLE_Y,
48 | marginTop: (this.currentIndex - MIDDLE_INDEX) * DATE_HEIGHT,
49 | };
50 |
51 | this.renderDatepickerItem = this.renderDatepickerItem.bind(this);
52 | this.handleContentTouch = this.handleContentTouch.bind(this);
53 | this.handleContentMouseDown = this.handleContentMouseDown.bind(this);
54 | this.handleContentMouseMove = this.handleContentMouseMove.bind(this);
55 | this.handleContentMouseUp = this.handleContentMouseUp.bind(this);
56 | }
57 |
58 | componentWillMount() {
59 | this._iniDates(this.props.value);
60 | }
61 |
62 | componentDidMount() {
63 | const viewport = this.viewport;
64 | viewport.addEventListener('touchstart', this.handleContentTouch, false);
65 | viewport.addEventListener('touchmove', this.handleContentTouch, false);
66 | viewport.addEventListener('touchend', this.handleContentTouch, false);
67 | viewport.addEventListener('mousedown', this.handleContentMouseDown, false);
68 | }
69 |
70 | componentWillReceiveProps(nextProps) {
71 | if (nextProps.value.getTime() === this.props.value.getTime()) {
72 | return;
73 | }
74 | this._iniDates(nextProps.value);
75 | this.currentIndex = MIDDLE_INDEX;
76 | this.setState({
77 | translateY: MIDDLE_Y,
78 | marginTop: (this.currentIndex - MIDDLE_INDEX) * DATE_HEIGHT,
79 | });
80 | }
81 |
82 | /**
83 | * Optimization component, Prevents unnecessary rendering
84 | * Only value or state change should re-rendering
85 | *
86 | * @param {Object} nextProps next props
87 | * @param {Object} nextState next state
88 | * @return {Boolean} Whether re-rendering
89 | */
90 | shouldComponentUpdate(nextProps, nextState) {
91 | return nextProps.value.getTime() !== this.props.value.getTime() ||
92 | !shallowEqual(nextState, this.state);
93 | }
94 |
95 | componentWillUnmount() {
96 | const viewport = this.viewport;
97 | viewport.removeEventListener('touchstart', this.handleContentTouch, false);
98 | viewport.removeEventListener('touchmove', this.handleContentTouch, false);
99 | viewport.removeEventListener('touchend', this.handleContentTouch, false);
100 | viewport.removeEventListener('mousedown', this.handleContentMouseDown, false);
101 |
102 | clearTimeout(this._moveToTimer);
103 | }
104 |
105 | _iniDates(date) {
106 | const typeName = this.props.type;
107 | const dates = Array(...Array(DATE_LENGTH))
108 | .map((value, index) =>
109 | TimeUtil[`next${typeName}`](date, (index - MIDDLE_INDEX) * this.props.step));
110 | this.setState({ dates });
111 | }
112 |
113 | _updateDates(direction) {
114 | const typeName = this.props.type;
115 | const { dates } = this.state;
116 | if (direction === 1) {
117 | this.currentIndex ++;
118 | this.setState({
119 | dates: [
120 | ...dates.slice(1),
121 | TimeUtil[`next${typeName}`](dates[dates.length - 1], this.props.step),
122 | ],
123 | marginTop: (this.currentIndex - MIDDLE_INDEX) * DATE_HEIGHT,
124 | });
125 | } else {
126 | this.currentIndex --;
127 | this.setState({
128 | dates: [
129 | TimeUtil[`next${typeName}`](dates[0], -this.props.step),
130 | ...dates.slice(0, dates.length - 1),
131 | ],
132 | marginTop: (this.currentIndex - MIDDLE_INDEX) * DATE_HEIGHT,
133 | });
134 | }
135 | }
136 |
137 | _checkIsUpdateDates(direction, translateY) {
138 | return direction === 1 ?
139 | this.currentIndex * DATE_HEIGHT + DATE_HEIGHT / 2 < -translateY :
140 | this.currentIndex * DATE_HEIGHT - DATE_HEIGHT / 2 > -translateY;
141 | }
142 |
143 | /**
144 | * 清除对象的transition样式
145 | * @param {Dom} obj 指定的对象
146 | * @return {undefined}
147 | */
148 | _clearTransition(obj) {
149 | addPrefixCss(obj, { transition: '' });
150 | }
151 |
152 | /**
153 | * 滑动到下一日期
154 | * @param {number} direction 滑动方向
155 | * @return {undefined}
156 | */
157 | _moveToNext(direction) {
158 | const date = this.state.dates[MIDDLE_INDEX];
159 | const { max, min } = this.props;
160 | if (direction === -1 && date.getTime() < min.getTime() && this.moveDateCount) {
161 | this._updateDates(1);
162 | } else if (direction === 1 && date.getTime() > max.getTime() && this.moveDateCount) {
163 | this._updateDates(-1);
164 | }
165 |
166 | this._moveTo(this.currentIndex);
167 | }
168 |
169 | /**
170 | * 添加滑动动画
171 | * @param {number} angle 角度
172 | * @return {undefined}
173 | */
174 | _moveTo(currentIndex) {
175 | this.animating = true;
176 |
177 | addPrefixCss(this.refs.scroll, { transition: 'transform .2s ease-out' });
178 |
179 | this.setState({
180 | translateY: -currentIndex * DATE_HEIGHT,
181 | });
182 |
183 | // NOTE: There is no transitionend, setTimeout is used instead.
184 | this._moveToTimer = setTimeout(() => {
185 | this.animating = false;
186 | this.props.onSelect(this.state.dates[MIDDLE_INDEX]);
187 | this._clearTransition(this.refs.scroll);
188 | }, 200);
189 | }
190 |
191 | handleStart(event) {
192 | this.touchY =
193 | (!isUndefined(event.targetTouches) &&
194 | !isUndefined(event.targetTouches[0])) ?
195 | event.targetTouches[0].pageY :
196 | event.pageY;
197 |
198 | this.translateY = this.state.translateY;
199 | this.moveDateCount = 0;
200 | }
201 |
202 |
203 | handleMove(event) {
204 | const touchY =
205 | (!isUndefined(event.targetTouches) &&
206 | !isUndefined(event.targetTouches[0])) ?
207 | event.targetTouches[0].pageY :
208 | event.pageY;
209 |
210 | const dir = touchY - this.touchY;
211 | const translateY = this.translateY + dir;
212 | const direction = dir > 0 ? -1 : 1;
213 |
214 | // 日期最小值,最大值限制
215 | const date = this.state.dates[MIDDLE_INDEX];
216 | const { max, min } = this.props;
217 | if (date.getTime() < min.getTime() ||
218 | date.getTime() > max.getTime()) {
219 | return;
220 | }
221 |
222 | // 检测是否更新日期列表
223 | if (this._checkIsUpdateDates(direction, translateY)) {
224 | this.moveDateCount = direction > 0 ? this.moveDateCount + 1 : this.moveDateCount - 1;
225 | this._updateDates(direction);
226 | }
227 |
228 | this.setState({ translateY });
229 | }
230 |
231 | handleEnd(event) {
232 | const touchY = event.pageY || event.changedTouches[0].pageY;
233 | const dir = touchY - this.touchY;
234 | const direction = dir > 0 ? -1 : 1;
235 | this._moveToNext(direction);
236 | }
237 |
238 | /**
239 | * 滑动日期选择器触屏事件
240 | * @param {Object} event 事件对象
241 | * @return {undefined}
242 | */
243 | handleContentTouch(event) {
244 | event.preventDefault();
245 | if (this.animating) return;
246 | if (event.type === 'touchstart') {
247 | this.handleStart(event);
248 | } else if (event.type === 'touchmove') {
249 | this.handleMove(event);
250 | } else if (event.type === 'touchend') {
251 | this.handleEnd(event);
252 | }
253 | }
254 |
255 | /**
256 | * 滑动日期选择器鼠标事件
257 | * @param {Object} event 事件对象
258 | * @return {undefined}
259 | */
260 | handleContentMouseDown(event) {
261 | if (this.animating) return;
262 | this.handleStart(event);
263 | document.addEventListener('mousemove', this.handleContentMouseMove);
264 | document.addEventListener('mouseup', this.handleContentMouseUp);
265 | }
266 |
267 | handleContentMouseMove(event) {
268 | if (this.animating) return;
269 | this.handleMove(event);
270 | }
271 |
272 | handleContentMouseUp(event) {
273 | if (this.animating) return;
274 | this.handleEnd(event);
275 | document.removeEventListener('mousemove', this.handleContentMouseMove);
276 | document.removeEventListener('mouseup', this.handleContentMouseUp);
277 | }
278 |
279 | /**
280 | * 渲染一个日期DOM对象
281 | * @param {Object} date date数据
282 | * @return {Object} JSX对象
283 | */
284 | renderDatepickerItem(date, index) {
285 | const className =
286 | (date < this.props.min || date > this.props.max) ?
287 | 'disabled' : '';
288 |
289 | let formatDate;
290 | if (isFunction(this.props.format)) {
291 | formatDate = this.props.format(date);
292 | } else {
293 | formatDate = TimeUtil.convertDate(date, this.props.format);
294 | }
295 |
296 | return (
297 |
300 | {formatDate}
301 |
302 | );
303 | }
304 |
305 | render() {
306 | const scrollStyle = formatCss({
307 | transform: `translateY(${this.state.translateY}px)`,
308 | marginTop: this.state.marginTop,
309 | });
310 |
311 | return (
312 |
313 |
this.viewport = viewport} // eslint-disable-line
315 | className="datepicker-viewport">
316 |
317 |
321 | {this.state.dates.map(this.renderDatepickerItem)}
322 |
323 |
324 |
325 |
326 | );
327 | }
328 | }
329 |
330 | export default DatePickerItem;
331 |
--------------------------------------------------------------------------------
/lib/Modal.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import ReactDOM from 'react-dom';
3 | const renderSubtreeIntoContainer = ReactDOM.unstable_renderSubtreeIntoContainer;
4 |
5 | type Props = {
6 | children: React.Element<*>,
7 | isOpen: boolean,
8 | }
9 |
10 | type DefaultProps = {
11 | isOpen: boolean,
12 | }
13 |
14 | class Modal extends Component {
15 |
16 | static defaultProps = {
17 | isOpen: false,
18 | }
19 |
20 | componentDidMount() {
21 | this._div = document.createElement('div');
22 | this._div.classList.add('Modal-Portal');
23 | document.body.appendChild(this._div);
24 | this.renderPortal(this.props);
25 | }
26 |
27 | componentWillReceiveProps(newProps) {
28 | this.renderPortal(newProps);
29 | }
30 |
31 | componentWillUnmount() {
32 | ReactDOM.unmountComponentAtNode(this._div);
33 | this._div.parentNode.removeChild(this._div);
34 | }
35 |
36 | renderPortal(props) {
37 | const portal =
38 | React.cloneElement(this.props.children, { ...props, key: 'portal' }, null);
39 |
40 | this.portal =
41 | renderSubtreeIntoContainer(this, portal, this._div);
42 | }
43 |
44 | render() {
45 | return (