cozy-ui 115.0.2 → 115.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [115.1.0](https://github.com/cozy/cozy-ui/compare/v115.0.2...v115.1.0) (2025-01-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * **DatePicker:** Add DatePicker component ([1c5fa03](https://github.com/cozy/cozy-ui/commit/1c5fa03))
7
+
1
8
  ## [115.0.2](https://github.com/cozy/cozy-ui/compare/v115.0.1...v115.0.2) (2024-12-20)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "115.0.2",
3
+ "version": "115.1.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -156,8 +156,10 @@
156
156
  },
157
157
  "dependencies": {
158
158
  "@babel/runtime": "^7.3.4",
159
+ "@date-io/date-fns": "1",
159
160
  "@material-ui/core": "4.12.3",
160
161
  "@material-ui/lab": "^4.0.0-alpha.61",
162
+ "@material-ui/pickers": "3.3.11",
161
163
  "@popperjs/core": "^2.4.4",
162
164
  "chart.js": "3.7.1",
163
165
  "classnames": "^2.2.5",
@@ -0,0 +1,208 @@
1
+ `Breakpoints` & `I18n` Providers are needed for this component
2
+
3
+ ### Usage
4
+
5
+ ### Date Picker
6
+
7
+ ```jsx
8
+ import React, { useState } from 'react';
9
+ import DemoProvider from 'cozy-ui/docs/components/DemoProvider';
10
+ import Variants from 'cozy-ui/docs/components/Variants';
11
+ import DatePicker from 'cozy-ui/transpiled/react/DatePicker';
12
+
13
+ const placeholder = isTesting() ? "01/01/2025" : undefined;
14
+
15
+ const initialVariants = [
16
+ { enableKeyboard: false, clearable: false, showTodayButton: false, disableFuture: false, disablePast: false, disableToolbar: false, autoOk: false, animateYearScrolling: false, disabled: false, readOnly: false }
17
+ ];
18
+
19
+ // Example
20
+ const [selectedDate, setSelectedDate] = useState(null);
21
+ <DemoProvider>
22
+ <Variants initialVariants={initialVariants}>
23
+ {variant => (
24
+ <DatePicker
25
+ onChange={setSelectedDate}
26
+ value={selectedDate}
27
+ placeholder={placeholder}
28
+ {...variant}
29
+ />
30
+ )
31
+ }
32
+ </Variants>
33
+ </DemoProvider>
34
+ ```
35
+
36
+ ### DateTime Picker
37
+
38
+ ```jsx
39
+ import React, { useState } from 'react';
40
+ import DemoProvider from 'cozy-ui/docs/components/DemoProvider';
41
+ import Variants from 'cozy-ui/docs/components/Variants';
42
+ import DatePicker from 'cozy-ui/transpiled/react/DatePicker';
43
+
44
+ const placeholder = isTesting() ? "01/01/2025" : undefined;
45
+
46
+ const initialVariants = [
47
+ { enableKeyboard: false, clearable: false, showTodayButton: false, disableFuture: false, disablePast: false, disableToolbar: false, autoOk: false, animateYearScrolling: false, disabled: false, readOnly: false, ampm: false }
48
+ ];
49
+
50
+ // Example
51
+ const [selectedDate, setSelectedDate] = useState(null);
52
+ <DemoProvider>
53
+ <Variants initialVariants={initialVariants}>
54
+ {variant => (
55
+ <DatePicker
56
+ mode="dateTime"
57
+ onChange={setSelectedDate}
58
+ value={selectedDate}
59
+ placeholder={placeholder}
60
+ {...variant}
61
+ />
62
+ )
63
+ }
64
+ </Variants>
65
+ </DemoProvider>
66
+ ```
67
+
68
+ ### Time Picker
69
+
70
+ ```jsx
71
+ import React, { useState } from 'react';
72
+ import DemoProvider from 'cozy-ui/docs/components/DemoProvider';
73
+ import Variants from 'cozy-ui/docs/components/Variants';
74
+ import DatePicker from 'cozy-ui/transpiled/react/DatePicker';
75
+
76
+ const placeholder = isTesting() ? "01/01/2025" : undefined;
77
+
78
+ const initialVariants = [
79
+ { enableKeyboard: false, clearable: false, showTodayButton: false, disableFuture: false, disablePast: false, disableToolbar: false, autoOk: false, animateYearScrolling: false, disabled: false, readOnly: false, ampm: false }
80
+ ];
81
+
82
+ // Example
83
+ const [selectedDate, setSelectedDate] = useState(null);
84
+ <DemoProvider>
85
+ <Variants initialVariants={initialVariants}>
86
+ {variant => (
87
+ <DatePicker
88
+ mode="time"
89
+ onChange={setSelectedDate}
90
+ value={selectedDate}
91
+ placeholder={placeholder}
92
+ {...variant}
93
+ />
94
+ )
95
+ }
96
+ </Variants>
97
+ </DemoProvider>
98
+ ```
99
+
100
+ ### API
101
+
102
+ ### Inheritance
103
+
104
+ Any prop not recognized by the pickers and their sub-components are passed down to material-ui [TextField](https://v4.mui.com/api/text-field/#textfield-api) component.
105
+
106
+ `DateIOType` — date object type of your linked date-io adapter (Date-fns, DayJS, etc.)
107
+
108
+ ### Props
109
+
110
+ #### **Date Picker**
111
+
112
+ |name|type|default|description|
113
+ |----|----|-------|-----------|
114
+ |onChange|(date: DateIOType) => void||onChange callback|
115
+ |value|Date||Picker value|
116
+ |allowKeyboardControl|Boolean|true|Enables keyboard listener for moving between days in calendar|
117
+ |autoOk|Boolean|false|Auto accept date on selection|
118
+ |disabled|Boolean||Disable picker and text field|
119
+ |disableFuture|Boolean|false|Disable future dates|
120
+ |disablePast|boolean|false|Disable past dates|
121
+ |disableToolbar|boolean|false|Hide toolbar and show only date/time views|
122
+ |emptyLabel|string|""|Message displaying in text field, if null passed (doesn't work in keyboard mode)|
123
+ |format|string||Format string|
124
+ |initialFocusedDate|Date||Date that will be initially highlighted if null was passed|
125
+ |inputVariant|"standard" \| "outlined" \| "filled"||Pass material-ui text field variant down, bypass internal variant prop|
126
+ |invalidDateMessage|ReactNode|"Invalid Date Format"|Message, appearing when date cannot be parsed|
127
+ |invalidLabel|string|"unknown"|Message displaying in text field if date is invalid (doesn't work in keyboard mode)|
128
+ |labelFunc|(date: DateIOType, invalidLabel: string) => string||Dynamic formatter of text field value|
129
+ |leftArrowButtonProps|Partial<IconButtonProps>||Props to pass to left arrow button|
130
+ |leftArrowIcon|ReactNode||Left arrow icon|
131
+ |loadingIndicator|Element||Custom loading indicator|
132
+ |maxDate|Date|Date(2100-01-01)|Max selectable date|
133
+ |maxDateMessage|ReactNode|"Date should not be after maximal date"|Error message, shown if date is more then maximal date|
134
+ |minDate|Date|Date(1900-01-01)|Min selectable date|
135
+ |minDateMessage|ReactNode|"Date should not be before minimal date"|Error message, shown if date is less then minimal date|
136
+ |onAccept|(date: DateIOType) => void||Callback fired when date is accepted|
137
+ |onClose|() => void||On close callback|
138
+ |onError|(error: ReactNode, value: DateIOType) => void||Callback fired when new error should be displayed (!! This is a side effect. Be careful if you want to rerender the component)|
139
+ |onMonthChange|(date: DateIOType) => void \| Promise<void>||Callback firing on month change. Return promise to render spinner till it will not be resolved|
140
+ |onOpen|Date||On open callback|
141
+ |onYearChange|Date||Callback firing on year change|
142
+ |open|boolean||Controlled picker open state|
143
+ |openTo|"date" \| "year" \| "month"||First view to show in DatePicker|
144
+ |orientation|"portrait" \| "landscape"|"portrait"|Force rendering in particular orientation|
145
+ |PopoverProps|Partial<PopoverProps>||Popover props passed to material-ui Popover (with variant="inline")|
146
+ |readOnly|boolean||Make picker read only|
147
+ |renderDay|(day: DateIOType, selectedDate: DateIOType, dayInCurrentMonth: boolean, dayComponent: Element) => Element||Custom renderer for day|
148
+ |rightArrowButtonProps|Partial<IconButtonProps>||Props to pass to right arrow button|
149
+ |rightArrowIcon|ReactNode||Right arrow icon|
150
+ |shouldDisableDate|(day: DateIOType) => boolean||Disable specific date|
151
+ |strictCompareDates|boolean|false|Compare dates by the exact timestamp, instead of start/end of date|
152
+ |TextFieldComponent|FunctionComponent<TextFieldProps>||Override input component|
153
+ |ToolbarComponent|FunctionComponent<ToolbarComponentProps>||Component that will replace default toolbar renderer|
154
+ |variant|"dialog" \| "inline" \| "static"|'dialog'|Picker container option|
155
+ |views|Array<"year" \| "date" \| "month">||Array of views to show|
156
+ ___
157
+ <br>
158
+
159
+ #### **Keyboard Date Picker**
160
+
161
+ Additional props for keyboard date picker
162
+
163
+ |name|type|default|description|
164
+ |----|----|-------|-----------|
165
+ |onChange|(date: DateIOType, isValid: boolean) => void||Keyboard onChange callback, that returns the value of the input when it changes and if it is a valid Date|
166
+ |InputAdornmentProps|Partial<InputAdornmentProps>||Props to pass to keyboard input adornment|
167
+ |inputValue|string||String value for controlling value with pure input string. Overrides value prop|
168
+ |KeyboardButtonProps|Partial<IconButtonProps>||Props to pass to keyboard adornment button|
169
+ |keyboardIcon|ReactNode||Icon displaying for open picker button|
170
+ |mask|string||Custom mask. Can be used to override generate from format. (e.g. __/__/____ __:__)|
171
+ |maskChar|string|"_"|Char string that will be replaced with number (for "_" mask will be "__/__/____")|
172
+ |refuse|RegExp|/\[^\\d\]+/gi|Refuse values regexp|
173
+ |rifmFormatter|(str: string) => string||Custom formatter to be passed into Rifm component|
174
+ ___
175
+ <br>
176
+
177
+ #### **DateTime & Time Picker**
178
+
179
+ Additional props for time date picker
180
+
181
+ |name|type|default|description|
182
+ |----|----|-------|-----------|
183
+ |ampm|boolean||12h/24h view for hour selection clock|
184
+ ___
185
+ <br>
186
+
187
+ ### Modal Wrapper
188
+
189
+ |name|type|default|description|
190
+ |----|----|-------|-----------|
191
+ |cancelLabel|ReactNode|"Cancel"|"CANCEL" label message|
192
+ |clearable|boolean||Show clear action in picker dialog|
193
+ |clearLabel|ReactNode|"Clear"|"Clear" label message|
194
+ |DialogProps|Partial<MuiDialogProps>||Props to be passed directly to material-ui Dialog|
195
+ |okLabel|ReactNode|"OK"|"OK" label message|
196
+ |showTodayButton|boolean||If true today button will be displayed. **Note:** that clear button has higher priority|
197
+ |todayLabel|ReactNode|"TODAY"|"TODAY" label message|
198
+ ___
199
+ <br>
200
+
201
+ ### Additional props
202
+
203
+ |name|type|default|description|
204
+ |----|----|-------|-----------|
205
+ |isValid|() => boolean||Function that returns if the value of the input is a valid Date|
206
+ |enableKeyboard|boolean||Enable the keyboard date picker|
207
+ |mode|"date" \| "time" \| "dateTime"|"date"|Picker mode|
208
+ ___
@@ -0,0 +1,16 @@
1
+ export const makeFormat = ({ ampm, mode, lang }) => {
2
+ switch (mode) {
3
+ case 'date':
4
+ return lang === 'fr' ? 'dd/LL/yyyy' : 'LL/dd/yyyy'
5
+ case 'time':
6
+ return lang === 'fr' ? 'HH:mm' : ampm ? 'HH:mm a' : 'HH:mm'
7
+ case 'dateTime':
8
+ return lang === 'fr'
9
+ ? 'dd/LL/yyyy HH:mm'
10
+ : ampm
11
+ ? 'LL/dd/yyyy HH:mm a'
12
+ : 'LL/dd/yyyy HH:mm'
13
+ default:
14
+ return lang === 'fr' ? 'dd/LL/yyyy' : ampm ? 'LL/dd/yyyy a' : 'LL/dd/yyyy'
15
+ }
16
+ }
@@ -0,0 +1,71 @@
1
+ import { makeFormat } from './helpers'
2
+
3
+ describe('makeFormat', () => {
4
+ describe('When lang is "fr"', () => {
5
+ it('should return the right format for default mode', () => {
6
+ const format = makeFormat({ lang: 'fr' })
7
+ expect(format).toBe('dd/LL/yyyy')
8
+ })
9
+
10
+ it('should return the right format for time mode', () => {
11
+ const format = makeFormat({ mode: 'time', lang: 'fr' })
12
+ expect(format).toBe('HH:mm')
13
+ })
14
+
15
+ it('should return the right format for dateTime mode', () => {
16
+ const format = makeFormat({ mode: 'dateTime', lang: 'fr' })
17
+ expect(format).toBe('dd/LL/yyyy HH:mm')
18
+ })
19
+
20
+ describe('When ampm is true', () => {
21
+ it('should return the right format for default mode', () => {
22
+ const format = makeFormat({ ampm: true, lang: 'fr' })
23
+ expect(format).toBe('dd/LL/yyyy')
24
+ })
25
+
26
+ it('should return the right format for time mode', () => {
27
+ const format = makeFormat({ mode: 'time', ampm: true, lang: 'fr' })
28
+ expect(format).toBe('HH:mm')
29
+ })
30
+
31
+ it('should return the right format for dateTime mode', () => {
32
+ const format = makeFormat({ mode: 'dateTime', ampm: true, lang: 'fr' })
33
+ expect(format).toBe('dd/LL/yyyy HH:mm')
34
+ })
35
+ })
36
+ })
37
+
38
+ describe('When lang is not "fr"', () => {
39
+ it('should return the right format for default mode', () => {
40
+ const format = makeFormat({ lang: 'en', ampm: false })
41
+ expect(format).toBe('LL/dd/yyyy')
42
+ })
43
+
44
+ it('should return the right format for time mode', () => {
45
+ const format = makeFormat({ mode: 'time', lang: 'en' })
46
+ expect(format).toBe('HH:mm')
47
+ })
48
+
49
+ it('should return the right format for dateTime mode', () => {
50
+ const format = makeFormat({ mode: 'dateTime', lang: 'en' })
51
+ expect(format).toBe('LL/dd/yyyy HH:mm')
52
+ })
53
+
54
+ describe('When ampm is true', () => {
55
+ it('should return the right format for default mode', () => {
56
+ const format = makeFormat({ ampm: true, lang: 'en' })
57
+ expect(format).toBe('LL/dd/yyyy a')
58
+ })
59
+
60
+ it('should return the right format for time mode', () => {
61
+ const format = makeFormat({ mode: 'time', ampm: true, lang: 'en' })
62
+ expect(format).toBe('HH:mm a')
63
+ })
64
+
65
+ it('should return the right format for dateTime mode', () => {
66
+ const format = makeFormat({ mode: 'dateTime', ampm: true, lang: 'en' })
67
+ expect(format).toBe('LL/dd/yyyy HH:mm a')
68
+ })
69
+ })
70
+ })
71
+ })
@@ -0,0 +1,289 @@
1
+ import DateFnsUtils from '@date-io/date-fns'
2
+ import {
3
+ MuiPickersUtilsProvider,
4
+ KeyboardDatePicker as MuiKeyboardDatePicker,
5
+ KeyboardTimePicker as MuiKeyboardTimePicker,
6
+ KeyboardDateTimePicker as MuiKeyboardDateTimePicker,
7
+ DatePicker as MuiDatePicker,
8
+ TimePicker as MuiTimePicker,
9
+ DateTimePicker as MuiDateTimePicker
10
+ } from '@material-ui/pickers'
11
+ import cx from 'classnames'
12
+ import formatFNS from 'date-fns/format'
13
+ import isBefore from 'date-fns/isBefore'
14
+ import LocaleEN from 'date-fns/locale/en-US'
15
+ import LocaleFR from 'date-fns/locale/fr'
16
+ import subDays from 'date-fns/subDays'
17
+ import PropTypes from 'prop-types'
18
+ import React, { forwardRef, useState } from 'react'
19
+
20
+ import { makeFormat } from './helpers'
21
+ import withOwnLocales from './locales/withOwnLocales'
22
+ import useBreakpoints from '../providers/Breakpoints'
23
+ import { useI18n } from '../providers/I18n'
24
+ import { makeStyles } from '../styles'
25
+
26
+ const localesFNS = {
27
+ fr: LocaleFR,
28
+ en: LocaleEN
29
+ }
30
+
31
+ const useStyles = makeStyles(() => ({
32
+ overrides: {
33
+ width: '100%',
34
+ height: isDesktop => (isDesktop ? '5rem' : 'inherit'),
35
+ MuiOutlinedInput: {
36
+ '&:focused': {
37
+ notchedOutline: {
38
+ borderColor: 'var(--primaryColor)'
39
+ }
40
+ }
41
+ }
42
+ }
43
+ }))
44
+
45
+ const DatePicker = forwardRef(
46
+ (
47
+ {
48
+ className,
49
+ label,
50
+ clearable = false,
51
+ value = null,
52
+ placeholder,
53
+ onFocus,
54
+ onBlur,
55
+ onChange,
56
+ minDate,
57
+ minDateLabelError,
58
+ format,
59
+ cancelLabel,
60
+ clearLabel,
61
+ okLabel,
62
+ todayLabel,
63
+ showTodayButton = false,
64
+ helperText,
65
+ errorLabel,
66
+ inputVariant = 'outlined',
67
+ inputProps,
68
+ KeyboardButtonProps,
69
+ enableKeyboard,
70
+ mode = 'date',
71
+ ampm,
72
+ ...props
73
+ },
74
+ ref
75
+ ) => {
76
+ const [error, setError] = useState(null)
77
+ const [isFocused, setIsFocused] = useState(false)
78
+
79
+ const { isDesktop } = useBreakpoints()
80
+ const classes = useStyles(isDesktop)
81
+ const { t, lang } = useI18n()
82
+
83
+ const isError = !isFocused && Boolean(error)
84
+ const _helperText = isError ? error : helperText ?? null
85
+ const _format = format || makeFormat({ ampm, mode, lang })
86
+ const _ampm = ampm ?? !(mode === 'time' || mode === 'dateTime')
87
+ const _placeholder = placeholder ?? formatFNS(new Date(), _format)
88
+ const _clearLabel = clearLabel || t('clear')
89
+ const _todayLabel = todayLabel || t('today')
90
+ const _cancelLabel = cancelLabel || t('cancel')
91
+ const _okLabel = okLabel || t('ok')
92
+ const _minDateLabelError = minDateLabelError
93
+ ? minDateLabelError
94
+ : minDate
95
+ ? t('minDateLabelError', {
96
+ date: formatFNS(minDate, _format)
97
+ })
98
+ : null
99
+
100
+ const _KeyboardButtonProps = {
101
+ 'aria-label': label,
102
+ ...KeyboardButtonProps
103
+ }
104
+ const _inputProps = { inputMode: 'numeric', ...inputProps }
105
+
106
+ const handleChange = val => {
107
+ if (val?.toString() !== 'Invalid Date') {
108
+ if (minDate && isBefore(val, subDays(minDate, 1))) {
109
+ setError(_minDateLabelError)
110
+ onChange(val, false)
111
+ return
112
+ }
113
+ setError(null)
114
+ onChange(val, true)
115
+ } else if (val === '') {
116
+ setError(null)
117
+ onChange(null, true)
118
+ } else {
119
+ setError(errorLabel ?? t('invalidDate'))
120
+ onChange(val, false)
121
+ }
122
+ }
123
+
124
+ const handleBlur = () => {
125
+ setIsFocused(false)
126
+ onFocus?.(true)
127
+ onBlur?.(false)
128
+ }
129
+ const handleFocus = () => {
130
+ setIsFocused(true)
131
+ onFocus?.(false)
132
+ onBlur?.(true)
133
+ }
134
+
135
+ let DatePickerComponent
136
+ switch (mode) {
137
+ case 'date':
138
+ DatePickerComponent = enableKeyboard
139
+ ? MuiKeyboardDatePicker
140
+ : MuiDatePicker
141
+ break
142
+ case 'time':
143
+ DatePickerComponent = enableKeyboard
144
+ ? MuiKeyboardTimePicker
145
+ : MuiTimePicker
146
+ break
147
+ case 'dateTime':
148
+ DatePickerComponent = enableKeyboard
149
+ ? MuiKeyboardDateTimePicker
150
+ : MuiDateTimePicker
151
+ break
152
+ }
153
+
154
+ return (
155
+ <MuiPickersUtilsProvider utils={DateFnsUtils} locale={localesFNS[lang]}>
156
+ <DatePickerComponent
157
+ inputRef={ref}
158
+ label={label}
159
+ placeholder={_placeholder}
160
+ value={value}
161
+ helperText={_helperText}
162
+ className={cx(classes.overrides, className)}
163
+ minDate={minDate}
164
+ ampm={_ampm}
165
+ format={_format}
166
+ onChange={handleChange}
167
+ error={isError}
168
+ onBlur={handleBlur}
169
+ onFocus={handleFocus}
170
+ inputVariant={inputVariant}
171
+ inputProps={_inputProps}
172
+ KeyboardButtonProps={_KeyboardButtonProps}
173
+ // Modal start
174
+ showTodayButton={showTodayButton}
175
+ todayLabel={_todayLabel}
176
+ clearable={clearable}
177
+ clearLabel={_clearLabel}
178
+ cancelLabel={_cancelLabel}
179
+ okLabel={_okLabel}
180
+ // Modal end
181
+ {...props}
182
+ />
183
+ </MuiPickersUtilsProvider>
184
+ )
185
+ }
186
+ )
187
+
188
+ DatePicker.displayName = 'DatePicker'
189
+
190
+ DatePicker.prototype = {
191
+ /*
192
+ Classname to override the input style
193
+ */
194
+ className: PropTypes.string,
195
+ /*
196
+ Label of the input
197
+ */
198
+ label: PropTypes.string,
199
+ /*
200
+ Value of th input. If set by default with a Date, isValidDate will be false if the value is empty (KeyboardDatePicker behavior)
201
+ */
202
+ value: PropTypes.string.isRequired,
203
+ /*
204
+ Placeholder of the input
205
+ */
206
+ placeholder: PropTypes.string,
207
+ /*
208
+ Function that returns the value of the input when it changes and if it is a valid Date
209
+ */
210
+ onChange: PropTypes.func.isRequired,
211
+ /*
212
+ Function that returns if the input is blured
213
+ */
214
+ onBlur: PropTypes.func,
215
+ /*
216
+ Function that returns if the input is focused
217
+ */
218
+ onFocus: PropTypes.func,
219
+ /*
220
+ Helper text to display when the input is in error
221
+ */
222
+ helperText: PropTypes.string,
223
+ /*
224
+ Min date selectable with the date picker (exclusive)
225
+ */
226
+ minDate: PropTypes.instanceOf(Date),
227
+ /*
228
+ Error message when the min date is not respected
229
+ */
230
+ minDateLabelError: PropTypes.string,
231
+ /*
232
+ Format of the date
233
+ */
234
+ format: PropTypes.string,
235
+ /*
236
+ Date picker cancellation label
237
+ */
238
+ cancelLabel: PropTypes.string,
239
+ /*
240
+ Show today button
241
+ */
242
+ showTodayButton: PropTypes.bool,
243
+ /*
244
+ Date picker today label
245
+ */
246
+ todayLabel: PropTypes.string,
247
+ /*
248
+ Date picker ok label
249
+ */
250
+ okLabel: PropTypes.string,
251
+ /*
252
+ Show the clear button
253
+ */
254
+ clearable: PropTypes.bool,
255
+ /*
256
+ Date picker clear label
257
+ */
258
+ clearLabel: PropTypes.string,
259
+ /*
260
+ Error message when the date is invalid
261
+ */
262
+ errorLabel: PropTypes.string,
263
+ /*
264
+ Variant of the input
265
+ */
266
+ inputVariant: PropTypes.string,
267
+ /*
268
+ Props to override the input
269
+ */
270
+ inputProps: PropTypes.object,
271
+ /*
272
+ Props to override the keyboard button
273
+ */
274
+ KeyboardButtonProps: PropTypes.object,
275
+ /*
276
+ Enable the keyboard date picker
277
+ */
278
+ enableKeyboard: PropTypes.bool,
279
+ /*
280
+ Mode of the date picker. Default is "date"
281
+ */
282
+ mode: PropTypes.oneOf(['date', 'time', 'dateTime']),
283
+ /*
284
+ Enable the AM/PM selector
285
+ */
286
+ ampm: PropTypes.bool
287
+ }
288
+
289
+ export default withOwnLocales(React.memo(DatePicker))
@@ -0,0 +1,8 @@
1
+ {
2
+ "cancel": "Cancel",
3
+ "clear": "Clear",
4
+ "invalidDate": "Invalid date",
5
+ "ok": "Ok",
6
+ "today": "Today",
7
+ "minDateLabelError": "Date should not be before minimal date (%{date})"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "cancel": "Annuler",
3
+ "clear": "Supprimer",
4
+ "invalidDate": "Date invalide",
5
+ "ok": "Ok",
6
+ "today": "Aujourd'hui",
7
+ "minDateLabelError": "La date ne doit pas être antérieure à la date minimale (%{date})"
8
+ }
@@ -0,0 +1,10 @@
1
+ import en from './en.json'
2
+ import fr from './fr.json'
3
+ import withOnlyLocales from '../../providers/I18n/withOnlyLocales'
4
+
5
+ export const locales = {
6
+ en,
7
+ fr
8
+ }
9
+
10
+ export default withOnlyLocales(locales)
package/react/index.js CHANGED
@@ -139,3 +139,4 @@ export { default as Modal } from './Modal'
139
139
  export { ListSkeleton, ListItemSkeleton } from './Skeletons'
140
140
  export { default as ActionsBar } from './ActionsBar'
141
141
  export { default as Markdown } from './Markdown'
142
+ export { default as DatePicker } from './DatePicker'
@@ -0,0 +1,19 @@
1
+ export var makeFormat = function makeFormat(_ref) {
2
+ var ampm = _ref.ampm,
3
+ mode = _ref.mode,
4
+ lang = _ref.lang;
5
+
6
+ switch (mode) {
7
+ case 'date':
8
+ return lang === 'fr' ? 'dd/LL/yyyy' : 'LL/dd/yyyy';
9
+
10
+ case 'time':
11
+ return lang === 'fr' ? 'HH:mm' : ampm ? 'HH:mm a' : 'HH:mm';
12
+
13
+ case 'dateTime':
14
+ return lang === 'fr' ? 'dd/LL/yyyy HH:mm' : ampm ? 'LL/dd/yyyy HH:mm a' : 'LL/dd/yyyy HH:mm';
15
+
16
+ default:
17
+ return lang === 'fr' ? 'dd/LL/yyyy' : ampm ? 'LL/dd/yyyy a' : 'LL/dd/yyyy';
18
+ }
19
+ };
@@ -0,0 +1,331 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
4
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
5
+ var _excluded = ["className", "label", "clearable", "value", "placeholder", "onFocus", "onBlur", "onChange", "minDate", "minDateLabelError", "format", "cancelLabel", "clearLabel", "okLabel", "todayLabel", "showTodayButton", "helperText", "errorLabel", "inputVariant", "inputProps", "KeyboardButtonProps", "enableKeyboard", "mode", "ampm"];
6
+
7
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
8
+
9
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
10
+
11
+ import DateFnsUtils from '@date-io/date-fns';
12
+ import { MuiPickersUtilsProvider, KeyboardDatePicker as MuiKeyboardDatePicker, KeyboardTimePicker as MuiKeyboardTimePicker, KeyboardDateTimePicker as MuiKeyboardDateTimePicker, DatePicker as MuiDatePicker, TimePicker as MuiTimePicker, DateTimePicker as MuiDateTimePicker } from '@material-ui/pickers';
13
+ import cx from 'classnames';
14
+ import formatFNS from 'date-fns/format';
15
+ import isBefore from 'date-fns/isBefore';
16
+ import LocaleEN from 'date-fns/locale/en-US';
17
+ import LocaleFR from 'date-fns/locale/fr';
18
+ import subDays from 'date-fns/subDays';
19
+ import PropTypes from 'prop-types';
20
+ import React, { forwardRef, useState } from 'react';
21
+ import { makeFormat } from "cozy-ui/transpiled/react/DatePicker/helpers";
22
+ import withOwnLocales from "cozy-ui/transpiled/react/DatePicker/locales/withOwnLocales";
23
+ import useBreakpoints from "cozy-ui/transpiled/react/providers/Breakpoints";
24
+ import { useI18n } from "cozy-ui/transpiled/react/providers/I18n";
25
+ import { makeStyles } from "cozy-ui/transpiled/react/styles";
26
+ var localesFNS = {
27
+ fr: LocaleFR,
28
+ en: LocaleEN
29
+ };
30
+ var useStyles = makeStyles(function () {
31
+ return {
32
+ overrides: {
33
+ width: '100%',
34
+ height: function height(isDesktop) {
35
+ return isDesktop ? '5rem' : 'inherit';
36
+ },
37
+ MuiOutlinedInput: {
38
+ '&:focused': {
39
+ notchedOutline: {
40
+ borderColor: 'var(--primaryColor)'
41
+ }
42
+ }
43
+ }
44
+ }
45
+ };
46
+ });
47
+ var DatePicker = /*#__PURE__*/forwardRef(function (_ref, ref) {
48
+ var className = _ref.className,
49
+ label = _ref.label,
50
+ _ref$clearable = _ref.clearable,
51
+ clearable = _ref$clearable === void 0 ? false : _ref$clearable,
52
+ _ref$value = _ref.value,
53
+ value = _ref$value === void 0 ? null : _ref$value,
54
+ placeholder = _ref.placeholder,
55
+ onFocus = _ref.onFocus,
56
+ onBlur = _ref.onBlur,
57
+ onChange = _ref.onChange,
58
+ minDate = _ref.minDate,
59
+ minDateLabelError = _ref.minDateLabelError,
60
+ format = _ref.format,
61
+ cancelLabel = _ref.cancelLabel,
62
+ clearLabel = _ref.clearLabel,
63
+ okLabel = _ref.okLabel,
64
+ todayLabel = _ref.todayLabel,
65
+ _ref$showTodayButton = _ref.showTodayButton,
66
+ showTodayButton = _ref$showTodayButton === void 0 ? false : _ref$showTodayButton,
67
+ helperText = _ref.helperText,
68
+ errorLabel = _ref.errorLabel,
69
+ _ref$inputVariant = _ref.inputVariant,
70
+ inputVariant = _ref$inputVariant === void 0 ? 'outlined' : _ref$inputVariant,
71
+ inputProps = _ref.inputProps,
72
+ KeyboardButtonProps = _ref.KeyboardButtonProps,
73
+ enableKeyboard = _ref.enableKeyboard,
74
+ _ref$mode = _ref.mode,
75
+ mode = _ref$mode === void 0 ? 'date' : _ref$mode,
76
+ ampm = _ref.ampm,
77
+ props = _objectWithoutProperties(_ref, _excluded);
78
+
79
+ var _useState = useState(null),
80
+ _useState2 = _slicedToArray(_useState, 2),
81
+ error = _useState2[0],
82
+ setError = _useState2[1];
83
+
84
+ var _useState3 = useState(false),
85
+ _useState4 = _slicedToArray(_useState3, 2),
86
+ isFocused = _useState4[0],
87
+ setIsFocused = _useState4[1];
88
+
89
+ var _useBreakpoints = useBreakpoints(),
90
+ isDesktop = _useBreakpoints.isDesktop;
91
+
92
+ var classes = useStyles(isDesktop);
93
+
94
+ var _useI18n = useI18n(),
95
+ t = _useI18n.t,
96
+ lang = _useI18n.lang;
97
+
98
+ var isError = !isFocused && Boolean(error);
99
+
100
+ var _helperText = isError ? error : helperText !== null && helperText !== void 0 ? helperText : null;
101
+
102
+ var _format = format || makeFormat({
103
+ ampm: ampm,
104
+ mode: mode,
105
+ lang: lang
106
+ });
107
+
108
+ var _ampm = ampm !== null && ampm !== void 0 ? ampm : !(mode === 'time' || mode === 'dateTime');
109
+
110
+ var _placeholder = placeholder !== null && placeholder !== void 0 ? placeholder : formatFNS(new Date(), _format);
111
+
112
+ var _clearLabel = clearLabel || t('clear');
113
+
114
+ var _todayLabel = todayLabel || t('today');
115
+
116
+ var _cancelLabel = cancelLabel || t('cancel');
117
+
118
+ var _okLabel = okLabel || t('ok');
119
+
120
+ var _minDateLabelError = minDateLabelError ? minDateLabelError : minDate ? t('minDateLabelError', {
121
+ date: formatFNS(minDate, _format)
122
+ }) : null;
123
+
124
+ var _KeyboardButtonProps = _objectSpread({
125
+ 'aria-label': label
126
+ }, KeyboardButtonProps);
127
+
128
+ var _inputProps = _objectSpread({
129
+ inputMode: 'numeric'
130
+ }, inputProps);
131
+
132
+ var handleChange = function handleChange(val) {
133
+ if ((val === null || val === void 0 ? void 0 : val.toString()) !== 'Invalid Date') {
134
+ if (minDate && isBefore(val, subDays(minDate, 1))) {
135
+ setError(_minDateLabelError);
136
+ onChange(val, false);
137
+ return;
138
+ }
139
+
140
+ setError(null);
141
+ onChange(val, true);
142
+ } else if (val === '') {
143
+ setError(null);
144
+ onChange(null, true);
145
+ } else {
146
+ setError(errorLabel !== null && errorLabel !== void 0 ? errorLabel : t('invalidDate'));
147
+ onChange(val, false);
148
+ }
149
+ };
150
+
151
+ var handleBlur = function handleBlur() {
152
+ setIsFocused(false);
153
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(true);
154
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(false);
155
+ };
156
+
157
+ var handleFocus = function handleFocus() {
158
+ setIsFocused(true);
159
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(false);
160
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(true);
161
+ };
162
+
163
+ var DatePickerComponent;
164
+
165
+ switch (mode) {
166
+ case 'date':
167
+ DatePickerComponent = enableKeyboard ? MuiKeyboardDatePicker : MuiDatePicker;
168
+ break;
169
+
170
+ case 'time':
171
+ DatePickerComponent = enableKeyboard ? MuiKeyboardTimePicker : MuiTimePicker;
172
+ break;
173
+
174
+ case 'dateTime':
175
+ DatePickerComponent = enableKeyboard ? MuiKeyboardDateTimePicker : MuiDateTimePicker;
176
+ break;
177
+ }
178
+
179
+ return /*#__PURE__*/React.createElement(MuiPickersUtilsProvider, {
180
+ utils: DateFnsUtils,
181
+ locale: localesFNS[lang]
182
+ }, /*#__PURE__*/React.createElement(DatePickerComponent, _extends({
183
+ inputRef: ref,
184
+ label: label,
185
+ placeholder: _placeholder,
186
+ value: value,
187
+ helperText: _helperText,
188
+ className: cx(classes.overrides, className),
189
+ minDate: minDate,
190
+ ampm: _ampm,
191
+ format: _format,
192
+ onChange: handleChange,
193
+ error: isError,
194
+ onBlur: handleBlur,
195
+ onFocus: handleFocus,
196
+ inputVariant: inputVariant,
197
+ inputProps: _inputProps,
198
+ KeyboardButtonProps: _KeyboardButtonProps // Modal start
199
+ ,
200
+ showTodayButton: showTodayButton,
201
+ todayLabel: _todayLabel,
202
+ clearable: clearable,
203
+ clearLabel: _clearLabel,
204
+ cancelLabel: _cancelLabel,
205
+ okLabel: _okLabel // Modal end
206
+
207
+ }, props)));
208
+ });
209
+ DatePicker.displayName = 'DatePicker';
210
+ DatePicker.prototype = {
211
+ /*
212
+ Classname to override the input style
213
+ */
214
+ className: PropTypes.string,
215
+
216
+ /*
217
+ Label of the input
218
+ */
219
+ label: PropTypes.string,
220
+
221
+ /*
222
+ Value of th input. If set by default with a Date, isValidDate will be false if the value is empty (KeyboardDatePicker behavior)
223
+ */
224
+ value: PropTypes.string.isRequired,
225
+
226
+ /*
227
+ Placeholder of the input
228
+ */
229
+ placeholder: PropTypes.string,
230
+
231
+ /*
232
+ Function that returns the value of the input when it changes and if it is a valid Date
233
+ */
234
+ onChange: PropTypes.func.isRequired,
235
+
236
+ /*
237
+ Function that returns if the input is blured
238
+ */
239
+ onBlur: PropTypes.func,
240
+
241
+ /*
242
+ Function that returns if the input is focused
243
+ */
244
+ onFocus: PropTypes.func,
245
+
246
+ /*
247
+ Helper text to display when the input is in error
248
+ */
249
+ helperText: PropTypes.string,
250
+
251
+ /*
252
+ Min date selectable with the date picker (exclusive)
253
+ */
254
+ minDate: PropTypes.instanceOf(Date),
255
+
256
+ /*
257
+ Error message when the min date is not respected
258
+ */
259
+ minDateLabelError: PropTypes.string,
260
+
261
+ /*
262
+ Format of the date
263
+ */
264
+ format: PropTypes.string,
265
+
266
+ /*
267
+ Date picker cancellation label
268
+ */
269
+ cancelLabel: PropTypes.string,
270
+
271
+ /*
272
+ Show today button
273
+ */
274
+ showTodayButton: PropTypes.bool,
275
+
276
+ /*
277
+ Date picker today label
278
+ */
279
+ todayLabel: PropTypes.string,
280
+
281
+ /*
282
+ Date picker ok label
283
+ */
284
+ okLabel: PropTypes.string,
285
+
286
+ /*
287
+ Show the clear button
288
+ */
289
+ clearable: PropTypes.bool,
290
+
291
+ /*
292
+ Date picker clear label
293
+ */
294
+ clearLabel: PropTypes.string,
295
+
296
+ /*
297
+ Error message when the date is invalid
298
+ */
299
+ errorLabel: PropTypes.string,
300
+
301
+ /*
302
+ Variant of the input
303
+ */
304
+ inputVariant: PropTypes.string,
305
+
306
+ /*
307
+ Props to override the input
308
+ */
309
+ inputProps: PropTypes.object,
310
+
311
+ /*
312
+ Props to override the keyboard button
313
+ */
314
+ KeyboardButtonProps: PropTypes.object,
315
+
316
+ /*
317
+ Enable the keyboard date picker
318
+ */
319
+ enableKeyboard: PropTypes.bool,
320
+
321
+ /*
322
+ Mode of the date picker. Default is "date"
323
+ */
324
+ mode: PropTypes.oneOf(['date', 'time', 'dateTime']),
325
+
326
+ /*
327
+ Enable the AM/PM selector
328
+ */
329
+ ampm: PropTypes.bool
330
+ };
331
+ export default withOwnLocales( /*#__PURE__*/React.memo(DatePicker));
@@ -0,0 +1,22 @@
1
+ var en = {
2
+ cancel: "Cancel",
3
+ clear: "Clear",
4
+ invalidDate: "Invalid date",
5
+ ok: "Ok",
6
+ today: "Today",
7
+ minDateLabelError: "Date should not be before minimal date (%{date})"
8
+ };
9
+ var fr = {
10
+ cancel: "Annuler",
11
+ clear: "Supprimer",
12
+ invalidDate: "Date invalide",
13
+ ok: "Ok",
14
+ today: "Aujourd'hui",
15
+ minDateLabelError: "La date ne doit pas \xEAtre ant\xE9rieure \xE0 la date minimale (%{date})"
16
+ };
17
+ import withOnlyLocales from "cozy-ui/transpiled/react/providers/I18n/withOnlyLocales";
18
+ export var locales = {
19
+ en: en,
20
+ fr: fr
21
+ };
22
+ export default withOnlyLocales(locales);
@@ -111,4 +111,5 @@ export { default as AlertProvider, useAlert } from './providers/Alert';
111
111
  export { default as Modal } from './Modal';
112
112
  export { ListSkeleton, ListItemSkeleton } from './Skeletons';
113
113
  export { default as ActionsBar } from './ActionsBar';
114
- export { default as Markdown } from './Markdown';
114
+ export { default as Markdown } from './Markdown';
115
+ export { default as DatePicker } from './DatePicker';