├── .babelrc
├── .eslintrc
├── .gitignore
├── .hophoprc
├── .smooth-releaserc
├── .stylelintrc
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── ci
├── pipeline.yml
├── test.sh
└── test.yml
├── examples
├── DatePicker.example
├── DatePickerInput.example
├── build
│ ├── bundle.js
│ ├── bundle.js.gz
│ ├── index.html
│ └── style.2703567a00ec10a4a818.min.css
├── examples.js
├── index.html
├── webpack.base.babel.js
├── webpack.config.babel.js
└── webpack.config.build.babel.js
├── generateReadme.js
├── index.d.ts
├── index.js
├── karma.conf.js
├── karma.js
├── package.json
├── src
├── DatePicker.js
├── DatePickerInput.js
├── Input.js
├── InvalidDate.js
├── Picker.js
├── PickerTop.js
├── README.md
├── Row.js
├── daypicker
│ ├── DayPicker.js
│ ├── DayPickerBody.js
│ └── DayPickerTop.js
├── icons.scss
├── index.js
├── monthpicker
│ ├── MonthPicker.js
│ ├── MonthPickerBody.js
│ └── MonthPickerTop.js
├── style.scss
├── utils
│ ├── DateUtils.js
│ ├── format.js
│ ├── index.js
│ ├── model.js
│ ├── pure.js
│ ├── skinnable.js
│ └── valueLink.js
└── yearpicker
│ ├── YearPicker.js
│ ├── YearPickerBody.js
│ └── YearPickerTop.js
├── styles.js
├── test
├── .eslintrc
├── index.js
└── tests
│ └── DatePickerInput-test.js
└── webpack.config.build.babel.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["buildo", { env: "react" }]
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./node_modules/scriptoni/lib/scripts/eslint/eslintrc.json",
3 | "rules": {
4 | "max-len": 0
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | npm-debug.log
4 | lib
5 | .eslintcache
6 | package-lock.json
7 |
--------------------------------------------------------------------------------
/.hophoprc:
--------------------------------------------------------------------------------
1 | toggl: no
2 | branchPrefix: n
3 | branchSuffix: y
4 |
--------------------------------------------------------------------------------
/.smooth-releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "github": {
3 | "changelog": {
4 | "ignoredLabels": ["DX", "discarded", "discussion", "greenkeeper"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./node_modules/scriptoni/lib/scripts/stylelint/stylelintrc.json"
3 | }
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 |
4 |
5 | ## [v5.0.17](https://github.com/buildo/rc-datepicker/tree/v5.0.17) (2024-07-09)
6 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.16...v5.0.17)
7 |
8 | #### New features:
9 |
10 | - Custom icon [#177](https://github.com/buildo/rc-datepicker/issues/177)
11 |
12 | ## [v5.0.16](https://github.com/buildo/rc-datepicker/tree/v5.0.16) (2020-04-27)
13 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.14...v5.0.16)
14 |
15 | #### New features:
16 |
17 | - Style.scss imports a file that does not exist in recent versions of react-flexview [#167](https://github.com/buildo/rc-datepicker/issues/167)
18 | - Placeholder not showing [#162](https://github.com/buildo/rc-datepicker/issues/162)
19 | - Disable user input? [#149](https://github.com/buildo/rc-datepicker/issues/149)
20 |
21 | #### Fixes (bugs & defects):
22 |
23 | - fix onChange typings for DatePickerInput: it should accept a second argument "formatteDate: string" [#159](https://github.com/buildo/rc-datepicker/issues/159)
24 |
25 | ## [v5.0.14](https://github.com/buildo/rc-datepicker/tree/v5.0.14) (2019-04-23)
26 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.13...v5.0.14)
27 |
28 | #### New features:
29 |
30 | - Allow already-working "placeholder" props in typings [#161](https://github.com/buildo/rc-datepicker/issues/161)
31 | - Logo Design for rc-datepicker [#154](https://github.com/buildo/rc-datepicker/issues/154)
32 |
33 | ## [v5.0.13](https://github.com/buildo/rc-datepicker/tree/v5.0.13) (2018-04-30)
34 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.12...v5.0.13)
35 |
36 | #### Fixes (bugs & defects):
37 |
38 | - @types/react should not be a dependency [#146](https://github.com/buildo/rc-datepicker/issues/146)
39 |
40 | ## [v5.0.12](https://github.com/buildo/rc-datepicker/tree/v5.0.12) (2018-03-23)
41 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.11...v5.0.12)
42 |
43 | #### Fixes (bugs & defects):
44 |
45 | - String refs are deprecated [#144](https://github.com/buildo/rc-datepicker/issues/144)
46 |
47 | ## [v5.0.11](https://github.com/buildo/rc-datepicker/tree/v5.0.11) (2018-02-26)
48 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.10...v5.0.11)
49 |
50 | #### Fixes (bugs & defects):
51 |
52 | - Add react16 to list of peerDependencies [#142](https://github.com/buildo/rc-datepicker/issues/142)
53 |
54 | ## [v5.0.10](https://github.com/buildo/rc-datepicker/tree/v5.0.10) (2017-10-13)
55 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.9...v5.0.10)
56 |
57 | #### Fixes (bugs & defects):
58 |
59 | - Typescript: `void` as state complains [#136](https://github.com/buildo/rc-datepicker/issues/136)
60 |
61 | ## [v5.0.9](https://github.com/buildo/rc-datepicker/tree/v5.0.9) (2017-08-28)
62 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.8...v5.0.9)
63 |
64 | #### Fixes (bugs & defects):
65 |
66 | - replace t.ReactChildren with exported ReactChildren [#131](https://github.com/buildo/rc-datepicker/issues/131)
67 |
68 | ## [v5.0.8](https://github.com/buildo/rc-datepicker/tree/v5.0.8) (2017-06-20)
69 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.7...v5.0.8)
70 |
71 | #### Fixes (bugs & defects):
72 |
73 | - [.d.ts] onChange first param should be a Date [#129](https://github.com/buildo/rc-datepicker/issues/129)
74 | - Ci is failing with Error: Missing description for prop 'startDate' in 'DatePickerInput'. [#126](https://github.com/buildo/rc-datepicker/issues/126)
75 |
76 | ## [v5.0.7](https://github.com/buildo/rc-datepicker/tree/v5.0.7) (2017-05-29)
77 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.6...v5.0.7)
78 |
79 | #### New features:
80 |
81 | - backport scriptoni@0.6 [#124](https://github.com/buildo/rc-datepicker/issues/124)
82 | - Add the ability to specify the initial visible date [#122](https://github.com/buildo/rc-datepicker/issues/122)
83 |
84 | ## [v5.0.6](https://github.com/buildo/rc-datepicker/tree/v5.0.6) (2017-05-05)
85 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.5...v5.0.6)
86 |
87 | #### New features:
88 |
89 | - typescript support [#105](https://github.com/buildo/rc-datepicker/issues/105)
90 |
91 | ## [v5.0.5](https://github.com/buildo/rc-datepicker/tree/v5.0.5) (2017-04-26)
92 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.4...v5.0.5)
93 |
94 | #### Fixes (bugs & defects):
95 |
96 | - DatePickerInput is ignoring locale when initialized [#114](https://github.com/buildo/rc-datepicker/issues/114)
97 | - Can not find lib/style.css. [#111](https://github.com/buildo/rc-datepicker/issues/111)
98 |
99 | ## [v5.0.4](https://github.com/buildo/rc-datepicker/tree/v5.0.4) (2017-03-08)
100 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.3...v5.0.4)
101 |
102 | #### New features:
103 |
104 | - DatePickerInput: add ability to open the popup on top [#112](https://github.com/buildo/rc-datepicker/issues/112)
105 |
106 | ## [v5.0.3](https://github.com/buildo/rc-datepicker/tree/v5.0.3) (2017-03-06)
107 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.2...v5.0.3)
108 |
109 | ## [v5.0.2](https://github.com/buildo/rc-datepicker/tree/v5.0.2) (2017-03-01)
110 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.1...v5.0.2)
111 |
112 | #### New features:
113 |
114 | - add "disabled" functionality to DatePickerInput [#109](https://github.com/buildo/rc-datepicker/issues/109)
115 |
116 | ## [v5.0.1](https://github.com/buildo/rc-datepicker/tree/v5.0.1) (2017-01-02)
117 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.0...v5.0.1)
118 |
119 | #### Fixes (bugs & defects):
120 |
121 | - style.css is not fully compiled in lib folder [#102](https://github.com/buildo/rc-datepicker/issues/102)
122 |
123 | ## [v5.0.0](https://github.com/buildo/rc-datepicker/tree/v5.0.0) (2016-12-29)
124 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.0-beta2...v5.0.0)
125 |
126 | #### New features:
127 |
128 | - [style] update style with new mockup [#98](https://github.com/buildo/rc-datepicker/issues/98)
129 | - [style] improve UI following styleguide [#81](https://github.com/buildo/rc-datepicker/issues/81)
130 | - React 15.x compatibility [#78](https://github.com/buildo/rc-datepicker/issues/78)
131 | - datepicker layout may break with different fonts [#68](https://github.com/buildo/rc-datepicker/issues/68)
132 |
133 | ## [v5.0.0-beta2](https://github.com/buildo/rc-datepicker/tree/v5.0.0-beta2) (2016-06-06)
134 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v5.0.0-beta1...v5.0.0-beta2)
135 |
136 | #### New features:
137 |
138 | - Find an alternative to valueLink [#88](https://github.com/buildo/rc-datepicker/issues/88)
139 |
140 | ## [v5.0.0-beta1](https://github.com/buildo/rc-datepicker/tree/v5.0.0-beta1) (2016-05-30)
141 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v4.0.0...v5.0.0-beta1)
142 |
143 | #### New features:
144 |
145 | - [style] improve picker style [#86](https://github.com/buildo/rc-datepicker/issues/86)
146 | - [style] improve button style [#82](https://github.com/buildo/rc-datepicker/issues/82)
147 |
148 | #### Fixes (bugs & defects):
149 |
150 | - [IE] datepicker returns InvalidDate at first click [#83](https://github.com/buildo/rc-datepicker/issues/83)
151 |
152 | ## [v4.0.0](https://github.com/buildo/rc-datepicker/tree/v4.0.0) (2016-04-21)
153 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v3.1.2...v4.0.0)
154 |
155 | ## [v3.1.2](https://github.com/buildo/rc-datepicker/tree/v3.1.2) (2016-03-30)
156 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v3.1.1...v3.1.2)
157 |
158 | #### Fixes (bugs & defects):
159 |
160 | - January calendar wrong with moment 2.11+ [#65](https://github.com/buildo/rc-datepicker/issues/65)
161 |
162 | ## [v3.1.1](https://github.com/buildo/rc-datepicker/tree/v3.1.1) (2016-03-23)
163 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v3.0.2...v3.1.1)
164 |
165 | #### Fixes (bugs & defects):
166 |
167 | - 3.1.0 break change mode button [#75](https://github.com/buildo/rc-datepicker/issues/75)
168 | - closeOnClickOutside not triggered when clicking into another DatePickerInput [#69](https://github.com/buildo/rc-datepicker/issues/69)
169 |
170 | #### New features:
171 |
172 | - Update lodash to ^4 [#73](https://github.com/buildo/rc-datepicker/issues/73)
173 | - use Popover from buildo-react-components [#61](https://github.com/buildo/rc-datepicker/issues/61)
174 | - improve DatePicker and DatePickerInput API [#39](https://github.com/buildo/rc-datepicker/issues/39)
175 | - Drop Travis and Coveralls in favor of Bamboo and Codecov [#11](https://github.com/buildo/rc-datepicker/issues/11)
176 |
177 | ## [v3.0.2](https://github.com/buildo/rc-datepicker/tree/v3.0.2) (2016-01-27)
178 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v3.0.1...v3.0.2)
179 |
180 | #### New features:
181 |
182 | - temporarily use moment@2.10.6 [#66](https://github.com/buildo/rc-datepicker/issues/66)
183 |
184 | ## [v3.0.1](https://github.com/buildo/rc-datepicker/tree/v3.0.1) (2015-12-22)
185 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v3.0.0...v3.0.1)
186 |
187 | #### New features:
188 |
189 | - add clearable [#62](https://github.com/buildo/rc-datepicker/issues/62)
190 | - fix YearPickerTop propTypes [#60](https://github.com/buildo/rc-datepicker/issues/60)
191 |
192 | ## [v3.0.0](https://github.com/buildo/rc-datepicker/tree/v3.0.0) (2015-10-12)
193 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v2.0.0...v3.0.0)
194 |
195 | #### Breaking:
196 |
197 | - separate format in displayFormat and returnFormat [#57](https://github.com/buildo/rc-datepicker/issues/57)
198 |
199 | #### New features:
200 |
201 | - handle undefined value [#55](https://github.com/buildo/rc-datepicker/issues/55)
202 |
203 | ## [v2.0.0](https://github.com/buildo/rc-datepicker/tree/v2.0.0) (2015-08-10)
204 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v1.3.0...v2.0.0)
205 |
206 | #### Fixes (bugs & defects):
207 |
208 | - DatePickerInput does not close if same date is inserted [#48](https://github.com/buildo/rc-datepicker/issues/48)
209 |
210 | #### New features:
211 |
212 | - remove unused and unwanted initialDate in componentWillRecieveProps of DatePickerInput [#40](https://github.com/buildo/rc-datepicker/issues/40)
213 | - should we introduce t-comb for prop and type checking? [#38](https://github.com/buildo/rc-datepicker/issues/38)
214 | - safer import of lodash [#35](https://github.com/buildo/rc-datepicker/issues/35)
215 | - add support for valueLink [#13](https://github.com/buildo/rc-datepicker/issues/13)
216 |
217 | ## [v1.3.0](https://github.com/buildo/rc-datepicker/tree/v1.3.0) (2015-07-23)
218 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v1.2.0...v1.3.0)
219 |
220 | #### New features:
221 |
222 | - add onShow and onHide callbacks to DatePickerInput [#36](https://github.com/buildo/rc-datepicker/issues/36)
223 |
224 | ## [v1.2.0](https://github.com/buildo/rc-datepicker/tree/v1.2.0) (2015-07-16)
225 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v1.1.0...v1.2.0)
226 |
227 | #### Fixes (bugs & defects):
228 |
229 | - CI is failing due to lint error [#30](https://github.com/buildo/rc-datepicker/issues/30)
230 | - remove window.onclick listener in componentWillUnmount [#29](https://github.com/buildo/rc-datepicker/issues/29)
231 |
232 | #### New features:
233 |
234 | - in the last release (v1.1.0) there is a console.log [#28](https://github.com/buildo/rc-datepicker/issues/28)
235 | - Type in closeOnClickOutiside prop. [#25](https://github.com/buildo/rc-datepicker/issues/25)
236 | - Rename to "rc-datepicker"? [#16](https://github.com/buildo/rc-datepicker/issues/16)
237 |
238 | ## [v1.1.0](https://github.com/buildo/rc-datepicker/tree/v1.1.0) (2015-07-06)
239 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v1.0.3...v1.1.0)
240 |
241 | #### New features:
242 |
243 | - Should log a warning if required locale is not available [#22](https://github.com/buildo/rc-datepicker/issues/22)
244 | - Add a screenshot in the README [#17](https://github.com/buildo/rc-datepicker/issues/17)
245 | - change repo name (remove "semantic") [#14](https://github.com/buildo/rc-datepicker/issues/14)
246 | - Add example project for quicker development [#7](https://github.com/buildo/rc-datepicker/issues/7)
247 |
248 | #### Fixes (bugs & defects):
249 |
250 | - the calendar doesn't close when you click outside [#20](https://github.com/buildo/rc-datepicker/issues/20)
251 |
252 | ## [v1.0.3](https://github.com/buildo/rc-datepicker/tree/v1.0.3) (2015-06-29)
253 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v1.0.2...v1.0.3)
254 |
255 | ## [v1.0.2](https://github.com/buildo/rc-datepicker/tree/v1.0.2) (2015-06-29)
256 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v1.0.0...v1.0.2)
257 |
258 | ## [v1.0.0](https://github.com/buildo/rc-datepicker/tree/v1.0.0) (2015-06-26)
259 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v0.2.0...v1.0.0)
260 |
261 | #### New features:
262 |
263 | - New build system based on karma/webpack/babel [#8](https://github.com/buildo/rc-datepicker/issues/8)
264 | - Improve build system to produce minified version [#3](https://github.com/buildo/rc-datepicker/issues/3)
265 |
266 | ## [v0.2.0](https://github.com/buildo/rc-datepicker/tree/v0.2.0) (2015-04-22)
267 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v0.1.4...v0.2.0)
268 |
269 | #### New features:
270 |
271 | - Add 'name' prop to DatePickerInput [#5](https://github.com/buildo/rc-datepicker/issues/5)
272 |
273 | ## [v0.1.4](https://github.com/buildo/rc-datepicker/tree/v0.1.4) (2015-04-22)
274 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v0.1.3...v0.1.4)
275 |
276 | #### New features:
277 |
278 | - Present date picker on input field focus [#4](https://github.com/buildo/rc-datepicker/issues/4)
279 |
280 | ## [v0.1.3](https://github.com/buildo/rc-datepicker/tree/v0.1.3) (2015-04-20)
281 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v0.1.2...v0.1.3)
282 |
283 | #### Fixes (bugs & defects):
284 |
285 | - When setting minDate the wrong bounds are set for 'year' mode [#1](https://github.com/buildo/rc-datepicker/issues/1)
286 |
287 | ## [v0.1.2](https://github.com/buildo/rc-datepicker/tree/v0.1.2) (2015-04-11)
288 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v0.1.1...v0.1.2)
289 |
290 | ## [v0.1.1](https://github.com/buildo/rc-datepicker/tree/v0.1.1) (2015-04-10)
291 | [Full Changelog](https://github.com/buildo/rc-datepicker/compare/v0.1.0...v0.1.1)
292 |
293 | #### Fixes (bugs & defects):
294 |
295 | - dateString shouldn't be set if date is undefined [#2](https://github.com/buildo/rc-datepicker/issues/2)
296 |
297 | ## [v0.1.0](https://github.com/buildo/rc-datepicker/tree/v0.1.0) (2015-04-10)
298 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 buildo s.r.l.s.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://nodei.co/npm/rc-datepicker/)
2 |
3 | # React Datepicker
4 | A decent and pretty date picker to be used with React
5 |
6 |
7 | 
8 |
9 |
10 | ```jsx
11 | import 'moment/locale/it.js';
12 | import { DatePicker, DatePickerInput } from 'rc-datepicker';
13 |
14 | const date = '2015-06-26' // or Date or Moment.js
15 |
16 | onChange = (jsDate, dateString) => {
17 | // ...
18 | }
19 |
20 | React.renderComponent(
21 |
22 | // this renders the full component (input and datepicker)
23 |
29 |
30 | // this renders only a fixed datepicker
31 |
32 |
,
33 | document.body
34 | );
35 | ```
36 |
37 | You can see a live demo [here](https://rawgit.com/buildo/react-semantic-datepicker/master/examples/build/index.html)
38 |
39 | or check the full examples [here](https://github.com/buildo/react-semantic-datepicker/tree/master/examples)
40 |
41 | **FOR WEBPACK USERS:** webpack by default imports every locale. Please take a look at [this question](http://stackoverflow.com/questions/25384360/how-to-prevent-moment-js-from-loading-locales-with-webpack) on Stack Overflow for possible solutions.
42 |
43 | ## Install
44 | ```
45 | npm install --save rc-datepicker
46 | ```
47 | The npm package is compiled in JavaScript 5
48 |
49 | ## Changelog
50 | See [CHANGELOG.md](https://github.com/buildo/rc-datepicker/blob/master/CHANGELOG.md)
51 |
52 | ## DatePickerInput API
53 | See [this readme](https://github.com/buildo/rc-datepicker/blob/master/src/README.md)
54 |
55 | ## Locales
56 | `DatePicker` and `DatePickerInput` use **Moment.js**, therefore they support any locale inside "moment/locale".
57 |
58 | To select a locale you need to require it **before** requiring the datepicker or moment anywhere in your app: this way it will be automatically selected as current locale.
59 |
60 | ```js
61 | import 'moment/locale/fr.js' // or 'rc-datepicker/node_modules/moment/locale/fr.js' if you don't have it in your node_modules folder
62 |
63 | import { DatePickerInput } from 'rc-datepicker';
64 | ```
65 |
66 | `DatePickerInput` will now use French locale by default.
67 |
68 | ### Switch between locales
69 | You can switch between locales by passing the prop "locale" to `DatePickerInput` or `DatePicker`.
70 |
71 | **WATCH OUT** this method requires the wanted locale to be already available in your bundle which is only true if you had already imported it or if you're using `moment-with-locales.min.js`
72 |
73 | ```jsx
74 | import 'moment/locale/fr.js'
75 | import 'moment/locale/es.js'
76 |
77 |
78 | ```
79 |
80 | ## Style
81 | `DatePickerInput` and `DatePicker` come with their own default style. In order to use it you should import it in your project as follows:
82 |
83 | ```js
84 | import 'rc-datepicker/lib/style.css';
85 | ```
86 |
87 | ## Examples
88 |
89 | ```shell
90 | $ npm install
91 | $ npm start
92 | $ open http://localhost:8080
93 | ```
94 |
--------------------------------------------------------------------------------
/ci/pipeline.yml:
--------------------------------------------------------------------------------
1 | resource_types:
2 | - name: pull-request
3 | type: docker-image
4 | source:
5 | repository: teliaoss/github-pr-resource
6 |
7 | resources:
8 | - name: master
9 | type: git
10 | icon: github-circle
11 | source:
12 | uri: git@github.com:buildo/rc-datepicker
13 | branch: master
14 | private_key: ((private-key))
15 |
16 | - name: pr
17 | type: pull-request
18 | source:
19 | repository: buildo/rc-datepicker
20 | access_token: ((github-token))
21 |
22 | jobs:
23 | - name: pr-test
24 | plan:
25 | - get: rc-datepicker
26 | resource: pr
27 | trigger: true
28 | version: every
29 | - put: pr
30 | params:
31 | path: rc-datepicker
32 | status: pending
33 | context: concourse
34 | - do:
35 | - task: test
36 | file: rc-datepicker/ci/test.yml
37 | attempts: 2
38 | on_success:
39 | put: pr
40 | params:
41 | path: rc-datepicker
42 | status: success
43 | context: concourse
44 | on_failure:
45 | put: pr
46 | params:
47 | path: rc-datepicker
48 | status: failure
49 | context: concourse
50 |
51 | - name: test
52 | plan:
53 | - get: rc-datepicker
54 | resource: master
55 | trigger: true
56 | - do:
57 | - task: test
58 | file: rc-datepicker/ci/test.yml
59 | attempts: 2
60 |
--------------------------------------------------------------------------------
/ci/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | npm i --no-package-lock
6 | npm run lint
7 | npm run lint-style
8 | npm run build
9 |
--------------------------------------------------------------------------------
/ci/test.yml:
--------------------------------------------------------------------------------
1 | platform: linux
2 |
3 | image_resource:
4 | type: docker-image
5 | source:
6 | repository: node
7 | tag: 10
8 |
9 | inputs:
10 | - name: rc-datepicker
11 |
12 | caches:
13 | - path: rc-datepicker/node_modules
14 |
15 | run:
16 | path: ci/test.sh
17 | dir: rc-datepicker
18 |
--------------------------------------------------------------------------------
/examples/DatePicker.example:
--------------------------------------------------------------------------------
1 | class Example extends React.Component {
2 |
3 | constructor(props) {
4 | super(props);
5 | const yesterday = new Date();
6 | yesterday.setDate(yesterday.getDate() - 1);
7 | this.state = {
8 | yesterday,
9 | value: '2015-05-13'
10 | };
11 | }
12 |
13 | resetState() {
14 | this.setState({ value: null });
15 | }
16 |
17 | render() {
18 | return (
19 |
20 |
jsDate = {String(this.state.value)}
21 |
this.setState({value: jsDate})}
25 | locale='fr'
26 | />
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/DatePickerInput.example:
--------------------------------------------------------------------------------
1 | class Example extends React.Component {
2 |
3 | constructor(props) {
4 | super(props);
5 | const yesterday = new Date();
6 | yesterday.setDate(yesterday.getDate() - 1);
7 | this.state = {
8 | yesterday,
9 | value: null
10 | };
11 | }
12 |
13 | resetState = () => this.setState({ value: null })
14 |
15 | render() {
16 | return (
17 |
18 |
19 |
jsDate = {String(this.state.value)}
20 |
21 |
32 |
33 |
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/build/bundle.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/buildo/rc-datepicker/06c70ef8b96457e470f1b544e440cc3c32ecce1d/examples/build/bundle.js.gz
--------------------------------------------------------------------------------
/examples/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RC Datepicker
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/build/style.2703567a00ec10a4a818.min.css:
--------------------------------------------------------------------------------
1 | .react-flex-view{box-sizing:"border-box";min-width:0;min-height:0;display:flexbox;display:-ms-flexbox;display:flex;-webkit-box-flex-direction:row;-moz-box-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-box-flex-wrap:nowrap;-moz-box-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-ms-flex-align:stretch;flex-align:stretch;-webkit-align-items:stretch;-moz-box-align-items:stretch;-ms-align-items:stretch;align-items:stretch}.react-flex-view.flex-column{-webkit-box-flex-direction:column;-moz-box-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.react-flex-view.flex-wrap{-webkit-box-flex-wrap:wrap;-moz-box-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.react-flex-view.align-content-start{-ms-flex-align:start;flex-align:start;-webkit-align-items:flex-start;-moz-box-align-items:flex-start;-ms-align-items:flex-start;align-items:flex-start}.react-flex-view.align-content-center{-ms-flex-align:center;flex-align:center;-webkit-align-items:center;-moz-box-align-items:center;-ms-align-items:center;align-items:center}.react-flex-view.align-content-end{-ms-flex-align:end;flex-align:end;-webkit-align-items:flex-end;-moz-box-align-items:flex-end;-ms-align-items:flex-end;align-items:flex-end}.react-flex-view.justify-content-start{-ms-flex-pack:start;flex-pack:start;-webkit-justify-content:flex-start;-moz-box-justify-content:flex-start;-ms-justify-content:flex-start;justify-content:flex-start}.react-flex-view.justify-content-center{-ms-flex-pack:center;flex-pack:center;-webkit-justify-content:center;-moz-box-justify-content:center;-ms-justify-content:center;justify-content:center}.react-flex-view.justify-content-end{-ms-flex-pack:end;flex-pack:end;-webkit-justify-content:flex-end;-moz-box-justify-content:flex-end;-ms-justify-content:flex-end;justify-content:flex-end}@font-face{font-family:rc-datepicker;src:url("data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SDIcAAAC8AAAAYGNtYXAAitFNAAABHAAAAGRnYXNwAAAAEAAAAYAAAAAIZ2x5ZmYIkl0AAAGIAAAC7GhlYWQLeirXAAAEdAAAADZoaGVhB3kDyQAABKwAAAAkaG10eBKTAOAAAATQAAAAIGxvY2ECQgFeAAAE8AAAABJtYXhwABgAfgAABQQAAAAgbmFtZUzHCYMAAAUkAAABznBvc3QAAwAAAAAG9AAAACAAAwLqAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADwcwPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQASAAAAA4ACAACAAYAAQAg8A3wVPBz//3//wAAAAAAIPAN8FPwc//9//8AAf/jD/cPsg+UAAMAAQAAAAAAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQA/AD8C5gLmADwAACUUBg8BDgEjIiYvAQcOASMiJi8BLgE1NDY/AScuATU0Nj8BPgEzMhYfATc+ATMyFh8BHgEVFAYPARceARUC5gkHTggUCwsUCKioBxULChUHTggICAioqAgICAhOBxUKCxUHqKgIFAsLFAhOBwkJB6ioBwnDChUHTggICAioqAgICAhOBxUKCxUHqKgIFAsLFAhOBwkJB6ioBwkJB04IFAsLFAioqAcVCwAAAAEAYwAaAp0DnQAVAAAJAhYUDwEGIicBJjQ3ATYyHwEWFAcCnf7RAS8LC18KHgv+WAsLAagLHgpfCwsDC/7Q/tELHgpfCwsBqAoeCwGoCwtfCh4LAAEAPgAaAnkDnQAVAAAJAQYiLwEmNDcJASY0PwE2MhcBFhQHAnn+WAseC18KCgEw/tAKCl8LHgsBqAoKAcL+WAsLXwoeCwEvATALHgpfCwv+WAseCgAAAAAPAAD/twO3A7cAAwAIAAwAEQAVABoAHwAjACgAOAA8AEEARQBWAHsAADczNSMXMzUjFSczNSMXMzUjFSczNSMBMzUjFQMzNSMVATM1IyczNSMVAzU0JisBIgYdARQWOwEyNgEzNSMnMzUjFTsBNSM3NTQmKwEiBh0BFBY7ATI2NTcRFAYjISImNRE0NjsBNTQ2OwEyFh0BMzU0NjsBMhYdATMyFhVJpaXJt7fJpaXJt7fJpaUBpbe33Le3Abelpdu3t8kLCCQICwsIJAgLAaSlpdu3t9ulpRILByQICwsIJAcL3Cse/NseKyseSTYmJCY22zYmJCY2SR4rAKWlpaXJt7e3t9yk/belpQGlpKT+W6Ukt7cB7qQICwsIpAcLC/4ZtyWkpKRupAgLCwikBwsLByT9JR4rKx4C2x4sNiY2NiY2NiY2NiY2LB4AAAEAAAABAABplrQ9Xw889QALBAAAAAAA1FHzNgAAAADUUfM2AAD/twO3A7cAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAA7cAAQAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAAAAAAACAAAAAyUAPwMAAGMCtwA+A7cAAAAAAAAACgAUAB4AegCkANABdgAAAAEAAAAIAHwADwAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQANAAAAAQAAAAAAAgAHAJYAAQAAAAAAAwANAEgAAQAAAAAABAANAKsAAQAAAAAABQALACcAAQAAAAAABgANAG8AAQAAAAAACgAaANIAAwABBAkAAQAaAA0AAwABBAkAAgAOAJ0AAwABBAkAAwAaAFUAAwABBAkABAAaALgAAwABBAkABQAWADIAAwABBAkABgAaAHwAAwABBAkACgA0AOxyYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJWZXJzaW9uIDEuMABWAGUAcgBzAGkAbwBuACAAMQAuADByYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJyYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJSZWd1bGFyAFIAZQBnAHUAbABhAHJyYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJGb250IGdlbmVyYXRlZCBieSBJY29Nb29uLgBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAC4AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") format("truetype");font-weight:400;font-style:normal}.icon-rc-datepicker{font-family:rc-datepicker!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-rc-datepicker_clear:before{content:"\F00D"}.icon-rc-datepicker_prev:before{content:"\F053"}.icon-rc-datepicker_next:before{content:"\F054"}.icon-rc-datepicker_calendar:before{content:"\F073"}.react-datepicker-component{position:relative;font-size:14px;font-weight:600}.react-datepicker-component.is-disabled{pointer-events:none;opacity:.5}.react-datepicker-component .react-datepicker{margin-left:5px;margin-top:5px;display:inherit}.react-datepicker-component .react-datepicker-input{position:relative;min-width:150px;height:36px;background:linear-gradient(#fff,#f2f4f7);border:1px solid #ced0da;border-radius:4px}.react-datepicker-component .react-datepicker-input.is-small{height:32px}.react-datepicker-component .react-datepicker-input input{width:100%;height:100%;background:transparent;border:none;box-sizing:border-box;padding-left:15px;padding-right:60px;font-size:14px;color:#354052;font-weight:600}.react-datepicker-component .react-datepicker-input input:focus{outline:none}.react-datepicker-component .react-datepicker-input input::-webkit-input-placeholder{color:#9098a7;font-weight:600}.react-datepicker-component .react-datepicker-input input:-moz-placeholder,.react-datepicker-component .react-datepicker-input input::-moz-placeholder{color:#9098a7;font-weight:600}.react-datepicker-component .react-datepicker-input input:-ms-input-placeholder{color:#9098a7;font-weight:600}.react-datepicker-component .react-datepicker-input .button-wrapper{position:absolute;right:0;top:0;height:100%}.react-datepicker-component .react-datepicker-input .button-wrapper .input-button{margin:0 10px;background:transparent;font-size:15px;border-radius:0 4px 4px 0;cursor:pointer;color:#b5c0ce}.react-datepicker-component .react-datepicker-input .button-wrapper .input-button:hover{background:transparent;color:#9098a7}.react-datepicker-component .react-datepicker-input .button-wrapper .clear-button{cursor:pointer;font-size:13px;color:#b5c0ce}.react-datepicker-component .react-datepicker-input .button-wrapper .clear-button:hover{color:#db242c}.react-datepicker-component .react-datepicker-input:hover{background:linear-gradient(#fff,#dfe3e8);border:1px solid #ced0da}.react-datepicker-component .react-datepicker-input:hover input{color:#354052}.react-datepicker-component .react-datepicker-input:hover input::-webkit-input-placeholder{color:#9098a7}.react-datepicker-component .react-datepicker-input:hover input:-moz-placeholder,.react-datepicker-component .react-datepicker-input:hover input::-moz-placeholder{color:#9098a7}.react-datepicker-component .react-datepicker-input:hover input:-ms-input-placeholder{color:#9098a7}.react-datepicker-component .react-datepicker-input:hover .button-wrapper .input-button{color:#9098a7}.react-datepicker-component .react-datepicker-input.is-open{background:linear-gradient(#fff,#dfe3e8);border:1px solid #2da1f8}.react-datepicker-component .react-datepicker-input.is-open input{color:#354052}.react-datepicker-component .react-datepicker-input.is-open input::-webkit-input-placeholder{color:#354052}.react-datepicker-component .react-datepicker-input.is-open input:-moz-placeholder,.react-datepicker-component .react-datepicker-input.is-open input::-moz-placeholder{color:#354052}.react-datepicker-component .react-datepicker-input.is-open input:-ms-input-placeholder{color:#354052}.react-datepicker-component .react-datepicker-input.is-open .button-wrapper .input-button{color:#b5c0ce}.react-datepicker-component .react-datepicker-input.has-value input{color:#354052}.react-datepicker{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:inline-block;font-size:14px;font-weight:600}.react-datepicker.floating{position:absolute;z-index:10000;box-shadow:1px 1px 5px 1px rgba(0,0,0,.1)}.react-datepicker.position-top{top:auto;bottom:100%;margin-bottom:5px}.react-datepicker.position-top .react-datepicker-container:after,.react-datepicker.position-top .react-datepicker-container:before{top:100%;left:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.react-datepicker.position-top .react-datepicker-container:after{border-top-color:#d9dee3;border-width:5px;margin-left:-5px}.react-datepicker.position-top .react-datepicker-container:before{border-top-color:#d9dee3;border-width:6px;margin-left:-6px}.react-datepicker:not(.position-top) .react-datepicker-container .react-datepicker-top:after,.react-datepicker:not(.position-top) .react-datepicker-container .react-datepicker-top:before{bottom:100%;left:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.react-datepicker:not(.position-top) .react-datepicker-container .react-datepicker-top:after{border-bottom-color:#2da1f8;border-width:5px;margin-left:-5px}.react-datepicker:not(.position-top) .react-datepicker-container .react-datepicker-top:before{border-bottom-color:#d9dee3;border-width:6px;margin-left:-6px}.react-datepicker .react-datepicker-container{width:250px;position:relative}.react-datepicker .react-datepicker-container .react-datepicker-top{text-align:center;background:linear-gradient(#2da1f8,#1789dd);color:#fff;border-top:1px solid #2da1f8;border-left:1px solid #2da1f8;border-right:1px solid #2da1f8;border-top-left-radius:2px;border-top-right-radius:2px}.react-datepicker .react-datepicker-container .react-datepicker-top .week-days{height:35px}.react-datepicker .react-datepicker-container .react-datepicker-top .week-days .week-day{cursor:default;font-weight:400;font-size:13px}.react-datepicker .react-datepicker-container .react-datepicker-top .display{height:35px}.react-datepicker .react-datepicker-container .react-datepicker-top .display .react-datepicker-button{text-decoration:none;padding:4px;text-align:center;font-size:15px;letter-spacing:.5px;cursor:pointer}.react-datepicker .react-datepicker-container .react-datepicker-top .display .react-datepicker-button.button-left{font-size:13px;padding:4px 16px;border-top-left-radius:2px}.react-datepicker .react-datepicker-container .react-datepicker-top .display .react-datepicker-button.button-right{font-size:13px;padding:4px 16px;border-top-right-radius:2px}.react-datepicker .react-datepicker-container .react-datepicker-top .display .react-datepicker-button:hover{background:rgba(0,0,0,.075);border-radius:4px}.react-datepicker .react-datepicker-container .react-datepicker-top .display .react-datepicker-button.fixed:hover{background:transparent;cursor:default}.react-datepicker .react-datepicker-container .react-datepicker-body{border-left:1px solid #d9dee3;border-right:1px solid #d9dee3;border-bottom:1px solid #d9dee3;border-bottom-right-radius:2px;border-bottom-left-radius:2px}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row{margin-top:0;width:100%;min-height:30px}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row:not(:last-child){border-bottom:1px solid #d9dee3}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row:last-child .react-datepicker-picker:first-child{border-bottom-left-radius:2px}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row:last-child .react-datepicker-picker:last-child{border-bottom-right-radius:2px}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker{color:#9098a7;background:#fff;cursor:pointer;text-decoration:none;font-weight:400}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker:not(:last-child){border-right:1px solid #d9dee3}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.day{min-height:30px}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.month,.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.year{min-height:65px}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker:hover{color:#354052}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.selected{color:#354052;background:#bad7f2;font-weight:700;margin-left:-1px;margin-top:-1px;margin-bottom:-1px;border:1px solid #bad7f2}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.current{font-weight:600;color:#354052;background:#f0f3f8}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.current:hover{color:#354052;background:#dfe5f0}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.selected.current{color:#354052;background:#bad7f2}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.disabled{cursor:default;color:#9098a7;background:#fff}.react-datepicker .react-datepicker-container .react-datepicker-body .react-datepicker-row .react-datepicker-picker.disabled:hover{color:#9098a7;background:#fff}
--------------------------------------------------------------------------------
/examples/examples.js:
--------------------------------------------------------------------------------
1 | import 'moment/locale/fr.js';
2 |
3 | import React from 'react';
4 | import ReactDOM from 'react-dom';
5 | import { DatePicker, DatePickerInput } from '../src';
6 |
7 | import '../src/style.scss';
8 |
9 | class Example extends React.Component {
10 |
11 | state = {
12 | datePickerDate: '2015-05-13',
13 | datePickerInputDate: null,
14 | datePickerInputDate2: null,
15 | showInput: true,
16 | disabled: false
17 | }
18 |
19 | toggleInput = () => this.setState({ showInput: !this.state.showInput })
20 |
21 | onClear = () => this.setState({ datePickerDate: null })
22 |
23 | log = (...x) => console.log(...x) // eslint-disable-line no-console
24 |
25 | resetState = () => this.setState({ datePickerInputDate2: undefined })
26 |
27 | render() {
28 | const yesterday = new Date();
29 | yesterday.setDate(yesterday.getDate() - 1);
30 | return (
31 |
32 |
DatePickerInput
33 |
34 |
onChange(jsDate, dateString)
35 |
dateString = "{this.state.datePickerInputDate}"
36 |
37 | {this.state.showInput &&
38 |
39 | this.setState({ datePickerInputDate: dateString })}
45 | onShow={this.log.bind(this, 'show')}
46 | onHide={this.log.bind(this, 'hide')}
47 | showOnInputClick
48 | placeholder='placeholder'
49 | locale='de'
50 | onClear={this.onClear}
51 | />
52 |
53 | }
54 |
55 |
DatePicker (fixed calendar component)
56 |
onChange(jsDate)
57 |
jsDate = {String(this.state.datePickerDate)}
58 |
this.setState({ datePickerDate: jsDate })}
63 | />
64 |
65 | VALUE LINK
66 |
67 | jsDate = {String(this.state.datePickerInputDate2)}
68 |
69 | this.setState({ datePickerInputDate2 })
77 | }}
78 | showOnInputClick
79 | placeholder='placeholder'
80 | locale='de'
81 | />
82 |
83 |
84 | );
85 | }
86 | }
87 |
88 | ReactDOM.render(, document.getElementById('container'));
89 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RC Datepicker
5 |
6 |
7 | <% for (var chunk in htmlWebpackPlugin.files.chunks.main.css) { %>
8 |
13 | <% } %>
14 |
15 |
16 |
17 | <% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
18 |
22 | <% } %>
23 |
24 |
25 |
--------------------------------------------------------------------------------
/examples/webpack.base.babel.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import StyleLintPlugin from 'stylelint-webpack-plugin';
3 |
4 | export const paths = {
5 | SRC: path.resolve(__dirname, '../src'),
6 | ENTRY: path.resolve(__dirname, 'examples.js'),
7 | INDEX_HTML: path.resolve(__dirname, 'index.html'),
8 | BUILD: path.resolve(__dirname, 'build'),
9 | EXAMPLES: path.resolve(__dirname),
10 | NODE_MODULES: path.resolve(__dirname, 'node_modules')
11 | };
12 |
13 | export default {
14 | output: {
15 | path: paths.BUILD,
16 | filename: 'bundle.js'
17 | },
18 |
19 | resolve: {
20 | root: [paths.NODE_MODULES]
21 | },
22 |
23 | plugins: [
24 | new StyleLintPlugin({
25 | files: '**/*.scss',
26 | failOnError: false,
27 | syntax: 'scss'
28 | })
29 | ],
30 |
31 | module: {
32 | loaders: [
33 | {
34 | test: /\.jsx?$/,
35 | loaders: ['babel'],
36 | include: [paths.SRC, paths.EXAMPLES]
37 | }
38 | ],
39 | preLoaders: [
40 | {
41 | test: /\.jsx?$/,
42 | loader: 'eslint',
43 | include: [paths.SRC, paths.EXAMPLES],
44 | exclude: /node_modules/
45 | }
46 | ]
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/examples/webpack.config.babel.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 | import HtmlWebpackPlugin from 'html-webpack-plugin';
3 | import ExtractTextPlugin from 'extract-text-webpack-plugin';
4 | import webpackBase, { paths } from './webpack.base.babel';
5 |
6 | export default {
7 | ...webpackBase,
8 |
9 | entry: [
10 | 'webpack/hot/dev-server',
11 | paths.ENTRY
12 | ],
13 |
14 | devtool: 'source-map',
15 |
16 | devServer: {
17 | contentBase: paths.BUILD,
18 | host: '0.0.0.0',
19 | port: '8080'
20 | },
21 |
22 | module: {
23 | ...webpackBase.module,
24 | loaders: webpackBase.module.loaders.concat([{
25 | test: /\.scss$/,
26 | loader: ExtractTextPlugin.extract('style', 'css!resolve-url!sass?sourceMap')
27 | }])
28 | },
29 |
30 | plugins: [
31 | ...webpackBase.plugins,
32 | new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('development') }),
33 | new HtmlWebpackPlugin({ inject: false, template: paths.INDEX_HTML }),
34 | new ExtractTextPlugin('style', 'style.[hash].min.css')
35 | ]
36 | };
37 |
--------------------------------------------------------------------------------
/examples/webpack.config.build.babel.js:
--------------------------------------------------------------------------------
1 | import webpack from 'webpack';
2 | import CompressionPlugin from 'compression-webpack-plugin';
3 | import HtmlWebpackPlugin from 'html-webpack-plugin';
4 | import ExtractTextPlugin from 'extract-text-webpack-plugin';
5 | import webpackBase, { paths } from './webpack.base.babel';
6 |
7 | export default {
8 | ...webpackBase,
9 |
10 | entry: paths.ENTRY,
11 |
12 | module: {
13 | ...webpackBase.module,
14 | loaders: webpackBase.module.loaders.concat([{
15 | test: /\.scss$/,
16 | loader: ExtractTextPlugin.extract('style', 'css!resolve-url!sass?sourceMap')
17 | }])
18 | },
19 |
20 | plugins: [
21 | ...webpackBase.plugins,
22 | new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') }),
23 | new webpack.optimize.UglifyJsPlugin({
24 | compress: {
25 | warnings: false,
26 | screw_ie8: true
27 | }
28 | }),
29 | new CompressionPlugin({ regExp: /\.js$|\.css$/ }),
30 | new webpack.NoErrorsPlugin(),
31 | new HtmlWebpackPlugin({ inject: false, template: paths.INDEX_HTML }),
32 | new ExtractTextPlugin('style', 'style.[hash].min.css')
33 | ]
34 | };
35 |
--------------------------------------------------------------------------------
/generateReadme.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { generateReadme } from 'react-readme-generator';
3 |
4 | generateReadme(path.resolve('src/DatePickerInput.js'));
5 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 | import * as moment from 'moment';
3 |
4 | export type Mode = {
5 | 'day': string,
6 | 'month': string,
7 | 'year': string
8 | };
9 |
10 | export type Position = 'top' | 'bottom';
11 |
12 | export type Value = string | Date | moment.Moment ; // | MomentDate
13 |
14 | export type ValueLink = {
15 | value? : Value,
16 | requestChange(e: Date): void
17 | }
18 |
19 | export type DateOnChangeHandler = {
20 | (jsDate: Date, dateString: string): void;
21 | }
22 |
23 | export interface DatePickerInputProps {
24 | value?: Value,
25 | valueLink?: ValueLink,
26 | onChange?: DateOnChangeHandler,
27 | onShow?: () => void,
28 | onHide?: () => void,
29 | onClear?: () => void,
30 | small?: boolean,
31 | defaultValue?: Value,
32 | minDate?: Value,
33 | maxDate?: Value,
34 | locale?: string,
35 | startMode?: Mode,
36 | fixedMode?: boolean,
37 | displayFormat?: string,
38 | returnFormat?: string,
39 | format?: string,
40 | validationFormat?: string,
41 | showOnInputClick?: boolean,
42 | closeOnClickOutside?: boolean,
43 | showInputButton?: boolean,
44 | autoClose?: boolean,
45 | floating?: boolean,
46 | disabled?: boolean,
47 | position?: Position,
48 | iconClassName?: string,
49 | iconClearClassName?: string,
50 | className?: string, // used to omit from inputProps
51 | style?: object, // used to omit from inputProps
52 | placeholder?: string
53 | }
54 |
55 | export class DatePickerInput extends React.Component {}
56 |
57 | export interface DatePickerProps {
58 | onChange?: DateOnChangeHandler;
59 | value?: Value,
60 | valueLink?: ValueLink,
61 | defaultValue?: Value,
62 | minDate?: Value,
63 | maxDate?: Value,
64 | locale?: string,
65 | startMode?: Mode,
66 | fixedMode?: boolean,
67 | returnFormat?: string,
68 | floating?: boolean,
69 | closeOnClickOutside?: boolean, // used only with DatePickerInput
70 | className?: string,
71 | prevIconClassName?: string,
72 | nextIconClassName?: string,
73 | position: Position,
74 | style?: object,
75 | placeholder?: string
76 | }
77 |
78 | export class DatePicker extends React.Component {}
79 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib');
2 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const webpack = require('webpack');
5 |
6 | const paths = {
7 | SRC: path.resolve(__dirname, 'src'),
8 | TEST: path.resolve(__dirname, 'test')
9 | };
10 |
11 | module.exports = (config) => {
12 | config.set({
13 |
14 | browserNoActivityTimeout: 30000,
15 |
16 | browsers: [ 'Chrome' ],
17 |
18 | singleRun: true,
19 |
20 | frameworks: [ 'mocha' ],
21 |
22 | files: [
23 | 'karma.js'
24 | ],
25 |
26 | preprocessors: {
27 | 'karma.js': [ 'webpack' ]
28 | },
29 |
30 | reporters: process.env.CONTINUOUS_INTEGRATION ? [ 'bamboo', 'coverage' ] : [ 'dots', 'coverage' ],
31 |
32 | bambooReporter: {
33 | filename: 'mocha.json'
34 | },
35 |
36 | coverageReporter: {
37 | reporters: [
38 | process.env.CONTINUOUS_INTEGRATION ?
39 | { type: 'lcov', subdir: 'lcov-report' } :
40 | { type: 'html', subdir: 'html-report' }
41 | ]
42 | },
43 |
44 | webpack: {
45 | module: {
46 | loaders: [
47 | {
48 | test: /\.jsx?$/,
49 | loader: 'babel',
50 | include: [paths.SRC, paths.TEST],
51 | exclude: [/node_modules/]
52 | }
53 | ],
54 | preLoaders: [
55 | {
56 | test: /\.jsx?$/,
57 | loader: 'isparta',
58 | include: paths.SRC,
59 | exclude: /node_modules/
60 | }, {
61 | test: /\.jsx?$/,
62 | loader: 'eslint',
63 | include: paths.SRC,
64 | exclude: /node_modules/
65 | }
66 | ]
67 | },
68 | plugins: [
69 | new webpack.DefinePlugin({
70 | 'process.env.NODE_ENV': JSON.stringify('test')
71 | })
72 | ]
73 | },
74 |
75 | webpackMiddleware: {
76 | noInfo: true //please don't spam the console when running in karma!
77 | }
78 |
79 | });
80 | };
81 |
--------------------------------------------------------------------------------
/karma.js:
--------------------------------------------------------------------------------
1 | // called by karma
2 | const testsContext = require.context('./test', true, /tests/);
3 | testsContext.keys().forEach(testsContext);
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rc-datepicker",
3 | "version": "5.0.17",
4 | "description": "DatePicker and DatePickerInput to be used with React",
5 | "main": "index.js",
6 | "files": [
7 | "src",
8 | "lib",
9 | "examples",
10 | "index.js",
11 | "styles.js",
12 | "index.d.ts"
13 | ],
14 | "types": "./index.d.ts",
15 | "scripts": {
16 | "test": "./node_modules/karma/bin/karma start",
17 | "build": "rm -rf lib && mkdir lib && babel src -d lib && webpack --config webpack.config.build.babel.js && rm lib/bundle.js",
18 | "lint": "scriptoni lint src examples/examples.js test",
19 | "lint-fix": "scriptoni lint src examples/examples.js test --fix",
20 | "lint-style": "scriptoni lint-style ./**/*.scss --syntax scss",
21 | "lint-style-fix": "scriptoni stylefmt ./**/*.scss",
22 | "prepublish": "npm run build",
23 | "preversion": "npm run lint && npm run test && npm run build-examples",
24 | "build-examples": "npm run clean && webpack --config examples/webpack.config.build.babel.js --progress",
25 | "start": "webpack-dev-server --config examples/webpack.config.babel.js --progress --hot --inline",
26 | "clean": "rm -rf examples/build",
27 | "generate-readme": "babel-node ./generateReadme.js",
28 | "release-version": "smooth-release"
29 | },
30 | "repository": {
31 | "type": "git",
32 | "url": "https://github.com/buildo/rc-datepicker.git"
33 | },
34 | "keywords": [
35 | "react",
36 | "react-component",
37 | "datepicker",
38 | "calendar",
39 | "date"
40 | ],
41 | "author": "Francesco Cioria ",
42 | "license": "MIT",
43 | "bugs": {
44 | "url": "https://github.com/buildo/rc-datepicker/issues"
45 | },
46 | "homepage": "https://github.com/buildo/rc-datepicker",
47 | "devDependencies": {
48 | "@types/react": "^15.6.28",
49 | "babel-cli": "^6.8.0",
50 | "babel-core": "^6.9.0",
51 | "babel-loader": "^6.4.1",
52 | "babel-preset-buildo": "^0.1.1",
53 | "babel-register": "^6.9.0",
54 | "compression-webpack-plugin": "^0.3.2",
55 | "css-loader": "^0.23.1",
56 | "debug": "^2.6.9",
57 | "eslint-loader": "^1.9.0",
58 | "expect": "^1.20.2",
59 | "extract-text-webpack-plugin": "^1.0.1",
60 | "html-webpack-plugin": "^2.30.1",
61 | "isparta-loader": "^2.0.0",
62 | "karma": "^0.13.22",
63 | "karma-bamboo-reporter": "^0.1.2",
64 | "karma-chrome-launcher": "^1.0.1",
65 | "karma-coverage": "^1.1.2",
66 | "karma-mocha": "^1.3.0",
67 | "karma-webpack": "^1.8.1",
68 | "mocha": "^2.5.3",
69 | "node-libs-browser": "^1.1.1",
70 | "node-sass": "^4.13.1",
71 | "react": "^0.14",
72 | "react-addons-test-utils": "^0.14.8",
73 | "react-dom": "^0.14",
74 | "react-readme-generator": "0.0.1",
75 | "require-dir": "^0.3.2",
76 | "resolve-url-loader": "^1.6.1",
77 | "sass-loader": "^3.2.3",
78 | "scriptoni": "^0.6.16",
79 | "smooth-release": "^8.0.3",
80 | "style-loader": "^0.13.2",
81 | "webpack": "^1.15.0",
82 | "webpack-dev-server": "^1.16.5"
83 | },
84 | "peerDependencies": {
85 | "react": "^0.14 || ^15.0 || ^16.0",
86 | "react-dom": "^0.14 || ^15.0 || ^16.0"
87 | },
88 | "dependencies": {
89 | "classnames": "^2.2.5",
90 | "lodash": "^4.13.1",
91 | "moment": "^2.13.0",
92 | "react-flexview": "^4.0.4",
93 | "revenge": "^0.4.4",
94 | "tcomb": "^3.2.1",
95 | "tcomb-react": "^0.9.0"
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/DatePicker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import moment from 'moment';
3 | import t from 'tcomb';
4 | import { props } from 'tcomb-react';
5 | import { format, valueLink, skinnable } from './utils';
6 | import { Value, Mode } from './utils/model';
7 | import DayPicker from './daypicker/DayPicker';
8 | import MonthPicker from './monthpicker/MonthPicker';
9 | import YearPicker from './yearpicker/YearPicker';
10 | import cx from 'classnames';
11 |
12 | @valueLink
13 | @format
14 | @skinnable()
15 | @props({
16 | onChange: t.maybe(t.Function),
17 | value: t.maybe(Value),
18 | valueLink: t.maybe(t.interface({
19 | value: t.maybe(Value),
20 | requestChange: t.Function
21 | })),
22 | defaultValue: t.maybe(Value),
23 | minDate: t.maybe(Value),
24 | maxDate: t.maybe(Value),
25 | locale: t.maybe(t.String),
26 | startMode: t.maybe(Mode),
27 | startDate: t.maybe(Value),
28 | fixedMode: t.maybe(t.Boolean),
29 | returnFormat: t.maybe(t.String),
30 | floating: t.maybe(t.Boolean),
31 | closeOnClickOutside: t.maybe(t.Boolean), // used only with DatePickerInput
32 | className: t.maybe(t.String),
33 | prevIconClassName: t.maybe(t.String),
34 | nextIconClassName: t.maybe(t.String),
35 | position: t.maybe(t.enums.of(['top', 'bottom'])),
36 | style: t.maybe(t.Object),
37 | placeholder: t.maybe(t.String)
38 | })
39 | export default class DatePicker extends React.Component {
40 |
41 | static defaultProps = {
42 | startMode: 'day',
43 | className: '',
44 | prevIconClassName: 'icon-rc-datepicker icon-rc-datepicker_prev',
45 | nextIconClassName: 'icon-rc-datepicker icon-rc-datepicker_next',
46 | style: {},
47 | position: 'bottom'
48 | }
49 |
50 | constructor(props) {
51 | super(props);
52 | if (props.locale) {
53 | moment.locale(props.locale);
54 | if (process.env.NODE_ENV !== 'production' && moment.locale() !== props.locale) {
55 | console.warn(`Setting "${props.locale}" as locale failed. Did you import it correctly?`); // eslint-disable-line no-console
56 | }
57 | }
58 | this.state = this.getStateFromProps(props);
59 | }
60 |
61 | getStateFromProps = (_props) => {
62 | const { value } = this.getValueLink(_props);
63 | const { defaultValue, startDate, startMode } = _props;
64 | const date = typeof value === 'string' ? this.parsePropDateString(value) : moment(value);
65 | const initialDate = defaultValue ?
66 | typeof defaultValue === 'string' ? this.parsePropDateString(defaultValue) : moment(defaultValue) :
67 | typeof startDate === 'string' ? this.parsePropDateString(startDate) : moment(startDate);
68 |
69 | const visibleDate = value ? date.clone() : initialDate; // must be copy, otherwise they get linked
70 | return {
71 | date: value ? date.clone() : undefined,
72 | visibleDate,
73 | mode: startMode
74 | };
75 | }
76 |
77 | onChangeVisibleDate = (date) => {
78 | this.setState({ visibleDate: date });
79 | }
80 |
81 | onChangeSelectedDate = (date) => {
82 | this.setState({
83 | visibleDate: date.clone(), // must be copy, otherwise they get linked
84 | date
85 | }, () => this.getValueLink().requestChange(date.toDate()));
86 | }
87 |
88 | onChangeMode = (mode) => {
89 | setTimeout(() => this.setState({ mode }));
90 | }
91 |
92 | changeYear = (year) => {
93 | this.setState({ visibleDate: this.state.visibleDate.clone().year(year) });
94 | }
95 |
96 | changeMonth = (month) => {
97 | this.setState({ visibleDate: this.state.visibleDate.clone().month(month) });
98 | }
99 |
100 | getLocals({ className, style, floating, minDate, maxDate, fixedMode, prevIconClassName, nextIconClassName, position }) {
101 | const { mode, date, visibleDate } = this.state;
102 | return {
103 | style,
104 | className: cx('react-datepicker', className, { floating, 'position-top': position === 'top' }),
105 | dayPickerProps: mode === Mode('day') && {
106 | date, visibleDate,
107 | minDate, maxDate,
108 | mode,
109 | fixedMode,
110 | prevIconClassName,
111 | nextIconClassName,
112 | changeMonth: this.changeMonth,
113 | onSelectDate: this.onChangeSelectedDate,
114 | onChangeMode: this.onChangeMode
115 | },
116 | monthPickerProps: mode === Mode('month') && {
117 | date, visibleDate,
118 | minDate, maxDate,
119 | mode,
120 | fixedMode,
121 | prevIconClassName,
122 | nextIconClassName,
123 | changeYear: this.changeYear,
124 | onSelectDate: this.onChangeSelectedDate,
125 | onChangeMode: this.onChangeMode,
126 | onChangeVisibleDate: this.onChangeVisibleDate
127 | },
128 | yearPickerProps: mode === Mode('year') && {
129 | date, visibleDate,
130 | minDate, maxDate,
131 | mode,
132 | fixedMode,
133 | prevIconClassName,
134 | nextIconClassName,
135 | changeYear: this.changeYear,
136 | onSelectDate: this.onChangeSelectedDate,
137 | onChangeMode: this.onChangeMode,
138 | onChangeVisibleDate: this.onChangeVisibleDate
139 | }
140 | };
141 | }
142 |
143 | template({ className, style, dayPickerProps, monthPickerProps, yearPickerProps }) {
144 | return (
145 |
146 | {dayPickerProps && }
147 | {monthPickerProps && }
148 | {yearPickerProps && }
149 |
150 | );
151 | }
152 |
153 | componentWillReceiveProps(nextProps) {
154 | if (this.getValueLink(nextProps).value !== this.getValueLink().value) {
155 | this.setState(this.getStateFromProps(nextProps));
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/DatePickerInput.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import moment from 'moment';
4 | import t from 'tcomb';
5 | import { props } from 'tcomb-react';
6 | import omit from 'lodash/omit';
7 | import DatePicker from './DatePicker';
8 | import { Value } from './utils/model';
9 | import { format, valueLink, skinnable } from './utils';
10 | import cx from 'classnames';
11 | import Input from './Input';
12 |
13 | const INVALID = 'Invalid date';
14 | const ENTER_KEYCODE = 13;
15 |
16 | export const Props = {
17 | value: t.maybe(Value),
18 | valueLink: t.maybe(t.interface({
19 | value: t.maybe(Value),
20 | requestChange: t.Function
21 | })),
22 | onChange: t.maybe(t.Function),
23 | onShow: t.maybe(t.Function),
24 | onHide: t.maybe(t.Function),
25 | onClear: t.maybe(t.Function),
26 | small: t.maybe(t.Boolean),
27 | defaultValue: t.maybe(Value),
28 | minDate: t.maybe(Value),
29 | maxDate: t.maybe(Value),
30 | locale: t.maybe(t.String),
31 | startMode: t.maybe(t.enums.of(['day', 'month', 'year'])),
32 | startDate: t.maybe(Value),
33 | fixedMode: t.maybe(t.Boolean),
34 | displayFormat: t.maybe(t.String),
35 | returnFormat: t.maybe(t.String),
36 | format: t.maybe(t.String),
37 | validationFormat: t.maybe(t.String),
38 | showOnInputClick: t.maybe(t.Boolean),
39 | closeOnClickOutside: t.maybe(t.Boolean),
40 | showInputButton: t.maybe(t.Boolean),
41 | autoClose: t.maybe(t.Boolean),
42 | floating: t.maybe(t.Boolean),
43 | disabled: t.maybe(t.Boolean),
44 | position: t.maybe(t.enums.of(['top', 'bottom'])),
45 | iconClassName: t.maybe(t.String),
46 | iconClearClassName: t.maybe(t.String),
47 | className: t.maybe(t.String), // used to omit from inputProps
48 | style: t.maybe(t.Object), // used to omit from inputProps
49 | placeholder: t.maybe(t.String)
50 | };
51 |
52 | /** A decent and pretty date picker to be used with React
53 | * @param value - current date
54 | * @param valueLink - valueLink object to replace "value" and "onChange"
55 | * @param onChange - called when value changes
56 | * @param onShow - called when datepicker is opened
57 | * @param onHide - called when datepicker is closed
58 | * @param onClear - called when value is cleared
59 | * @param small - whether it's small or not
60 | * @param defaultValue - default date
61 | * @param minDate - minimum date selectable by the user
62 | * @param maxDate - maximum date selectable by the user
63 | * @param locale - locale used for translations
64 | * @param startMode - the start view of the datepicker
65 | * @param startDate - specify an initial "visible" date with no need to select a defaultValue
66 | * @param fixedMode - whether the user can use multiple views or not
67 | * @param displayFormat - MomentJS format used to display current date
68 | * @param returnFormat - MomentJS format used to format date before returing through "onChange"
69 | * @param format - MomentJS format used to format date before returing through "onChange"
70 | * @param validationFormat - MomentJS format used to format date before returing through "onChange"
71 | * @param showOnInputClick - whether the datepicker should open when user click on the input
72 | * @param closeOnClickOutside - whether the datepicker should close when user clicks outside of it
73 | * @param showInputButton - whether the input-button should be rendered
74 | * @param autoClose - pass true if you want the datepicker to close automatically after the user selects a value
75 | * @param floating - whether the datepicker should float over the page content (absolute position)
76 | * @param position - whether the datepicker should be rendered above or below the input field
77 | * @param disabled - whether the datepicker should be disabled or not
78 | * @param iconClassName - classname used for the icon
79 | * @param iconClearClassName - classname used for the clear icon
80 | * @param className - className used for the wrapper div
81 | * @param style - style used for the wrapper div
82 | * @param placeholder
83 | */
84 |
85 |
86 | @format
87 | @valueLink
88 | @skinnable()
89 | @props(Props, { strict: false })
90 | export default class DatePickerInput extends React.Component {
91 |
92 | static defaultProps = {
93 | onShow: () => {},
94 | onHide: () => {},
95 | startMode: 'day',
96 | autoClose: true,
97 | closeOnClickOutside: true,
98 | floating: true,
99 | small: false,
100 | showInputButton: true,
101 | position: 'bottom',
102 | iconClassName: 'icon-rc-datepicker icon-rc-datepicker_calendar',
103 | iconClearClassName: 'icon-rc-datepicker icon-rc-datepicker_clear',
104 | className: '',
105 | style: {}
106 | }
107 |
108 | datePickerInputRef = null;
109 |
110 | constructor(props) {
111 | super(props);
112 | if (props.locale) {
113 | moment.locale(props.locale);
114 | }
115 | const _date = this.getValueLink().value || props.defaultValue;
116 | const date = typeof _date === 'string' ? this.parsePropDateString(_date) : moment(_date);
117 | this.state = {
118 | date: _date ? date : undefined,
119 | hasValue: !!_date,
120 | dateString: _date ? this.formatDisplayedDate(date) : '',
121 | showing: false
122 | };
123 | }
124 |
125 | componentDidMount() {
126 | if (this.props.closeOnClickOutside) {
127 | this.addOnClickListener();
128 | }
129 | }
130 |
131 | addOnClickListener = () => {
132 | if (window.attachEvent) {
133 | // Internet Explorer
134 | window.attachEvent('onclick', this.hideOnClickOutside);
135 | } else if (window.addEventListener) {
136 | window.addEventListener('click', this.hideOnClickOutside, false);
137 | }
138 | }
139 |
140 | removeOnClickListener = () => {
141 | if (window.detachEvent) {
142 | // Internet Explorer
143 | window.detachEvent('onclick', this.hideOnClickOutside);
144 | } else if (window.removeEventListener) {
145 | window.removeEventListener('click', this.hideOnClickOutside, false);
146 | }
147 | }
148 |
149 | getDatePickerInput = () => {
150 | return ReactDOM.findDOMNode(this.datePickerInputRef);
151 | }
152 |
153 | isEventInsideDatePickerInput = (el) => {
154 | if (el === this.getDatePickerInput()) {
155 | return true;
156 | } else if (el.parentNode) {
157 | return this.isEventInsideDatePickerInput(el.parentNode);
158 | } else {
159 | return false;
160 | }
161 | }
162 |
163 | hideOnClickOutside = (e) => {
164 | if (!this.isEventInsideDatePickerInput(e.target) && this.state.showing) {
165 | this.hide();
166 | }
167 | }
168 |
169 | hide = () => {
170 | this.setState({ showing: false }, this.props.onHide);
171 | }
172 |
173 | show = () => {
174 | if (!this.state.showing) {
175 | this.setState({ showing: true }, this.props.onShow);
176 | }
177 | }
178 |
179 | toggleDatePicker = () => {
180 | const callback = this.state.showing ? this.props.onHide : this.props.onShow;
181 | this.setState({ showing: !this.state.showing }, callback);
182 | }
183 |
184 | hideOnEnterKey = (e) => {
185 | if (e.keyCode === ENTER_KEYCODE) {
186 | this.hide();
187 | }
188 | }
189 |
190 | onClear = () => {
191 | const _date = this.props.defaultValue;
192 | const date = typeof _date === 'string' ? this.parsePropDateString(_date) : moment(_date);
193 | this.setState(
194 | {
195 | date: _date ? date : undefined,
196 | dateString: _date ? this.formatDisplayedDate(date) : '',
197 | showing: false
198 | },
199 | this.props.onClear
200 | );
201 | }
202 |
203 | _onChangeDate = (jsDate) => {
204 | const newDate = moment(jsDate);
205 | const newDateString = this.formatDisplayedDate(newDate);
206 | if (this.props.autoClose) {
207 | this.hide();
208 | }
209 | this.getValueLink().requestChange(jsDate, this.formatReturnedDate(newDate));
210 | if (newDateString !== this.state.dateString) {
211 | this.setState({
212 | hasValue: true,
213 | date: newDate,
214 | dateString: newDateString
215 | });
216 | }
217 | }
218 |
219 | onChangeInput = ({ target: { value: dateString } }) => {
220 | if (dateString || this.state.date) {
221 | const parsedDate = this.parseInputDateString(dateString);
222 | const date = parsedDate.isValid() ? parsedDate : this.state.date;
223 |
224 | const jsDate = parsedDate.isValid() ? parsedDate.toDate() : INVALID;
225 | const returnedDateString = jsDate ? this.formatReturnedDate(parsedDate) : INVALID;
226 |
227 | this.setState({
228 | dateString,
229 | date,
230 | hasValue: parsedDate.isValid()
231 | }, () => this.getValueLink().requestChange(jsDate, returnedDateString));
232 | } else if (!dateString) {
233 | this.setState({ dateString });
234 | }
235 | }
236 |
237 | getLocals(props) {
238 | const {
239 | showInputButton,
240 | iconClassName,
241 | showOnInputClick,
242 | onClear,
243 | small,
244 | iconClearClassName,
245 | defaultValue,
246 | minDate, maxDate,
247 | locale,
248 | startMode,
249 | startDate,
250 | fixedMode,
251 | floating,
252 | closeOnClickOutside,
253 | className,
254 | disabled,
255 | position,
256 | placeholder,
257 | style
258 | } = props;
259 | const { showing: active, hasValue, dateString: value, date } = this.state;
260 |
261 | const inputProps = omit(props, Object.keys(Props));
262 | const onInputClick = showOnInputClick ? this.show : undefined;
263 | const onButtonClick = showInputButton ? this.toggleDatePicker : undefined;
264 | const onInputClear = onClear ? this.onClear : undefined;
265 |
266 | return {
267 | style,
268 | className: cx('react-datepicker-component', { 'is-disabled': disabled }, className),
269 | inputProps: {
270 | value,
271 | small, active, hasValue,
272 | iconClassName, iconClearClassName,
273 | onInputClick, onButtonClick, onInputClear,
274 | onInputChange: this.onChangeInput,
275 | onInputKeyUp: this.hideOnEnterKey,
276 | placeholder,
277 | ...inputProps
278 | },
279 | datePickerProps: active && {
280 | defaultValue,
281 | minDate,
282 | maxDate,
283 | locale,
284 | startMode,
285 | startDate,
286 | fixedMode,
287 | floating,
288 | position,
289 | closeOnClickOutside,
290 | value: date ? date.toDate() : undefined,
291 | onChange: this._onChangeDate
292 | }
293 | };
294 | }
295 |
296 | template({ className, style, inputProps, datePickerProps }) {
297 | return (
298 | { this.datePickerInputRef = input; }}>
299 |
300 | {datePickerProps && }
301 |
302 | );
303 | }
304 |
305 | componentWillReceiveProps(nextProps) {
306 | const { value } = this.getValueLink(nextProps);
307 |
308 | // Update `date` and `dateString` if `props.value` has changed
309 | if (value !== INVALID && value !== this.getValueLink().value) {
310 | if (value) {
311 | const date = typeof value === 'string' ?
312 | this.parsePropDateString(value, nextProps) : moment(value);
313 | this.setState({
314 | date,
315 | dateString: date.isValid() ?
316 | this.formatDisplayedDate(date, nextProps) : this.state.dateString
317 | });
318 | } else {
319 | this.setState({
320 | date: undefined,
321 | dateString: ''
322 | });
323 | }
324 | }
325 |
326 | // Close datepicker if `disabled` has switched to `true`
327 | if (nextProps.disabled && !this.props.disabled) {
328 | this.hide();
329 | }
330 | }
331 |
332 | componentWillUnmount() {
333 | if (this.props.closeOnClickOutside) {
334 | this.removeOnClickListener();
335 | }
336 | }
337 | }
338 |
--------------------------------------------------------------------------------
/src/Input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import { props } from 'tcomb-react';
4 | import t from 'tcomb';
5 | import View from 'react-flexview';
6 | import skinnable from './utils/skinnable';
7 | import pure from './utils/pure';
8 |
9 | @pure
10 | @skinnable()
11 | @props({
12 | value: t.maybe(t.String),
13 | onInputChange: t.Function,
14 | iconClearClassName: t.String,
15 | iconClassName: t.String,
16 | hasValue: t.Boolean,
17 | active: t.Boolean,
18 | small: t.Boolean,
19 | onButtonClick: t.maybe(t.Function),
20 | onInputClick: t.maybe(t.Function),
21 | onInputClear: t.maybe(t.Function),
22 | onInputKeyUp: t.Function
23 | }, { strict: false })
24 | export default class Input extends React.Component {
25 |
26 | getLocals(props) {
27 | const {
28 | value,
29 | iconClearClassName,
30 | iconClassName,
31 | hasValue,
32 | active,
33 | small,
34 | onButtonClick,
35 | onInputClick,
36 | onInputChange,
37 | onInputClear,
38 | onInputKeyUp,
39 | ...inputProps
40 | } = props;
41 |
42 | return {
43 | className: cx('react-datepicker-input', {
44 | 'is-open': active,
45 | 'has-value': hasValue,
46 | 'is-small': small
47 | }),
48 | inputButtonProps: onButtonClick && {
49 | onButtonClick, iconClassName,
50 | className: cx('input-button', { active })
51 | },
52 | clearButtonProps: onInputClear && hasValue && {
53 | onInputClear, iconClearClassName
54 | },
55 | inputProps: {
56 | value,
57 | onChange: onInputChange,
58 | onClick: onInputClick,
59 | onKeyUp: onInputKeyUp,
60 | ...inputProps
61 | }
62 | };
63 | }
64 |
65 | templateInputButton({ className, onButtonClick, iconClassName }) {
66 | return (
67 |
68 |
69 |
70 | );
71 | }
72 |
73 | templateClearButton({ onInputClear, iconClearClassName }) {
74 | return (
75 |
76 |
77 |
78 | );
79 | }
80 |
81 | template({ className, inputButtonProps, clearButtonProps, inputProps }) {
82 | return (
83 |
84 |
85 |
86 | {clearButtonProps && this.templateClearButton(clearButtonProps)}
87 | {inputButtonProps && this.templateInputButton(inputButtonProps)}
88 |
89 |
90 | );
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/InvalidDate.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import t from 'tcomb';
3 | import { props } from 'tcomb-react';
4 | import { pure, skinnable } from './utils';
5 |
6 | @pure
7 | @skinnable()
8 | @props({
9 | invalidDate: t.maybe(t.String)
10 | })
11 | export default class InvalidDate extends React.Component {
12 | template({ invalidDate }) {
13 | return (
14 |
15 |
16 | {invalidDate}
17 |
18 |
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Picker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import t from 'tcomb';
4 | import { props } from 'tcomb-react';
5 | import View from 'react-flexview';
6 | import { pure, skinnable } from './utils';
7 | import { MomentDate, Value, Mode } from './utils/model';
8 |
9 | @pure
10 | @skinnable()
11 | @props({
12 | date: MomentDate,
13 | minDate: t.maybe(Value),
14 | maxDate: t.maybe(Value),
15 | isSelected: t.Boolean,
16 | isCurrent: t.Boolean,
17 | isEnabled: t.Boolean,
18 | isDisabled: t.maybe(t.Boolean),
19 | onSelectDate: t.Function,
20 | mode: Mode
21 | })
22 | export default class Picker extends React.Component {
23 |
24 | onClick = e => {
25 | e.preventDefault();
26 | if (this.props.isEnabled) {
27 | this.props.onSelectDate(this.props.date);
28 | }
29 | }
30 |
31 | getFormat = mode => {
32 | switch (mode) {
33 | case Mode('day'): return 'D';
34 | case Mode('month'): return 'MMM';
35 | case Mode('year'): return 'YYYY';
36 | }
37 | }
38 |
39 | getLocals({ date, mode, isCurrent, isSelected, isEnabled }) {
40 | const string = date.format(this.getFormat(mode));
41 |
42 | return {
43 | value: string.charAt(0).toUpperCase() + string.slice(1), // first letter always uppercase
44 | className: cx('react-datepicker-picker', {
45 | [mode]: true,
46 | current: isCurrent,
47 | selected: isSelected,
48 | disabled: !isEnabled
49 | }),
50 | onClick: this.onClick
51 | };
52 | }
53 |
54 | template({ className, onClick, value }) {
55 | return (
56 |
61 | {value}
62 |
63 | );
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/PickerTop.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import t from 'tcomb';
4 | import { props, ReactChildren } from 'tcomb-react';
5 | import View from 'react-flexview';
6 | import { pure, skinnable } from './utils';
7 |
8 | @pure
9 | @skinnable()
10 | @props({
11 | fixed: t.maybe(t.Boolean),
12 | handleClick: t.maybe(t.Function),
13 | nextDate: t.maybe(t.Function),
14 | previousDate: t.maybe(t.Function),
15 | value: t.union([t.String, t.Number]),
16 | weekDays: t.maybe(ReactChildren),
17 | prevIconClassName: t.String,
18 | nextIconClassName: t.String
19 | })
20 | export default class PickerTop extends React.Component {
21 | template({ value, fixed, previousDate, nextDate, handleClick, weekDays, prevIconClassName, nextIconClassName }) {
22 | return (
23 |
24 |
25 |
30 |
31 |
32 |
37 | {value}
38 |
39 |
44 |
45 |
46 |
47 | {weekDays}
48 |
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # DatePickerInput
2 |
3 | A decent and pretty date picker to be used with React
4 |
5 | ## Props
6 | |Name|Type|Default|Description|
7 | |----|----|-------|-----------|
8 | | **value** | union(String | Date | MomentDate)
| | *optional*. Current date |
9 | | **valueLink** | {value: ?String | Date | MomentDate, requestChange: Function}
| | *optional*. ValueLink object to replace "value" and "onChange" |
10 | | **onChange** | {jsDate: Date, dateString: string}
| | *optional*. Called when value changes |
11 | | **onShow** | Function
| "onShow"
| *optional*. Called when datepicker is opened |
12 | | **onHide** | Function
| "onHide"
| *optional*. Called when datepicker is closed |
13 | | **onClear** | Function
| | *optional*. Called when value is cleared |
14 | | **small** | Boolean
| false
| *optional*. Whether it's small or not |
15 | | **defaultValue** | union(String | Date | MomentDate)
| | *optional*. Default date |
16 | | **minDate** | union(String | Date | MomentDate)
| | *optional*. Minimum date selectable by the user |
17 | | **maxDate** | union(String | Date | MomentDate)
| | *optional*. Maximum date selectable by the user |
18 | | **locale** | String
| | *optional*. Locale used for translations |
19 | | **startMode** | enum("day" | "month" | "year")
| "day"
| *optional*. The start view of the datepicker |
20 | | **startDate** | union(String | Date | MomentDate)
| | *optional*. Specify an initial "visible" date with no need to select a defaultValue |
21 | | **fixedMode** | Boolean
| | *optional*. Whether the user can use multiple views or not |
22 | | **displayFormat** | String
| | *optional*. MomentJS format used to display current date |
23 | | **returnFormat** | String
| | *optional*. MomentJS format used to format date before returing through "onChange" |
24 | | **format** | String
| | *optional*. MomentJS format used to format date before returing through "onChange" |
25 | | **validationFormat** | String
| | *optional*. MomentJS format used to format date before returing through "onChange" |
26 | | **showOnInputClick** | Boolean
| | *optional*. Whether the datepicker should open when user click on the input |
27 | | **closeOnClickOutside** | Boolean
| true
| *optional*. Whether the datepicker should close when user clicks outside of it |
28 | | **showInputButton** | Boolean
| true
| *optional*. Whether the input-button should be rendered |
29 | | **autoClose** | Boolean
| true
| *optional*. Pass true if you want the datepicker to close automatically after the user selects a value |
30 | | **floating** | Boolean
| true
| *optional*. Whether the datepicker should float over the page content (absolute position) |
31 | | **disabled** | Boolean
| | *optional*. Whether the datepicker should be disabled or not |
32 | | **position** | enum("top" | "bottom")
| "bottom"
| *optional*. Whether the datepicker should be rendered above or below the input field |
33 | | **iconClassName** | String
| "icon-rc-datepicker icon-rc-datepicker_calendar"
| *optional*. Classname used for the icon |
34 | | **iconClearClassName** | String
| "icon-rc-datepicker icon-rc-datepicker_clear"
| *optional*. Classname used for the clear icon |
35 | | **className** | String
| ""
| *optional*. ClassName used for the wrapper div |
36 | | **style** | Object
| {}
| *optional*. Style used for the wrapper div |
37 | | **placeholder** | String
| | *optional*. |
--------------------------------------------------------------------------------
/src/Row.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import cx from 'classnames';
3 | import t from 'tcomb';
4 | import { props, ReactChildren } from 'tcomb-react';
5 | import View from 'react-flexview';
6 | import { pure, skinnable } from './utils';
7 | import { Mode } from './utils/model';
8 |
9 | @pure
10 | @skinnable()
11 | @props({
12 | pickers: t.list(ReactChildren),
13 | mode: Mode
14 | })
15 | export default class Row extends React.Component {
16 |
17 | getLocals({ mode, pickers }) {
18 | return {
19 | pickers,
20 | className: cx('react-datepicker-row', mode)
21 | };
22 | }
23 |
24 | template({ className, pickers }) {
25 | return (
26 |
27 | {pickers}
28 |
29 | );
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/daypicker/DayPicker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import t from 'tcomb';
3 | import { props } from 'tcomb-react';
4 | import View from 'react-flexview';
5 | import { pure, skinnable } from '../utils';
6 | import { Value, Mode, MomentDate } from '../utils/model';
7 | import DayPickerTop from './DayPickerTop';
8 | import DayPickerBody from './DayPickerBody';
9 |
10 | @pure
11 | @skinnable()
12 | @props({
13 | changeMonth: t.Function,
14 | visibleDate: MomentDate,
15 | date: t.maybe(Value),
16 | minDate: t.maybe(Value),
17 | maxDate: t.maybe(Value),
18 | onSelectDate: t.Function,
19 | onChangeMode: t.Function,
20 | mode: Mode,
21 | fixedMode: t.maybe(t.Boolean),
22 | prevIconClassName: t.String,
23 | nextIconClassName: t.String
24 | })
25 | export default class DayPicker extends React.Component {
26 |
27 | getLocals({
28 | date, visibleDate, onSelectDate, minDate,
29 | maxDate, changeMonth, onChangeMode, mode, fixedMode,
30 | prevIconClassName, nextIconClassName
31 | }) {
32 | return {
33 | dayPickerTopProps: {
34 | visibleDate,
35 | changeMonth,
36 | onChangeMode,
37 | fixedMode,
38 | prevIconClassName,
39 | nextIconClassName
40 | },
41 | dayPickerBodyProps: {
42 | date, visibleDate,
43 | minDate, maxDate,
44 | onSelectDate,
45 | mode
46 | }
47 | };
48 | }
49 |
50 | template({ dayPickerTopProps, dayPickerBodyProps }) {
51 | return (
52 |
53 |
54 |
55 |
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/daypicker/DayPickerBody.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import range from 'lodash/range';
3 | import t from 'tcomb';
4 | import { props } from 'tcomb-react';
5 | import View from 'react-flexview';
6 | import { pure, skinnable } from '../utils';
7 | import { Value, Mode, MomentDate } from '../utils/model';
8 | import InvalidDate from '../InvalidDate';
9 | import Picker from '../Picker';
10 | import Row from '../Row';
11 | import { isInsideTheEnabledArea, getVisibleDays } from '../utils/DateUtils';
12 |
13 | const COLUMNS = 7;
14 | const ROWS = 6;
15 |
16 | @pure
17 | @skinnable()
18 | @props({
19 | visibleDate: MomentDate,
20 | date: t.maybe(Value),
21 | minDate: t.maybe(Value),
22 | maxDate: t.maybe(Value),
23 | onSelectDate: t.Function,
24 | mode: Mode
25 | })
26 | export default class DayPickerBody extends React.Component {
27 |
28 | getLocals({ date, visibleDate, minDate, maxDate, onSelectDate, mode }) {
29 | if (!visibleDate.isValid()) {
30 | return ;
31 | }
32 | const year = visibleDate.year();
33 | const month = visibleDate.month();
34 | const selectedDateString = date ? date.format('DD/MM/YYYY') : undefined;
35 |
36 | const visibleDays = getVisibleDays(month, year);
37 | const pickers = visibleDays.days.map((dayOfMonth, index) => {
38 | const date = visibleDate.clone();
39 | const isCurrent = index >= visibleDays.startCurrent && index <= visibleDays.endCurrent;
40 | if (!isCurrent) {
41 | date.add(index < visibleDays.startCurrent ? -1 : 1, 'M');
42 | }
43 | date.date(dayOfMonth);
44 | const dateString = date.format('DD/MM/YYYY');
45 | return {
46 | date,
47 | isCurrent,
48 | onSelectDate,
49 | mode,
50 | isSelected: dateString === selectedDateString,
51 | isEnabled: isInsideTheEnabledArea(date, mode, minDate, maxDate),
52 | key: dateString
53 | };
54 | });
55 |
56 | return { pickers, mode };
57 | }
58 |
59 | templateDays = ({ pickers }) => pickers.map(p => )
60 |
61 | template({ pickers, mode }) {
62 | const days = this.templateDays({ pickers });
63 | const rows = range(ROWS).map(index =>
64 |
65 | );
66 |
67 | return (
68 |
69 | {rows}
70 |
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/daypicker/DayPickerTop.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import capitalize from 'lodash/capitalize';
3 | import t from 'tcomb';
4 | import { props } from 'tcomb-react';
5 | import View from 'react-flexview';
6 | import { pure, skinnable } from '../utils';
7 | import { MomentDate, Mode } from '../utils/model';
8 | import { getWeekdaysMin } from '../utils/DateUtils.js';
9 | import PickerTop from '../PickerTop';
10 |
11 | @pure
12 | @skinnable()
13 | @props({
14 | changeMonth: t.Function,
15 | visibleDate: MomentDate,
16 | onChangeMode: t.Function,
17 | fixedMode: t.maybe(t.Boolean),
18 | prevIconClassName: t.String,
19 | nextIconClassName: t.String
20 | })
21 | export default class DayPickerTop extends React.Component {
22 |
23 | onChangeMode = () => {
24 | if (!this.props.fixedMode) {
25 | this.props.onChangeMode(Mode('month'));
26 | }
27 | }
28 |
29 | getMonth = () => this.props.visibleDate.month()
30 |
31 | previousDate = () => this.props.changeMonth(this.getMonth() - 1)
32 |
33 | nextDate = () => this.props.changeMonth(this.getMonth() + 1)
34 |
35 | getLocals({ visibleDate, fixedMode, prevIconClassName, nextIconClassName }) {
36 | return {
37 | fixed: !!fixedMode,
38 | value: capitalize(visibleDate.format('MMMM YYYY')),
39 | handleClick: this.onChangeMode,
40 | previousDate: this.previousDate,
41 | nextDate: this.nextDate,
42 | weekDays: getWeekdaysMin(),
43 | prevIconClassName,
44 | nextIconClassName
45 | };
46 | }
47 |
48 | templateWeekDays = ({ weekDays }) => (
49 |
50 | {weekDays.map((dayMin, i) => (
51 |
56 | {dayMin}
57 |
58 | ))}
59 |
60 | )
61 |
62 | template({ weekDays, ...locales }) {
63 | return (
64 |
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/icons.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'rc-datepicker';
3 | src: url('data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SDIcAAAC8AAAAYGNtYXAAitFNAAABHAAAAGRnYXNwAAAAEAAAAYAAAAAIZ2x5ZmYIkl0AAAGIAAAC7GhlYWQLeirXAAAEdAAAADZoaGVhB3kDyQAABKwAAAAkaG10eBKTAOAAAATQAAAAIGxvY2ECQgFeAAAE8AAAABJtYXhwABgAfgAABQQAAAAgbmFtZUzHCYMAAAUkAAABznBvc3QAAwAAAAAG9AAAACAAAwLqAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADwcwPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQASAAAAA4ACAACAAYAAQAg8A3wVPBz//3//wAAAAAAIPAN8FPwc//9//8AAf/jD/cPsg+UAAMAAQAAAAAAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQA/AD8C5gLmADwAACUUBg8BDgEjIiYvAQcOASMiJi8BLgE1NDY/AScuATU0Nj8BPgEzMhYfATc+ATMyFh8BHgEVFAYPARceARUC5gkHTggUCwsUCKioBxULChUHTggICAioqAgICAhOBxUKCxUHqKgIFAsLFAhOBwkJB6ioBwnDChUHTggICAioqAgICAhOBxUKCxUHqKgIFAsLFAhOBwkJB6ioBwkJB04IFAsLFAioqAcVCwAAAAEAYwAaAp0DnQAVAAAJAhYUDwEGIicBJjQ3ATYyHwEWFAcCnf7RAS8LC18KHgv+WAsLAagLHgpfCwsDC/7Q/tELHgpfCwsBqAoeCwGoCwtfCh4LAAEAPgAaAnkDnQAVAAAJAQYiLwEmNDcJASY0PwE2MhcBFhQHAnn+WAseC18KCgEw/tAKCl8LHgsBqAoKAcL+WAsLXwoeCwEvATALHgpfCwv+WAseCgAAAAAPAAD/twO3A7cAAwAIAAwAEQAVABoAHwAjACgAOAA8AEEARQBWAHsAADczNSMXMzUjFSczNSMXMzUjFSczNSMBMzUjFQMzNSMVATM1IyczNSMVAzU0JisBIgYdARQWOwEyNgEzNSMnMzUjFTsBNSM3NTQmKwEiBh0BFBY7ATI2NTcRFAYjISImNRE0NjsBNTQ2OwEyFh0BMzU0NjsBMhYdATMyFhVJpaXJt7fJpaXJt7fJpaUBpbe33Le3Abelpdu3t8kLCCQICwsIJAgLAaSlpdu3t9ulpRILByQICwsIJAcL3Cse/NseKyseSTYmJCY22zYmJCY2SR4rAKWlpaXJt7e3t9yk/belpQGlpKT+W6Ukt7cB7qQICwsIpAcLC/4ZtyWkpKRupAgLCwikBwsLByT9JR4rKx4C2x4sNiY2NiY2NiY2NiY2LB4AAAEAAAABAABplrQ9Xw889QALBAAAAAAA1FHzNgAAAADUUfM2AAD/twO3A7cAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAA7cAAQAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAAAAAAACAAAAAyUAPwMAAGMCtwA+A7cAAAAAAAAACgAUAB4AegCkANABdgAAAAEAAAAIAHwADwAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAOAK4AAQAAAAAAAQANAAAAAQAAAAAAAgAHAJYAAQAAAAAAAwANAEgAAQAAAAAABAANAKsAAQAAAAAABQALACcAAQAAAAAABgANAG8AAQAAAAAACgAaANIAAwABBAkAAQAaAA0AAwABBAkAAgAOAJ0AAwABBAkAAwAaAFUAAwABBAkABAAaALgAAwABBAkABQAWADIAAwABBAkABgAaAHwAAwABBAkACgA0AOxyYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJWZXJzaW9uIDEuMABWAGUAcgBzAGkAbwBuACAAMQAuADByYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJyYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJSZWd1bGFyAFIAZQBnAHUAbABhAHJyYy1kYXRlcGlja2VyAHIAYwAtAGQAYQB0AGUAcABpAGMAawBlAHJGb250IGdlbmVyYXRlZCBieSBJY29Nb29uLgBGAG8AbgB0ACAAZwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABJAGMAbwBNAG8AbwBuAC4AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') format('truetype');
4 | font-weight: 400;
5 | font-style: normal;
6 | }
7 |
8 | .icon-rc-datepicker {
9 | /* use !important to prevent issues with browser extensions that change fonts */
10 | font-family: 'rc-datepicker' !important;
11 | speak: none;
12 | font-style: normal;
13 | font-weight: 400;
14 | font-variant: normal;
15 | text-transform: none;
16 | line-height: 1;
17 |
18 | /* Better Font Rendering =========== */
19 | -webkit-font-smoothing: antialiased;
20 | -moz-osx-font-smoothing: grayscale;
21 | }
22 |
23 | .icon-rc-datepicker_clear::before {
24 | content: '\f00d';
25 | }
26 |
27 | .icon-rc-datepicker_prev::before {
28 | content: '\f053';
29 | }
30 |
31 | .icon-rc-datepicker_next::before {
32 | content: '\f054';
33 | }
34 |
35 | .icon-rc-datepicker_calendar::before {
36 | content: '\f073';
37 | }
38 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import DatePicker from './DatePicker';
2 | import DatePickerInput from './DatePickerInput';
3 |
4 | export DatePicker from './DatePicker';
5 | export DatePickerInput from './DatePickerInput';
6 |
7 | export default { DatePicker, DatePickerInput };
8 |
--------------------------------------------------------------------------------
/src/monthpicker/MonthPicker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import t from 'tcomb';
3 | import { props } from 'tcomb-react';
4 | import { pure, skinnable } from '../utils';
5 | import { MomentDate, Value, Mode } from '../utils/model';
6 | import MonthPickerTop from './MonthPickerTop';
7 | import MonthPickerBody from './MonthPickerBody';
8 |
9 | @pure
10 | @skinnable()
11 | @props({
12 | changeYear: t.Function,
13 | visibleDate: MomentDate,
14 | date: t.maybe(Value),
15 | minDate: t.maybe(Value),
16 | maxDate: t.maybe(Value),
17 | onChangeVisibleDate: t.Function,
18 | onSelectDate: t.Function,
19 | onChangeMode: t.Function,
20 | mode: Mode,
21 | fixedMode: t.maybe(t.Boolean),
22 | prevIconClassName: t.String,
23 | nextIconClassName: t.String
24 | })
25 | export default class MonthPicker extends React.Component {
26 |
27 | onSelectDate = (date) => {
28 | const { fixedMode, onSelectDate, onChangeMode, onChangeVisibleDate } = this.props;
29 | if (fixedMode) {
30 | onSelectDate(date);
31 | } else {
32 | onChangeVisibleDate(date);
33 | onChangeMode(Mode('day'));
34 | }
35 | }
36 |
37 | getLocals({
38 | date, visibleDate, minDate,
39 | maxDate, changeYear, onChangeMode, mode, fixedMode,
40 | prevIconClassName, nextIconClassName
41 | }) {
42 | return {
43 | monthPickerTopProps: {
44 | visibleDate,
45 | changeYear,
46 | onChangeMode,
47 | fixedMode,
48 | prevIconClassName,
49 | nextIconClassName
50 | },
51 | monthPickerBodyProps: {
52 | date, visibleDate,
53 | minDate, maxDate,
54 | mode,
55 | onSelectDate: this.onSelectDate
56 | }
57 | };
58 | }
59 |
60 | template({ monthPickerTopProps, monthPickerBodyProps }) {
61 | return (
62 |
63 |
64 |
65 |
66 | );
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/monthpicker/MonthPickerBody.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import moment from 'moment';
3 | import t from 'tcomb';
4 | import { props } from 'tcomb-react';
5 | import { pure, skinnable } from '../utils';
6 | import { Value, Mode, MomentDate } from '../utils/model';
7 | import InvalidDate from '../InvalidDate';
8 | import Picker from '../Picker';
9 | import Row from '../Row';
10 | import { isInsideTheEnabledArea } from '../utils/DateUtils';
11 | import range from 'lodash/range';
12 |
13 | const COLUMNS = 4;
14 | const ROWS = 3;
15 |
16 | @pure
17 | @skinnable()
18 | @props({
19 | visibleDate: MomentDate,
20 | date: t.maybe(Value),
21 | minDate: t.maybe(Value),
22 | maxDate: t.maybe(Value),
23 | onSelectDate: t.Function,
24 | mode: Mode
25 | })
26 | export default class MonthPickerBody extends React.Component {
27 |
28 | getLocals({ date, visibleDate, minDate, maxDate, onSelectDate, mode }) {
29 | if (!visibleDate.isValid()) {
30 | return ;
31 | }
32 | const year = visibleDate.year();
33 | const selectedMonth = date ? date.month() : -1;
34 | const selectedYear = date ? date.year() : -1;
35 | const pickers = moment.months().map((_, index) => {
36 | const date = moment([year, index, 1]);
37 | return {
38 | date,
39 | onSelectDate,
40 | mode,
41 | isCurrent: true,
42 | isSelected: selectedMonth === index && selectedYear === year,
43 | isEnabled: isInsideTheEnabledArea(date, mode, minDate, maxDate),
44 | key: index
45 | };
46 | });
47 |
48 | return { pickers, mode };
49 | }
50 |
51 | templateMonths = ({ pickers }) => pickers.map(p => )
52 |
53 | template({ pickers, mode }) {
54 | const months = this.templateMonths({ pickers });
55 | const rows = range(ROWS).map(index => (
56 |
61 | ));
62 |
63 | return (
64 |
65 | {rows}
66 |
67 | );
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/monthpicker/MonthPickerTop.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import t from 'tcomb';
3 | import { props } from 'tcomb-react';
4 | import { pure, skinnable } from '../utils';
5 | import { MomentDate, Mode } from '../utils/model';
6 | import PickerTop from '../PickerTop';
7 |
8 | @pure
9 | @skinnable()
10 | @props({
11 | visibleDate: MomentDate,
12 | onChangeMode: t.Function,
13 | changeYear: t.Function,
14 | fixedMode: t.maybe(t.Boolean),
15 | prevIconClassName: t.String,
16 | nextIconClassName: t.String
17 | })
18 | export default class MonthPickerTop extends React.Component {
19 |
20 | onChangeMode = () => {
21 | if (!this.props.fixedMode) {
22 | this.props.onChangeMode(Mode('year'));
23 | }
24 | }
25 |
26 | getYear = () => this.props.visibleDate.year()
27 |
28 | previousDate = () => this.props.changeYear(this.getYear() - 1)
29 |
30 | nextDate = () => this.props.changeYear(this.getYear() + 1)
31 |
32 | getLocals({ fixedMode }) {
33 | return {
34 | prevIconClassName: this.props.prevIconClassName,
35 | nextIconClassName: this.props.nextIconClassName,
36 | fixed: !!fixedMode,
37 | value: this.getYear(),
38 | handleClick: this.onChangeMode,
39 | previousDate: this.previousDate,
40 | nextDate: this.nextDate
41 | };
42 | }
43 |
44 | template(locales) {
45 | return ;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/style.scss:
--------------------------------------------------------------------------------
1 | @import './icons.scss';
2 |
3 | // PALETTE
4 |
5 | $_gradientGrey: linear-gradient(#fff, #f2f4f7);
6 | $_gradientDarkGrey: linear-gradient(#fff, #dfe3e8);
7 | $_gradientAzure: linear-gradient(#2da1f8, #1789dd);
8 | $_white: #fff;
9 | $_paleGrey: #f0f3f8;
10 | $_silver: #ced0da;
11 | $_cloudyGrey: #b5c0ce;
12 | $_coolGrey: #9098a7;
13 | $_darkGrey: #354052;
14 | $_cerulean: #2da1f8;
15 | $_waterBlue: #1991eb;
16 | $_strawberry: #db242c;
17 |
18 | // INPUT VARIABLES
19 |
20 | $font-size: 14px !default;
21 | $font-weight: 600 !default;
22 | $font-weight-selected: bold !default;
23 |
24 | $input-height: 36px !default;
25 | $input-height-small: 32px !default;
26 | $input-min-width: 150px !default;
27 |
28 | $input-font-size: 14px !default;
29 |
30 | $input-border-radius: 4px !default;
31 | $input-border-color: $_silver !default;
32 | $input-border-color-hover: $input-border-color !default;
33 | $input-border-color-open: $_cerulean !default;
34 |
35 | $input-background: $_gradientGrey !default;
36 | $input-background-hover: $_gradientDarkGrey !default;
37 | $input-background-open: $_gradientDarkGrey !default;
38 |
39 | $input-color: $_darkGrey !default;
40 | $input-color-hover: $input-color !default;
41 | $input-color-open: $input-color !default;
42 | $input-color-has-value: $_darkGrey !default;
43 |
44 | $input-placeholder-color: $_coolGrey !default;
45 | $input-placeholder-color-hover: $input-placeholder-color !default;
46 |
47 | $input-button-icon-color: $_cloudyGrey !default;
48 | $input-button-icon-color-hover: $_coolGrey !default;
49 | $input-button-icon-color-open: $input-button-icon-color !default;
50 |
51 | $input-button-icon-size: 15px !default;
52 |
53 | $input-button-background: transparent !default;
54 | $input-button-background-hover: transparent !default;
55 |
56 | $input-clear-button-icon-size: 13px !default;
57 | $input-clear-button-color: $input-button-icon-color !default;
58 | $input-clear-button-color-hover: $_strawberry !default;
59 |
60 | $input-opacity-disabled: .5 !default;
61 |
62 | // PICKER VARIABLES
63 |
64 | $picker-width: 250px !default;
65 |
66 | $picker-color: $_coolGrey !default;
67 | $picker-color-hover: $_darkGrey !default;
68 |
69 | $picker-background: $_white !default;
70 | $picker-background-hover: $_paleGrey !default;
71 |
72 | $picker-border-radius: 2px !default;
73 | $picker-border-color: #d9dee3 !default;
74 |
75 | $picker-box-shadow: 2px 2px 2px $_coolGrey !default;
76 | $picker-arrow-size: 5px !default;
77 |
78 | $picker-header-background: $_gradientAzure !default;
79 | $picker-header-background-hover: rgba(0, 0, 0, .075) !default;
80 | $picker-header-color: $_white !default;
81 | $picker-header-border-color: $_cerulean !default;
82 |
83 | $picker-current-background: $_paleGrey !default;
84 | $picker-current-background-hover: darken($picker-current-background, 5%) !default;
85 | $picker-current-color: $_darkGrey !default;
86 | $picker-current-color-hover: $picker-current-color !default;
87 |
88 | $picker-selected-background: #bad7f2 !default;
89 | $picker-selected-background-hover: $picker-selected-background !default;
90 | $picker-selected-color: $_darkGrey !default;
91 | $picker-selected-color-hover: $picker-selected-color !default;
92 |
93 | $picker-disabled-background: $_white !default;
94 | $picker-disabled-color: $_coolGrey !default;
95 |
96 | // STYLE
97 |
98 | @mixin placeholder {
99 | &::-webkit-input-placeholder {
100 | @content;
101 | }
102 |
103 | &:-moz-placeholder {
104 | @content;
105 | }
106 |
107 | &::-moz-placeholder {
108 | @content;
109 | }
110 |
111 | &:-ms-input-placeholder {
112 | @content;
113 | }
114 | }
115 |
116 | // DatePickerInput
117 |
118 | .react-datepicker-component {
119 | position: relative;
120 | font-size: $font-size;
121 | font-weight: $font-weight;
122 |
123 | &.is-disabled {
124 | pointer-events: none;
125 | opacity: $input-opacity-disabled;
126 | }
127 |
128 | .react-datepicker {
129 | margin-left: 5px;
130 | margin-top: 5px;
131 | display: inherit;
132 | }
133 |
134 | .react-datepicker-input {
135 | position: relative;
136 | min-width: $input-min-width;
137 | height: $input-height;
138 | background: $input-background;
139 | border: 1px solid $input-border-color;
140 | border-radius: $input-border-radius;
141 |
142 | &.is-small {
143 | height: $input-height-small;
144 | }
145 |
146 | input {
147 | width: 100%;
148 | height: 100%;
149 | background: transparent;
150 | border: none;
151 | box-sizing: border-box;
152 | padding-left: 15px;
153 | padding-right: 60px;
154 | font-size: $input-font-size;
155 | color: $input-color;
156 | font-weight: 600;
157 |
158 | &:focus {
159 | outline: none;
160 | }
161 |
162 | @include placeholder() {
163 | color: $input-placeholder-color;
164 | font-weight: 600;
165 | }
166 | }
167 |
168 | .button-wrapper {
169 | position: absolute;
170 | right: 0;
171 | top: 0;
172 | height: 100%;
173 |
174 | .input-button {
175 | margin: 0 10px;
176 | background: $input-button-background;
177 | font-size: $input-button-icon-size;
178 | border-radius: 0 $input-border-radius $input-border-radius 0;
179 | cursor: pointer;
180 | color: $input-button-icon-color;
181 |
182 | &:hover {
183 | background: $input-button-background-hover;
184 | color: $input-button-icon-color-hover;
185 | }
186 | }
187 |
188 | .clear-button {
189 | cursor: pointer;
190 | font-size: $input-clear-button-icon-size;
191 | color: $input-clear-button-color;
192 |
193 | &:hover {
194 | color: $input-clear-button-color-hover;
195 | }
196 | }
197 | }
198 |
199 | &:hover {
200 | background: $input-background-hover;
201 | border: 1px solid $input-border-color-hover;
202 |
203 | input {
204 | color: $input-color-hover;
205 |
206 | @include placeholder() {
207 | color: $input-placeholder-color-hover;
208 | }
209 | }
210 |
211 | .button-wrapper .input-button {
212 | color: $input-button-icon-color-hover;
213 | }
214 | }
215 |
216 | &.is-open {
217 | background: $input-background-open;
218 | border: 1px solid $input-border-color-open;
219 |
220 | input {
221 | color: $input-color-open;
222 |
223 | @include placeholder() {
224 | color: $input-color-open;
225 | }
226 | }
227 |
228 | .button-wrapper .input-button {
229 | color: $input-button-icon-color-open;
230 | }
231 | }
232 |
233 | &.has-value input {
234 | color: $input-color-has-value;
235 | }
236 | }
237 | }
238 |
239 | // DatePicker
240 |
241 | .react-datepicker {
242 | -webkit-touch-callout: none;
243 | -webkit-user-select: none;
244 | -khtml-user-select: none;
245 | -moz-user-select: none;
246 | -ms-user-select: none;
247 | user-select: none;
248 | display: inline-block;
249 | font-size: $font-size;
250 | font-weight: $font-weight;
251 |
252 | &.floating {
253 | position: absolute;
254 | z-index: 10000;
255 | box-shadow: 1px 1px 5px 1px rgba(0, 0, 0, .1);
256 | }
257 |
258 | &.position-top {
259 | top: auto;
260 | bottom: 100%;
261 | margin-bottom: 5px;
262 |
263 | .react-datepicker-container {
264 | &::after,
265 | &::before {
266 | top: 100%;
267 | left: 50%;
268 | border: solid transparent;
269 | content: ' ';
270 | height: 0;
271 | width: 0;
272 | position: absolute;
273 | pointer-events: none;
274 | }
275 |
276 | &::after {
277 | border-top-color: $picker-border-color;
278 | border-width: $picker-arrow-size;
279 | margin-left: -1 * $picker-arrow-size;
280 | }
281 |
282 | &::before {
283 | border-top-color: $picker-border-color;
284 | border-width: ($picker-arrow-size + 1);
285 | margin-left: -1 * ($picker-arrow-size + 1);
286 | }
287 | }
288 | }
289 |
290 | &:not(.position-top) {
291 | .react-datepicker-container {
292 | .react-datepicker-top {
293 | &::after,
294 | &::before {
295 | bottom: 100%;
296 | left: 50%;
297 | border: solid transparent;
298 | content: ' ';
299 | height: 0;
300 | width: 0;
301 | position: absolute;
302 | pointer-events: none;
303 | }
304 |
305 | &::after {
306 | border-bottom-color: $picker-header-border-color;
307 | border-width: $picker-arrow-size;
308 | margin-left: -1 * $picker-arrow-size;
309 | }
310 |
311 | &::before {
312 | border-bottom-color: $picker-border-color;
313 | border-width: ($picker-arrow-size + 1);
314 | margin-left: -1 * ($picker-arrow-size + 1);
315 | }
316 | }
317 | }
318 | }
319 |
320 | .react-datepicker-container {
321 | width: $picker-width;
322 | position: relative;
323 |
324 | .react-datepicker-top {
325 | text-align: center;
326 | background: $picker-header-background;
327 | color: $picker-header-color;
328 | border-top: 1px solid $picker-header-border-color;
329 | border-left: 1px solid $picker-header-border-color;
330 | border-right: 1px solid $picker-header-border-color;
331 | border-top-left-radius: $picker-border-radius;
332 | border-top-right-radius: $picker-border-radius;
333 |
334 | .week-days {
335 | height: 35px;
336 |
337 | .week-day {
338 | cursor: default;
339 | font-weight: 400;
340 | font-size: 13px;
341 | }
342 | }
343 |
344 | .display {
345 | height: 35px;
346 |
347 | .react-datepicker-button {
348 | text-decoration: none;
349 | padding: 4px;
350 | text-align: center;
351 | font-size: 15px;
352 | letter-spacing: .5px;
353 | cursor: pointer;
354 |
355 | &.button-left {
356 | font-size: 13px;
357 | padding: 4px 16px;
358 | border-top-left-radius: $picker-border-radius;
359 | }
360 |
361 | &.button-right {
362 | font-size: 13px;
363 | padding: 4px 16px;
364 | border-top-right-radius: $picker-border-radius;
365 | }
366 |
367 | &:hover {
368 | background: $picker-header-background-hover;
369 | border-radius: 4px;
370 | }
371 |
372 | &.fixed:hover {
373 | background: transparent;
374 | cursor: default;
375 | }
376 | }
377 | }
378 | }
379 |
380 | .react-datepicker-body {
381 | border-left: 1px solid $picker-border-color;
382 | border-right: 1px solid $picker-border-color;
383 | border-bottom: 1px solid $picker-border-color;
384 | border-bottom-right-radius: $picker-border-radius;
385 | border-bottom-left-radius: $picker-border-radius;
386 |
387 | .react-datepicker-row {
388 | margin-top: 0;
389 | width: 100%;
390 | min-height: 30px;
391 |
392 | &:not(:last-child) {
393 | border-bottom: 1px solid $picker-border-color;
394 | }
395 |
396 | &:last-child .react-datepicker-picker {
397 | &:first-child {
398 | border-bottom-left-radius: $picker-border-radius;
399 | }
400 |
401 | &:last-child {
402 | border-bottom-right-radius: $picker-border-radius;
403 | }
404 | }
405 |
406 | .react-datepicker-picker {
407 | color: $picker-color;
408 | background: $picker-background;
409 | cursor: pointer;
410 | text-decoration: none;
411 | font-weight: 400;
412 |
413 | &:not(:last-child) {
414 | border-right: 1px solid $picker-border-color;
415 | }
416 |
417 | &.day {
418 | min-height: 30px !important;
419 | }
420 |
421 | &.month {
422 | min-height: 65px !important;
423 | }
424 |
425 | &.year {
426 | min-height: 65px !important;
427 | }
428 |
429 | &:hover {
430 | color: $picker-color-hover;
431 | background: $picker-background-hover;
432 | }
433 |
434 | &.selected {
435 | color: $picker-selected-color;
436 | background: $picker-selected-background;
437 | font-weight: $font-weight-selected;
438 | }
439 |
440 | &.current {
441 | font-weight: 600;
442 | color: $picker-current-color;
443 | background: $picker-current-background;
444 |
445 | &:hover {
446 | color: $picker-current-color-hover;
447 | background: $picker-current-background-hover;
448 | }
449 | }
450 |
451 | &.selected.current {
452 | color: $picker-selected-color;
453 | background: $picker-selected-background;
454 | }
455 |
456 | &.disabled {
457 | cursor: default;
458 | color: $picker-disabled-color;
459 | background: $picker-disabled-background;
460 |
461 | &:hover {
462 | color: $picker-disabled-color;
463 | background: $picker-disabled-background;
464 | }
465 | }
466 | }
467 | }
468 | }
469 | }
470 | }
471 |
--------------------------------------------------------------------------------
/src/utils/DateUtils.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import range from 'lodash/range';
3 |
4 | export const daysInMonthCount = (month, year) => moment([year, month]).endOf('month').date();
5 |
6 | export const getArrayByBoundary = (start, end) => {
7 | return range(end - start).map(i => i + start);
8 | };
9 |
10 | export const getWeekdaysMin = () => {
11 | const offset = moment().localeData().firstDayOfWeek();
12 | const weekdaysMin = moment.weekdaysMin();
13 |
14 | range(offset).forEach(() => {
15 | const firstDay = weekdaysMin.shift();
16 | weekdaysMin.push(firstDay);
17 | });
18 | return weekdaysMin;
19 | };
20 |
21 | export const getVisibleDays = (month, year) => {
22 | const offset = moment([year, month]).startOf('month').weekday();
23 | const previousMonth = month === 0 ? 11 : (month - 1);
24 | const previousYear = month === 0 ? (year - 1) : year;
25 |
26 | const currentMonthLength = daysInMonthCount(month, year) + 1;
27 | const previousMonthLength = daysInMonthCount(previousMonth, previousYear) + 1; // We need the last number too
28 |
29 | const previous = getArrayByBoundary(previousMonthLength - offset, previousMonthLength);
30 | const current = getArrayByBoundary(1, currentMonthLength);
31 | const following = getArrayByBoundary(1, 43 - previous.length - current.length);
32 | return {
33 | startCurrent: previous.length,
34 | endCurrent: previous.length + current.length - 1,
35 | days: previous.concat(current).concat(following)
36 | };
37 | };
38 |
39 | export const getVisibleYears = (year) => {
40 | const startDecadeYear = parseInt(year / 10, 10) * 10;
41 | const endDecadeYear = startDecadeYear + 9;
42 | const previous = [startDecadeYear - 1];
43 | const current = getArrayByBoundary(startDecadeYear, endDecadeYear + 1);
44 | const following = [endDecadeYear + 1];
45 | return {
46 | startCurrent: previous.length,
47 | endCurrent: previous.length + current.length - 1,
48 | years: previous.concat(current).concat(following)
49 | };
50 | };
51 |
52 | export const evaluateDateProp = (props, propName, componentName) => {
53 | const dateProp = props[propName];
54 | if (dateProp && (typeof dateProp !== 'string' && !(dateProp instanceof Date) && !moment.isMoment(dateProp))) {
55 | return new Error(`${propName} validation failed in ${componentName}`);
56 | }
57 | };
58 |
59 | export const isInsideTheEnabledArea = (date, mode, minDate, maxDate) => {
60 | if (!minDate && !maxDate) {
61 | return true;
62 | }
63 |
64 | const minDateMoment = typeof minDate === 'string' ? moment(minDate, moment.ISO_8601, true) : moment(minDate);
65 | const maxDateMoment = typeof maxDate === 'string' ? moment(maxDate, moment.ISO_8601, true) : moment(maxDate);
66 |
67 | let format;
68 | switch (mode) {
69 | case 'day':
70 | format = 'YYYY/MM/DD';
71 | break;
72 |
73 | case 'month':
74 | format = 'YYYY/MM';
75 | break;
76 |
77 | case 'year':
78 | format = 'YYYY';
79 | break;
80 | }
81 |
82 | return (!minDate || date.format(format) >= minDateMoment.format(format)) && (!maxDate || date.format(format) <= maxDateMoment.format(format));
83 | };
84 |
--------------------------------------------------------------------------------
/src/utils/format.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | export default function format(Component) {
4 |
5 | Component.prototype.getDisplayFormat = function(props) {
6 | const { displayFormat, fixedMode, startMode } = (props || this.props);
7 | if (displayFormat) {
8 | return displayFormat;
9 | }
10 | if (fixedMode) {
11 | switch (startMode) {
12 | case 'day':
13 | return 'DD';
14 | case 'month':
15 | return 'MMMM';
16 | case 'year':
17 | return 'YYYY';
18 | }
19 | }
20 |
21 | return 'L';
22 | };
23 |
24 | Component.prototype.formatReturnedDate = function(date, props) {
25 | const { returnFormat } = (props || this.props);
26 | return date.format(returnFormat);
27 | };
28 |
29 | Component.prototype.formatDisplayedDate = function(date, props) {
30 | return date.format(this.getDisplayFormat(props));
31 | };
32 |
33 | Component.prototype.parsePropDateString = function(dateString, props) {
34 | const { returnFormat } = (props || this.props);
35 | if (!returnFormat) {
36 | return moment(dateString);
37 | } else {
38 | return moment(dateString, returnFormat, true);
39 | }
40 | };
41 |
42 | Component.prototype.parseInputDateString = function(dateString, props) {
43 | const format = this.getDisplayFormat(props);
44 | if (!format) {
45 | return moment(dateString);
46 | } else {
47 | return moment(dateString, format, true);
48 | }
49 | };
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export format from './format';
2 | export valueLink from './valueLink';
3 | export pure from './pure';
4 | export skinnable from './skinnable';
5 |
--------------------------------------------------------------------------------
/src/utils/model.js:
--------------------------------------------------------------------------------
1 | import t from 'tcomb';
2 | import moment from 'moment';
3 |
4 | export const Mode = t.enums.of(['day', 'month', 'year']);
5 |
6 | export const MomentDate = t.irreducible('MomentDate', x => moment.isMoment(x));
7 |
8 | export const Value = t.union([t.String, t.Date, MomentDate]);
9 |
--------------------------------------------------------------------------------
/src/utils/pure.js:
--------------------------------------------------------------------------------
1 | export default from 'revenge/lib/decorators/pure';
2 |
--------------------------------------------------------------------------------
/src/utils/skinnable.js:
--------------------------------------------------------------------------------
1 | export default from 'revenge/lib/decorators/skinnable';
2 |
--------------------------------------------------------------------------------
/src/utils/valueLink.js:
--------------------------------------------------------------------------------
1 | export default function format(Component) {
2 | Component.prototype.getValueLink = function(_props) {
3 | const props = _props || this.props;
4 | return props.valueLink || {
5 | value: props.value,
6 | requestChange: props.onChange
7 | };
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/src/yearpicker/YearPicker.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import t from 'tcomb';
3 | import { props } from 'tcomb-react';
4 | import View from 'react-flexview';
5 | import { pure, skinnable } from '../utils';
6 | import { MomentDate, Value, Mode } from '../utils/model';
7 | import YearPickerTop from './YearPickerTop';
8 | import YearPickerBody from './YearPickerBody';
9 |
10 | @pure
11 | @skinnable()
12 | @props({
13 | changeYear: t.Function,
14 | visibleDate: MomentDate,
15 | date: t.maybe(Value),
16 | minDate: t.maybe(Value),
17 | maxDate: t.maybe(Value),
18 | onChangeVisibleDate: t.Function,
19 | onSelectDate: t.Function,
20 | onChangeMode: t.Function,
21 | mode: Mode,
22 | fixedMode: t.maybe(t.Boolean),
23 | prevIconClassName: t.String,
24 | nextIconClassName: t.String
25 | })
26 | export default class YearPicker extends React.Component {
27 |
28 | onSelectDate = (date) => {
29 | const { fixedMode, onSelectDate, onChangeMode, onChangeVisibleDate } = this.props;
30 | if (fixedMode) {
31 | onSelectDate(date);
32 | } else {
33 | onChangeVisibleDate(date);
34 | onChangeMode(Mode('month'));
35 | }
36 | }
37 |
38 | getLocals({
39 | date, visibleDate, minDate,
40 | maxDate, changeYear, mode,
41 | prevIconClassName, nextIconClassName
42 | }) {
43 | return {
44 | yearPickerTopProps: {
45 | visibleDate,
46 | changeYear,
47 | prevIconClassName,
48 | nextIconClassName
49 | },
50 | yearPickerBodyProps: {
51 | date, visibleDate,
52 | minDate, maxDate,
53 | mode,
54 | onSelectDate: this.onSelectDate
55 | }
56 | };
57 | }
58 |
59 | template({ yearPickerTopProps, yearPickerBodyProps }) {
60 | return (
61 |
62 |
63 |
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/yearpicker/YearPickerBody.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import moment from 'moment';
3 | import t from 'tcomb';
4 | import { props } from 'tcomb-react';
5 | import range from 'lodash/range';
6 | import { pure, skinnable } from '../utils';
7 | import { Value, Mode, MomentDate } from '../utils/model';
8 | import { isInsideTheEnabledArea, getVisibleYears } from '../utils/DateUtils';
9 | import InvalidDate from '../InvalidDate';
10 | import Picker from '../Picker';
11 | import Row from '../Row';
12 |
13 | const COLUMNS = 4;
14 | const ROWS = 3;
15 |
16 | @pure
17 | @skinnable()
18 | @props({
19 | visibleDate: MomentDate,
20 | date: t.maybe(Value),
21 | minDate: t.maybe(Value),
22 | maxDate: t.maybe(Value),
23 | onSelectDate: t.Function,
24 | mode: Mode
25 | })
26 | export default class YearPickerBody extends React.Component {
27 |
28 | getLocals({ date, visibleDate, minDate, maxDate, onSelectDate, mode }) {
29 | if (!visibleDate.isValid()) {
30 | return ;
31 | }
32 | const year = visibleDate.year();
33 | const selectedYear = date ? date.year() : -1;
34 |
35 | const visibleYears = getVisibleYears(year);
36 | const pickers = visibleYears.years.map((_year, index) => {
37 | const date = moment([_year, 0, 1]);
38 | const isCurrent = index >= visibleYears.startCurrent && index <= visibleYears.endCurrent;
39 | return {
40 | date,
41 | onSelectDate,
42 | mode,
43 | isCurrent,
44 | isSelected: selectedYear === _year,
45 | isEnabled: isInsideTheEnabledArea(date, mode, minDate, maxDate),
46 | key: index
47 | };
48 | });
49 |
50 | return { pickers, mode };
51 | }
52 |
53 | templateYears = ({ pickers }) => pickers.map(p => )
54 |
55 | template({ pickers, mode }) {
56 | const years = this.templateYears({ pickers });
57 | const rows = range(ROWS).map(index =>
58 |
59 | );
60 |
61 | return (
62 |
63 | {rows}
64 |
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/yearpicker/YearPickerTop.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import t from 'tcomb';
3 | import { props } from 'tcomb-react';
4 | import { pure, skinnable } from '../utils';
5 | import { MomentDate } from '../utils/model';
6 | import PickerTop from '../PickerTop';
7 |
8 | @pure
9 | @skinnable()
10 | @props({
11 | visibleDate: MomentDate,
12 | changeYear: t.Function,
13 | prevIconClassName: t.String,
14 | nextIconClassName: t.String
15 | })
16 | export default class YearPickerTop extends React.Component {
17 |
18 | getYear = () => this.props.visibleDate.year()
19 |
20 | previousDate = () => this.props.changeYear(this.getYear() - 10)
21 |
22 | nextDate = () => this.props.changeYear(this.getYear() + 10)
23 |
24 | getLocals({ prevIconClassName, nextIconClassName }) {
25 | const year = this.getYear();
26 | const startDecadeYear = parseInt(year / 10, 10) * 10;
27 | const endDecadeYear = startDecadeYear + 9;
28 |
29 | return {
30 | prevIconClassName,
31 | nextIconClassName,
32 | fixed: true,
33 | previousDate: this.previousDate,
34 | nextDate: this.nextDate,
35 | value: `${startDecadeYear}-${endDecadeYear}`
36 | };
37 | }
38 |
39 | template(locals) {
40 | return ;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/styles.js:
--------------------------------------------------------------------------------
1 | require('./src/style.scss');
2 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "describe": true,
4 | "it": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | // called by mocha
2 | const requireDir = require('require-dir');
3 |
4 | require('babel-register')({
5 | ignore: /node_modules/,
6 | extensions: ['.js', '.jsx'],
7 | presets: ['es2015', 'react', 'stage-0'],
8 | plugins: [
9 | 'transform-decorators-legacy',
10 | 'lodash'
11 | ]
12 | });
13 |
14 | requireDir('./tests', {
15 | recurse: true
16 | });
17 |
--------------------------------------------------------------------------------
/test/tests/DatePickerInput-test.js:
--------------------------------------------------------------------------------
1 | import 'moment/locale/fr';
2 | import 'moment/locale/de';
3 |
4 | import React from 'react';
5 | import TestUtils from 'react-addons-test-utils';
6 | import expect from 'expect';
7 | import { DatePickerInput, DatePicker } from '../../src';
8 |
9 | describe('DatePickerInput', () => {
10 |
11 | it('presents the DatePicker when clicking on the calendar button', () => {
12 |
13 | const input = TestUtils.renderIntoDocument( {}} />);
14 |
15 | let datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
16 | expect(datePickers.length).toBe(0);
17 |
18 | const calendarButton = TestUtils.findRenderedDOMComponentWithClass(input, 'input-button');
19 | TestUtils.Simulate.click(calendarButton);
20 |
21 | datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
22 | expect(datePickers.length).toBe(1, 'DatePicker was not displayed after clicking on the calendar button');
23 |
24 | });
25 |
26 | it('presents the DatePicker when clicking on the input area', () => {
27 |
28 | const input = TestUtils.renderIntoDocument( {}} showOnInputClick />);
29 |
30 | let datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
31 | expect(datePickers.length).toBe(0);
32 |
33 | const datePickerInputArea = TestUtils.findRenderedDOMComponentWithTag(input, 'input');
34 | TestUtils.Simulate.click(datePickerInputArea);
35 |
36 | datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
37 | expect(datePickers.length).toBe(1, 'DatePicker was not displayed after clicking on the input area');
38 |
39 | });
40 |
41 | it('should pass the name prop down to the underlying input field', () => {
42 |
43 | const input = TestUtils.renderIntoDocument( {}} name='foobar' />);
44 |
45 | const datePickerInputArea = TestUtils.findRenderedDOMComponentWithTag(input, 'input');
46 | expect(datePickerInputArea.name).toBe('foobar', `Underlying input's 'name' prop is '${datePickerInputArea.name}' instead of 'foobar'`);
47 |
48 | });
49 |
50 | it('DatePicker should be floating above content', () => {
51 |
52 | const input = TestUtils.renderIntoDocument( {}} />);
53 |
54 | const input2 = TestUtils.renderIntoDocument( {}} />);
55 |
56 | const wrapper1 = TestUtils.findRenderedDOMComponentWithClass(input, 'react-datepicker-component');
57 |
58 | const calendarButton = TestUtils.findRenderedDOMComponentWithClass(input2, 'input-button');
59 | TestUtils.Simulate.click(calendarButton);
60 |
61 | const wrapper2 = TestUtils.findRenderedDOMComponentWithClass(input2, 'react-datepicker-component');
62 |
63 | const previousHeight = wrapper1.clientHeight;
64 | const newHeight = wrapper2.clientHeight;
65 | expect(newHeight).toBe(previousHeight, `datepicker component height is ${newHeight} instead of ${previousHeight}`);
66 |
67 | });
68 |
69 | it('DatePickers should have correct locales', () => {
70 |
71 | const inputFr = TestUtils.renderIntoDocument( {}} locale='fr' className='french' />);
72 | const inputDe = TestUtils.renderIntoDocument( {}} locale='de' className='german' />);
73 |
74 | const frenchWeekDays = TestUtils.scryRenderedDOMComponentsWithClass(inputFr, 'week-day');
75 | const germanWeekDays = TestUtils.scryRenderedDOMComponentsWithClass(inputDe, 'week-day');
76 |
77 | expect(frenchWeekDays[0].innerHTML).toBe('lu', 'First DatePicker is not in french');
78 | expect(germanWeekDays[0].innerHTML).toBe('Mo', 'Second DatePicker is not in german');
79 |
80 | });
81 |
82 |
83 | it('DatePicker should work with valueLink', () => {
84 |
85 | let date = new Date('2015-07-15');
86 |
87 | const onChange = (jsDate) => {
88 | date = jsDate;
89 | };
90 |
91 | const valueLink = {
92 | value: date,
93 | requestChange: onChange
94 | };
95 |
96 | const input = TestUtils.renderIntoDocument();
97 |
98 | const datePickerInputArea = TestUtils.findRenderedDOMComponentWithTag(input, 'input');
99 | TestUtils.Simulate.click(datePickerInputArea);
100 | expect(datePickerInputArea.value).toBe('15.07.2015', 'initial value is wrong');
101 |
102 | const pickerButton = TestUtils.scryRenderedDOMComponentsWithClass(input, 'react-datepicker-picker')[0];
103 | TestUtils.Simulate.click(pickerButton);
104 | expect(datePickerInputArea.value).toBe('29.06.2015', 'displayed value didn\'t change correctly');
105 |
106 | const formattedDate = [date.getDate(), date.getMonth() + 1, date.getFullYear()].join('.');
107 | expect(formattedDate).toBe('29.6.2015', 'stored value didn\'t change correctly');
108 |
109 | });
110 |
111 | it('DatePicker should close on select date', () => {
112 |
113 | const input = TestUtils.renderIntoDocument( {}} showOnInputClick autoClose />);
114 |
115 | let datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
116 | expect(datePickers.length).toBe(0);
117 |
118 | const datePickerInputArea = TestUtils.findRenderedDOMComponentWithTag(input, 'input');
119 | TestUtils.Simulate.click(datePickerInputArea);
120 |
121 | datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
122 | expect(datePickers.length).toBe(1);
123 |
124 | const pickerButton = TestUtils.scryRenderedDOMComponentsWithClass(input, 'react-datepicker-picker')[0];
125 | TestUtils.Simulate.click(pickerButton);
126 |
127 | datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
128 | expect(datePickers.length).toBe(0, 'DatePicker didn\'t close correctly');
129 |
130 | });
131 |
132 | it('DatePicker should close on enter key event on input', () => {
133 |
134 | const input = TestUtils.renderIntoDocument( {}} showOnInputClick />);
135 |
136 | let datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
137 | expect(datePickers.length).toBe(0);
138 |
139 | const datePickerInputArea = TestUtils.findRenderedDOMComponentWithTag(input, 'input');
140 | TestUtils.Simulate.click(datePickerInputArea);
141 |
142 | datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
143 | expect(datePickers.length).toBe(1);
144 |
145 | TestUtils.Simulate.keyUp(datePickerInputArea, { keyCode: 13 });
146 |
147 | datePickers = TestUtils.scryRenderedComponentsWithType(input, DatePicker);
148 | expect(datePickers.length).toBe(0, 'DatePicker didn\'t close correctly');
149 |
150 | });
151 |
152 | });
153 |
--------------------------------------------------------------------------------
/webpack.config.build.babel.js:
--------------------------------------------------------------------------------
1 | import ExtractTextPlugin from 'extract-text-webpack-plugin';
2 |
3 | import path from 'path';
4 |
5 | const paths = {
6 | SRC: path.resolve(__dirname, 'src'),
7 | ENTRY: path.resolve(__dirname, 'styles.js'),
8 | BUILD: path.resolve(__dirname, 'lib'),
9 | NODE_MODULES: path.resolve(__dirname, 'node_modules')
10 | };
11 |
12 | export default {
13 |
14 | output: {
15 | path: paths.BUILD,
16 | filename: 'bundle.js'
17 | },
18 |
19 | entry: paths.ENTRY,
20 |
21 | resolve: {
22 | root: [paths.NODE_MODULES]
23 | },
24 |
25 | module: {
26 | loaders: [{
27 | test: /\.jsx?$/,
28 | loaders: ['babel'],
29 | include: [paths.SRC]
30 | }, {
31 | test: /\.scss$/,
32 | loader: ExtractTextPlugin.extract('style', 'css!resolve-url!sass?sourceMap')
33 | }]
34 | },
35 |
36 | plugins: [
37 | new ExtractTextPlugin('style', 'style.css')
38 | ]
39 | };
40 |
--------------------------------------------------------------------------------