├── .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 |
233 | 236 | select time 237 | 238 | 239 | 244 |
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 |
34 | 37 | default 38 | 39 | 42 | dark 43 | 44 | 47 | ios 48 | 49 | 52 | android 53 | 54 | 57 | android-dark 58 | 59 |
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 |
14 |
15 |
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 &&
205 | {confirmText} 208 | {cancelText} 211 |
} 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 (