@contentful/field-editor-date 2.0.9 → 2.0.10-canary.2
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/dist/cjs/DatepickerInput.js +2 -10
- package/dist/cjs/TimepickerInput.js +17 -32
- package/dist/cjs/utils/data.spec.js +18 -24
- package/dist/cjs/utils/date.js +69 -38
- package/dist/esm/DatepickerInput.js +2 -5
- package/dist/esm/TimepickerInput.js +17 -27
- package/dist/esm/utils/data.spec.js +18 -19
- package/dist/esm/utils/date.js +69 -33
- package/dist/types/DatepickerInput.d.ts +2 -3
- package/dist/types/types.d.ts +1 -2
- package/dist/types/utils/date.d.ts +4 -1
- package/package.json +3 -3
|
@@ -11,12 +11,6 @@ Object.defineProperty(exports, "DatepickerInput", {
|
|
|
11
11
|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
12
12
|
const _f36components = require("@contentful/f36-components");
|
|
13
13
|
const _css = require("@emotion/css");
|
|
14
|
-
const _moment = /*#__PURE__*/ _interop_require_default(require("moment"));
|
|
15
|
-
function _interop_require_default(obj) {
|
|
16
|
-
return obj && obj.__esModule ? obj : {
|
|
17
|
-
default: obj
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
14
|
function _getRequireWildcardCache(nodeInterop) {
|
|
21
15
|
if (typeof WeakMap !== "function") return null;
|
|
22
16
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -75,14 +69,12 @@ const DatepickerInput = (props)=>{
|
|
|
75
69
|
toDate
|
|
76
70
|
];
|
|
77
71
|
}, []);
|
|
78
|
-
const
|
|
79
|
-
const selectedDate = dateObj ? new Date(dateObj.years, dateObj.months, dateObj.date) : undefined;
|
|
72
|
+
const selectedDate = props.value;
|
|
80
73
|
return /*#__PURE__*/ _react.default.createElement(_f36components.Datepicker, {
|
|
81
74
|
className: styles.root,
|
|
82
75
|
selected: selectedDate,
|
|
83
76
|
onSelect: (day)=>{
|
|
84
|
-
|
|
85
|
-
props.onChange(momentDay);
|
|
77
|
+
props.onChange(day ?? undefined);
|
|
86
78
|
},
|
|
87
79
|
inputProps: {
|
|
88
80
|
isDisabled: props.disabled,
|
|
@@ -11,12 +11,7 @@ Object.defineProperty(exports, "TimepickerInput", {
|
|
|
11
11
|
const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
|
|
12
12
|
const _f36components = require("@contentful/f36-components");
|
|
13
13
|
const _css = require("@emotion/css");
|
|
14
|
-
const
|
|
15
|
-
function _interop_require_default(obj) {
|
|
16
|
-
return obj && obj.__esModule ? obj : {
|
|
17
|
-
default: obj
|
|
18
|
-
};
|
|
19
|
-
}
|
|
14
|
+
const _datefns = require("date-fns");
|
|
20
15
|
function _getRequireWildcardCache(nodeInterop) {
|
|
21
16
|
if (typeof WeakMap !== "function") return null;
|
|
22
17
|
var cacheBabelInterop = new WeakMap();
|
|
@@ -60,41 +55,31 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
60
55
|
}
|
|
61
56
|
const validInputFormats = [
|
|
62
57
|
'hh:mm a',
|
|
63
|
-
'hh:mm A',
|
|
64
58
|
'h:mm a',
|
|
65
|
-
'h:mm A',
|
|
66
59
|
'hh:mm',
|
|
67
|
-
'k:mm',
|
|
68
60
|
'kk:mm',
|
|
61
|
+
'k:mm',
|
|
69
62
|
'h a',
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
'
|
|
73
|
-
'HH'
|
|
63
|
+
'HH',
|
|
64
|
+
'H:mm',
|
|
65
|
+
'h'
|
|
74
66
|
];
|
|
67
|
+
const REF_DATE = new Date(2000, 0, 1);
|
|
75
68
|
function parseRawInput(raw) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (date.isValid()) {
|
|
80
|
-
time = date;
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
69
|
+
for (const fmt of validInputFormats){
|
|
70
|
+
const parsed = (0, _datefns.parse)(raw, fmt, REF_DATE);
|
|
71
|
+
if ((0, _datefns.isValid)(parsed)) return parsed;
|
|
83
72
|
}
|
|
84
|
-
return
|
|
73
|
+
return null;
|
|
85
74
|
}
|
|
86
|
-
const getDefaultTime = ()=>
|
|
87
|
-
|
|
88
|
-
};
|
|
89
|
-
const formatToString = (uses12hClock, value)=>{
|
|
90
|
-
return uses12hClock ? value.format('hh:mm A') : value.format('HH:mm');
|
|
91
|
-
};
|
|
75
|
+
const getDefaultTime = ()=>(0, _datefns.parse)('12:00 AM', 'hh:mm a', REF_DATE);
|
|
76
|
+
const formatToString = (uses12hClock, value)=>(0, _datefns.format)(value, uses12hClock ? 'hh:mm a' : 'HH:mm');
|
|
92
77
|
const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm = 'AM', onChange })=>{
|
|
93
78
|
const [selectedTime, setSelectedTime] = (0, _react.useState)(()=>{
|
|
94
79
|
return formatToString(uses12hClock, getDefaultTime());
|
|
95
80
|
});
|
|
96
81
|
(0, _react.useEffect)(()=>{
|
|
97
|
-
setSelectedTime(formatToString(uses12hClock, (0,
|
|
82
|
+
setSelectedTime(formatToString(uses12hClock, (0, _datefns.parse)(`${time} ${ampm}`, 'hh:mm a', REF_DATE)));
|
|
98
83
|
}, [
|
|
99
84
|
time,
|
|
100
85
|
ampm,
|
|
@@ -106,8 +91,8 @@ const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm = 'AM',
|
|
|
106
91
|
const parsedTime = parseRawInput(raw);
|
|
107
92
|
if (parsedTime) {
|
|
108
93
|
onChange({
|
|
109
|
-
time:
|
|
110
|
-
ampm:
|
|
94
|
+
time: (0, _datefns.format)(parsedTime, 'hh:mm'),
|
|
95
|
+
ampm: (0, _datefns.format)(parsedTime, 'a').toUpperCase()
|
|
111
96
|
});
|
|
112
97
|
}
|
|
113
98
|
}, [
|
|
@@ -122,8 +107,8 @@ const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm = 'AM',
|
|
|
122
107
|
const value = parsedTime ?? getDefaultTime();
|
|
123
108
|
setSelectedTime(formatToString(uses12hClock, value));
|
|
124
109
|
onChange({
|
|
125
|
-
time:
|
|
126
|
-
ampm:
|
|
110
|
+
time: (0, _datefns.format)(value, 'hh:mm'),
|
|
111
|
+
ampm: (0, _datefns.format)(value, 'a').toUpperCase()
|
|
127
112
|
});
|
|
128
113
|
}, [
|
|
129
114
|
selectedTime,
|
|
@@ -2,19 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
const _moment = /*#__PURE__*/ _interop_require_default(require("moment"));
|
|
6
5
|
const _date = require("./date");
|
|
7
|
-
function _interop_require_default(obj) {
|
|
8
|
-
return obj && obj.__esModule ? obj : {
|
|
9
|
-
default: obj
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
6
|
describe('date utils', ()=>{
|
|
13
7
|
describe('buildFieldValue', ()=>{
|
|
14
8
|
it('should work properly', ()=>{
|
|
15
9
|
expect((0, _date.buildFieldValue)({
|
|
16
10
|
data: {
|
|
17
|
-
date:
|
|
11
|
+
date: new Date('2018-02-02'),
|
|
18
12
|
time: '05:00',
|
|
19
13
|
ampm: 'PM',
|
|
20
14
|
utcOffset: '+03:00'
|
|
@@ -27,7 +21,7 @@ describe('date utils', ()=>{
|
|
|
27
21
|
});
|
|
28
22
|
expect((0, _date.buildFieldValue)({
|
|
29
23
|
data: {
|
|
30
|
-
date:
|
|
24
|
+
date: new Date('2015-01-14'),
|
|
31
25
|
time: '05:00',
|
|
32
26
|
ampm: 'AM',
|
|
33
27
|
utcOffset: '-05:00'
|
|
@@ -40,7 +34,7 @@ describe('date utils', ()=>{
|
|
|
40
34
|
});
|
|
41
35
|
expect((0, _date.buildFieldValue)({
|
|
42
36
|
data: {
|
|
43
|
-
date:
|
|
37
|
+
date: new Date('2015-01-14'),
|
|
44
38
|
time: '17:00',
|
|
45
39
|
ampm: 'PM',
|
|
46
40
|
utcOffset: '-05:00'
|
|
@@ -55,7 +49,7 @@ describe('date utils', ()=>{
|
|
|
55
49
|
it('returns date-only format when usesTime and usesTimezone are false', ()=>{
|
|
56
50
|
expect((0, _date.buildFieldValue)({
|
|
57
51
|
data: {
|
|
58
|
-
date:
|
|
52
|
+
date: new Date('2020-06-15'),
|
|
59
53
|
time: '10:30',
|
|
60
54
|
ampm: 'AM',
|
|
61
55
|
utcOffset: '+00:00'
|
|
@@ -70,7 +64,7 @@ describe('date utils', ()=>{
|
|
|
70
64
|
it('returns datetime without timezone when usesTime=true, usesTimezone=false', ()=>{
|
|
71
65
|
expect((0, _date.buildFieldValue)({
|
|
72
66
|
data: {
|
|
73
|
-
date:
|
|
67
|
+
date: new Date('2020-06-15'),
|
|
74
68
|
time: '14:30',
|
|
75
69
|
ampm: 'PM',
|
|
76
70
|
utcOffset: '+00:00'
|
|
@@ -99,7 +93,7 @@ describe('date utils', ()=>{
|
|
|
99
93
|
it('handles midnight (12:00 AM) correctly', ()=>{
|
|
100
94
|
expect((0, _date.buildFieldValue)({
|
|
101
95
|
data: {
|
|
102
|
-
date:
|
|
96
|
+
date: new Date('2021-03-01'),
|
|
103
97
|
time: '12:00',
|
|
104
98
|
ampm: 'AM',
|
|
105
99
|
utcOffset: '+00:00'
|
|
@@ -114,7 +108,7 @@ describe('date utils', ()=>{
|
|
|
114
108
|
it('handles noon (12:00 PM) correctly', ()=>{
|
|
115
109
|
expect((0, _date.buildFieldValue)({
|
|
116
110
|
data: {
|
|
117
|
-
date:
|
|
111
|
+
date: new Date('2021-03-01'),
|
|
118
112
|
time: '12:00',
|
|
119
113
|
ampm: 'PM',
|
|
120
114
|
utcOffset: '+00:00'
|
|
@@ -129,7 +123,7 @@ describe('date utils', ()=>{
|
|
|
129
123
|
it('preserves half-hour offset +05:30 (India)', ()=>{
|
|
130
124
|
expect((0, _date.buildFieldValue)({
|
|
131
125
|
data: {
|
|
132
|
-
date:
|
|
126
|
+
date: new Date('2023-08-15'),
|
|
133
127
|
time: '10:00',
|
|
134
128
|
ampm: 'AM',
|
|
135
129
|
utcOffset: '+05:30'
|
|
@@ -144,7 +138,7 @@ describe('date utils', ()=>{
|
|
|
144
138
|
it('preserves negative half-hour offset -09:30', ()=>{
|
|
145
139
|
expect((0, _date.buildFieldValue)({
|
|
146
140
|
data: {
|
|
147
|
-
date:
|
|
141
|
+
date: new Date('2023-08-15'),
|
|
148
142
|
time: '03:30',
|
|
149
143
|
ampm: 'AM',
|
|
150
144
|
utcOffset: '-09:30'
|
|
@@ -159,7 +153,7 @@ describe('date utils', ()=>{
|
|
|
159
153
|
it('preserves quarter-hour offset +05:45 (Nepal)', ()=>{
|
|
160
154
|
expect((0, _date.buildFieldValue)({
|
|
161
155
|
data: {
|
|
162
|
-
date:
|
|
156
|
+
date: new Date('2023-08-15'),
|
|
163
157
|
time: '05:45',
|
|
164
158
|
ampm: 'AM',
|
|
165
159
|
utcOffset: '+05:45'
|
|
@@ -174,7 +168,7 @@ describe('date utils', ()=>{
|
|
|
174
168
|
it('preserves UTC +00:00 offset', ()=>{
|
|
175
169
|
expect((0, _date.buildFieldValue)({
|
|
176
170
|
data: {
|
|
177
|
-
date:
|
|
171
|
+
date: new Date('2023-01-01'),
|
|
178
172
|
time: '00:00',
|
|
179
173
|
ampm: 'AM',
|
|
180
174
|
utcOffset: '+00:00'
|
|
@@ -189,7 +183,7 @@ describe('date utils', ()=>{
|
|
|
189
183
|
it('preserves far-west offset -12:00', ()=>{
|
|
190
184
|
expect((0, _date.buildFieldValue)({
|
|
191
185
|
data: {
|
|
192
|
-
date:
|
|
186
|
+
date: new Date('2023-01-01'),
|
|
193
187
|
time: '23:59',
|
|
194
188
|
ampm: 'PM',
|
|
195
189
|
utcOffset: '-12:00'
|
|
@@ -204,7 +198,7 @@ describe('date utils', ()=>{
|
|
|
204
198
|
it('preserves far-east offset +14:00', ()=>{
|
|
205
199
|
expect((0, _date.buildFieldValue)({
|
|
206
200
|
data: {
|
|
207
|
-
date:
|
|
201
|
+
date: new Date('2023-01-01'),
|
|
208
202
|
time: '01:00',
|
|
209
203
|
ampm: 'AM',
|
|
210
204
|
utcOffset: '+14:00'
|
|
@@ -219,7 +213,7 @@ describe('date utils', ()=>{
|
|
|
219
213
|
it('11:59 PM → 23:59', ()=>{
|
|
220
214
|
expect((0, _date.buildFieldValue)({
|
|
221
215
|
data: {
|
|
222
|
-
date:
|
|
216
|
+
date: new Date('2023-06-01'),
|
|
223
217
|
time: '11:59',
|
|
224
218
|
ampm: 'PM',
|
|
225
219
|
utcOffset: '+00:00'
|
|
@@ -234,7 +228,7 @@ describe('date utils', ()=>{
|
|
|
234
228
|
it('01:00 AM → 01:00 (no shift)', ()=>{
|
|
235
229
|
expect((0, _date.buildFieldValue)({
|
|
236
230
|
data: {
|
|
237
|
-
date:
|
|
231
|
+
date: new Date('2023-06-01'),
|
|
238
232
|
time: '01:00',
|
|
239
233
|
ampm: 'AM',
|
|
240
234
|
utcOffset: '+00:00'
|
|
@@ -290,12 +284,12 @@ describe('date utils', ()=>{
|
|
|
290
284
|
});
|
|
291
285
|
expect(result.date).toBeUndefined();
|
|
292
286
|
});
|
|
293
|
-
it('parses a date-only string
|
|
287
|
+
it('parses a date-only string', ()=>{
|
|
294
288
|
const result = (0, _date.userInputFromDatetime)({
|
|
295
289
|
value: '2022-09-16',
|
|
296
290
|
uses12hClock: false
|
|
297
291
|
});
|
|
298
|
-
expect(result.utcOffset).
|
|
292
|
+
expect(result.utcOffset).toBe('+00:00');
|
|
299
293
|
});
|
|
300
294
|
it('preserves raw time and does not shift by system timezone (positive offset)', ()=>{
|
|
301
295
|
const result = (0, _date.userInputFromDatetime)({
|
|
@@ -315,7 +309,7 @@ describe('date utils', ()=>{
|
|
|
315
309
|
expect(result.ampm).toBe('AM');
|
|
316
310
|
expect(result.utcOffset).toBe('-05:30');
|
|
317
311
|
});
|
|
318
|
-
it('handles UTC "Z" suffix —
|
|
312
|
+
it('handles UTC "Z" suffix — normalizes offset to +00:00', ()=>{
|
|
319
313
|
const result = (0, _date.userInputFromDatetime)({
|
|
320
314
|
value: '2021-06-01T12:00Z',
|
|
321
315
|
uses12hClock: false
|
package/dist/cjs/utils/date.js
CHANGED
|
@@ -22,32 +22,37 @@ _export(exports, {
|
|
|
22
22
|
return userInputFromDatetime;
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
const
|
|
26
|
-
function _interop_require_default(obj) {
|
|
27
|
-
return obj && obj.__esModule ? obj : {
|
|
28
|
-
default: obj
|
|
29
|
-
};
|
|
30
|
-
}
|
|
25
|
+
const _datefns = require("date-fns");
|
|
31
26
|
const ZONE_RX = /(Z|[+-]\d{2}[:+]?\d{2})$/;
|
|
32
|
-
function
|
|
33
|
-
return (0,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
function startOfTodayOffset() {
|
|
28
|
+
return (0, _datefns.format)(new Date(), 'xxx');
|
|
29
|
+
}
|
|
30
|
+
function parseUtcOffset(datetimeString) {
|
|
31
|
+
const match = datetimeString.match(ZONE_RX);
|
|
32
|
+
if (!match) return '+00:00';
|
|
33
|
+
return match[1] === 'Z' ? '+00:00' : match[1];
|
|
37
34
|
}
|
|
38
|
-
function
|
|
35
|
+
function fieldValueToDate(datetimeString) {
|
|
39
36
|
if (!datetimeString) {
|
|
40
37
|
return null;
|
|
41
38
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
datetime.utcOffset(datetimeString);
|
|
45
|
-
}
|
|
46
|
-
return datetime;
|
|
39
|
+
const date = (0, _datefns.parseISO)(datetimeString);
|
|
40
|
+
return (0, _datefns.isValid)(date) ? date : null;
|
|
47
41
|
}
|
|
48
42
|
function timeFromUserInput(input) {
|
|
49
43
|
const timeInput = input.time || '00:00';
|
|
50
|
-
|
|
44
|
+
const parsed = (0, _datefns.parse)(timeInput, 'HH:mm', new Date(0));
|
|
45
|
+
let hours = (0, _datefns.getHours)(parsed);
|
|
46
|
+
const minutes = (0, _datefns.getMinutes)(parsed);
|
|
47
|
+
if (input.ampm === 'PM' && hours < 12) {
|
|
48
|
+
hours += 12;
|
|
49
|
+
} else if (input.ampm === 'AM' && hours === 12) {
|
|
50
|
+
hours = 0;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
hours,
|
|
54
|
+
minutes
|
|
55
|
+
};
|
|
51
56
|
}
|
|
52
57
|
function datetimeFromUserInput(input) {
|
|
53
58
|
if (!input.date) {
|
|
@@ -55,12 +60,14 @@ function datetimeFromUserInput(input) {
|
|
|
55
60
|
valid: null
|
|
56
61
|
};
|
|
57
62
|
}
|
|
58
|
-
const
|
|
59
|
-
const date =
|
|
60
|
-
hours
|
|
61
|
-
minutes
|
|
63
|
+
const { hours, minutes } = timeFromUserInput(input);
|
|
64
|
+
const date = (0, _datefns.set)(input.date, {
|
|
65
|
+
hours,
|
|
66
|
+
minutes,
|
|
67
|
+
seconds: 0,
|
|
68
|
+
milliseconds: 0
|
|
62
69
|
});
|
|
63
|
-
if (
|
|
70
|
+
if ((0, _datefns.isValid)(date)) {
|
|
64
71
|
return {
|
|
65
72
|
valid: date
|
|
66
73
|
};
|
|
@@ -78,34 +85,58 @@ function buildFieldValue({ data, usesTime, usesTimezone }) {
|
|
|
78
85
|
invalid: true
|
|
79
86
|
};
|
|
80
87
|
}
|
|
81
|
-
|
|
88
|
+
if (!date.valid) {
|
|
89
|
+
return {
|
|
90
|
+
valid: null,
|
|
91
|
+
invalid: false
|
|
92
|
+
};
|
|
93
|
+
}
|
|
82
94
|
if (usesTimezone) {
|
|
83
|
-
|
|
95
|
+
return {
|
|
96
|
+
valid: (0, _datefns.format)(date.valid, "yyyy-MM-dd'T'HH:mm") + data.utcOffset,
|
|
97
|
+
invalid: false
|
|
98
|
+
};
|
|
84
99
|
} else if (usesTime) {
|
|
85
|
-
|
|
100
|
+
return {
|
|
101
|
+
valid: (0, _datefns.format)(date.valid, "yyyy-MM-dd'T'HH:mm"),
|
|
102
|
+
invalid: false
|
|
103
|
+
};
|
|
86
104
|
} else {
|
|
87
|
-
|
|
105
|
+
return {
|
|
106
|
+
valid: (0, _datefns.format)(date.valid, 'yyyy-MM-dd'),
|
|
107
|
+
invalid: false
|
|
108
|
+
};
|
|
88
109
|
}
|
|
89
|
-
return {
|
|
90
|
-
valid: date?.valid ? date.valid.format(format) : null,
|
|
91
|
-
invalid: false
|
|
92
|
-
};
|
|
93
110
|
}
|
|
94
111
|
function getDefaultAMPM() {
|
|
95
112
|
return 'AM';
|
|
96
113
|
}
|
|
97
114
|
function getDefaultUtcOffset() {
|
|
98
|
-
return
|
|
115
|
+
return startOfTodayOffset();
|
|
116
|
+
}
|
|
117
|
+
function extractTimeFromIso(value) {
|
|
118
|
+
const match = value.match(/T(\d{2}:\d{2})/);
|
|
119
|
+
return match ? match[1] : undefined;
|
|
99
120
|
}
|
|
100
121
|
function userInputFromDatetime({ value, uses12hClock }) {
|
|
101
|
-
const datetime =
|
|
102
|
-
if (datetime) {
|
|
103
|
-
const
|
|
122
|
+
const datetime = fieldValueToDate(value);
|
|
123
|
+
if (datetime && value) {
|
|
124
|
+
const rawTime = extractTimeFromIso(value);
|
|
125
|
+
const timeDate = rawTime ? (0, _datefns.parse)(rawTime, 'HH:mm', new Date(0)) : datetime;
|
|
126
|
+
const hours = (0, _datefns.getHours)(timeDate);
|
|
127
|
+
const ampm = hours < 12 ? 'AM' : 'PM';
|
|
128
|
+
let time;
|
|
129
|
+
if (uses12hClock) {
|
|
130
|
+
const h12 = hours % 12 || 12;
|
|
131
|
+
time = `${String(h12).padStart(2, '0')}:${String((0, _datefns.getMinutes)(timeDate)).padStart(2, '0')}`;
|
|
132
|
+
} else {
|
|
133
|
+
time = rawTime ?? (0, _datefns.format)(datetime, 'HH:mm');
|
|
134
|
+
}
|
|
104
135
|
return {
|
|
105
136
|
date: datetime,
|
|
106
|
-
time
|
|
107
|
-
ampm
|
|
108
|
-
utcOffset:
|
|
137
|
+
time,
|
|
138
|
+
ampm,
|
|
139
|
+
utcOffset: parseUtcOffset(value)
|
|
109
140
|
};
|
|
110
141
|
} else {
|
|
111
142
|
return {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { Datepicker } from '@contentful/f36-components';
|
|
3
3
|
import { css } from '@emotion/css';
|
|
4
|
-
import moment from 'moment';
|
|
5
4
|
const YEARS_INTO_FUTURE = 100;
|
|
6
5
|
const MIN_YEAR = '1000';
|
|
7
6
|
const styles = {
|
|
@@ -19,14 +18,12 @@ export const DatepickerInput = (props)=>{
|
|
|
19
18
|
toDate
|
|
20
19
|
];
|
|
21
20
|
}, []);
|
|
22
|
-
const
|
|
23
|
-
const selectedDate = dateObj ? new Date(dateObj.years, dateObj.months, dateObj.date) : undefined;
|
|
21
|
+
const selectedDate = props.value;
|
|
24
22
|
return /*#__PURE__*/ React.createElement(Datepicker, {
|
|
25
23
|
className: styles.root,
|
|
26
24
|
selected: selectedDate,
|
|
27
25
|
onSelect: (day)=>{
|
|
28
|
-
|
|
29
|
-
props.onChange(momentDay);
|
|
26
|
+
props.onChange(day ?? undefined);
|
|
30
27
|
},
|
|
31
28
|
inputProps: {
|
|
32
29
|
isDisabled: props.disabled,
|
|
@@ -1,44 +1,34 @@
|
|
|
1
1
|
import React, { useState, useCallback, useEffect } from 'react';
|
|
2
2
|
import { TextInput, Flex } from '@contentful/f36-components';
|
|
3
3
|
import { css } from '@emotion/css';
|
|
4
|
-
import
|
|
4
|
+
import { parse, format, isValid } from 'date-fns';
|
|
5
5
|
const validInputFormats = [
|
|
6
6
|
'hh:mm a',
|
|
7
|
-
'hh:mm A',
|
|
8
7
|
'h:mm a',
|
|
9
|
-
'h:mm A',
|
|
10
8
|
'hh:mm',
|
|
11
|
-
'k:mm',
|
|
12
9
|
'kk:mm',
|
|
10
|
+
'k:mm',
|
|
13
11
|
'h a',
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'HH'
|
|
12
|
+
'HH',
|
|
13
|
+
'H:mm',
|
|
14
|
+
'h'
|
|
18
15
|
];
|
|
16
|
+
const REF_DATE = new Date(2000, 0, 1);
|
|
19
17
|
function parseRawInput(raw) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (date.isValid()) {
|
|
24
|
-
time = date;
|
|
25
|
-
break;
|
|
26
|
-
}
|
|
18
|
+
for (const fmt of validInputFormats){
|
|
19
|
+
const parsed = parse(raw, fmt, REF_DATE);
|
|
20
|
+
if (isValid(parsed)) return parsed;
|
|
27
21
|
}
|
|
28
|
-
return
|
|
22
|
+
return null;
|
|
29
23
|
}
|
|
30
|
-
const getDefaultTime = ()=>
|
|
31
|
-
|
|
32
|
-
};
|
|
33
|
-
const formatToString = (uses12hClock, value)=>{
|
|
34
|
-
return uses12hClock ? value.format('hh:mm A') : value.format('HH:mm');
|
|
35
|
-
};
|
|
24
|
+
const getDefaultTime = ()=>parse('12:00 AM', 'hh:mm a', REF_DATE);
|
|
25
|
+
const formatToString = (uses12hClock, value)=>format(value, uses12hClock ? 'hh:mm a' : 'HH:mm');
|
|
36
26
|
export const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm = 'AM', onChange })=>{
|
|
37
27
|
const [selectedTime, setSelectedTime] = useState(()=>{
|
|
38
28
|
return formatToString(uses12hClock, getDefaultTime());
|
|
39
29
|
});
|
|
40
30
|
useEffect(()=>{
|
|
41
|
-
setSelectedTime(formatToString(uses12hClock,
|
|
31
|
+
setSelectedTime(formatToString(uses12hClock, parse(`${time} ${ampm}`, 'hh:mm a', REF_DATE)));
|
|
42
32
|
}, [
|
|
43
33
|
time,
|
|
44
34
|
ampm,
|
|
@@ -50,8 +40,8 @@ export const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm =
|
|
|
50
40
|
const parsedTime = parseRawInput(raw);
|
|
51
41
|
if (parsedTime) {
|
|
52
42
|
onChange({
|
|
53
|
-
time:
|
|
54
|
-
ampm:
|
|
43
|
+
time: format(parsedTime, 'hh:mm'),
|
|
44
|
+
ampm: format(parsedTime, 'a').toUpperCase()
|
|
55
45
|
});
|
|
56
46
|
}
|
|
57
47
|
}, [
|
|
@@ -66,8 +56,8 @@ export const TimepickerInput = ({ disabled, uses12hClock, time = '12:00', ampm =
|
|
|
66
56
|
const value = parsedTime ?? getDefaultTime();
|
|
67
57
|
setSelectedTime(formatToString(uses12hClock, value));
|
|
68
58
|
onChange({
|
|
69
|
-
time:
|
|
70
|
-
ampm:
|
|
59
|
+
time: format(value, 'hh:mm'),
|
|
60
|
+
ampm: format(value, 'a').toUpperCase()
|
|
71
61
|
});
|
|
72
62
|
}, [
|
|
73
63
|
selectedTime,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import moment from 'moment';
|
|
2
1
|
import { buildFieldValue, userInputFromDatetime, getDefaultAMPM, getDefaultUtcOffset } from './date';
|
|
3
2
|
describe('date utils', ()=>{
|
|
4
3
|
describe('buildFieldValue', ()=>{
|
|
5
4
|
it('should work properly', ()=>{
|
|
6
5
|
expect(buildFieldValue({
|
|
7
6
|
data: {
|
|
8
|
-
date:
|
|
7
|
+
date: new Date('2018-02-02'),
|
|
9
8
|
time: '05:00',
|
|
10
9
|
ampm: 'PM',
|
|
11
10
|
utcOffset: '+03:00'
|
|
@@ -18,7 +17,7 @@ describe('date utils', ()=>{
|
|
|
18
17
|
});
|
|
19
18
|
expect(buildFieldValue({
|
|
20
19
|
data: {
|
|
21
|
-
date:
|
|
20
|
+
date: new Date('2015-01-14'),
|
|
22
21
|
time: '05:00',
|
|
23
22
|
ampm: 'AM',
|
|
24
23
|
utcOffset: '-05:00'
|
|
@@ -31,7 +30,7 @@ describe('date utils', ()=>{
|
|
|
31
30
|
});
|
|
32
31
|
expect(buildFieldValue({
|
|
33
32
|
data: {
|
|
34
|
-
date:
|
|
33
|
+
date: new Date('2015-01-14'),
|
|
35
34
|
time: '17:00',
|
|
36
35
|
ampm: 'PM',
|
|
37
36
|
utcOffset: '-05:00'
|
|
@@ -46,7 +45,7 @@ describe('date utils', ()=>{
|
|
|
46
45
|
it('returns date-only format when usesTime and usesTimezone are false', ()=>{
|
|
47
46
|
expect(buildFieldValue({
|
|
48
47
|
data: {
|
|
49
|
-
date:
|
|
48
|
+
date: new Date('2020-06-15'),
|
|
50
49
|
time: '10:30',
|
|
51
50
|
ampm: 'AM',
|
|
52
51
|
utcOffset: '+00:00'
|
|
@@ -61,7 +60,7 @@ describe('date utils', ()=>{
|
|
|
61
60
|
it('returns datetime without timezone when usesTime=true, usesTimezone=false', ()=>{
|
|
62
61
|
expect(buildFieldValue({
|
|
63
62
|
data: {
|
|
64
|
-
date:
|
|
63
|
+
date: new Date('2020-06-15'),
|
|
65
64
|
time: '14:30',
|
|
66
65
|
ampm: 'PM',
|
|
67
66
|
utcOffset: '+00:00'
|
|
@@ -90,7 +89,7 @@ describe('date utils', ()=>{
|
|
|
90
89
|
it('handles midnight (12:00 AM) correctly', ()=>{
|
|
91
90
|
expect(buildFieldValue({
|
|
92
91
|
data: {
|
|
93
|
-
date:
|
|
92
|
+
date: new Date('2021-03-01'),
|
|
94
93
|
time: '12:00',
|
|
95
94
|
ampm: 'AM',
|
|
96
95
|
utcOffset: '+00:00'
|
|
@@ -105,7 +104,7 @@ describe('date utils', ()=>{
|
|
|
105
104
|
it('handles noon (12:00 PM) correctly', ()=>{
|
|
106
105
|
expect(buildFieldValue({
|
|
107
106
|
data: {
|
|
108
|
-
date:
|
|
107
|
+
date: new Date('2021-03-01'),
|
|
109
108
|
time: '12:00',
|
|
110
109
|
ampm: 'PM',
|
|
111
110
|
utcOffset: '+00:00'
|
|
@@ -120,7 +119,7 @@ describe('date utils', ()=>{
|
|
|
120
119
|
it('preserves half-hour offset +05:30 (India)', ()=>{
|
|
121
120
|
expect(buildFieldValue({
|
|
122
121
|
data: {
|
|
123
|
-
date:
|
|
122
|
+
date: new Date('2023-08-15'),
|
|
124
123
|
time: '10:00',
|
|
125
124
|
ampm: 'AM',
|
|
126
125
|
utcOffset: '+05:30'
|
|
@@ -135,7 +134,7 @@ describe('date utils', ()=>{
|
|
|
135
134
|
it('preserves negative half-hour offset -09:30', ()=>{
|
|
136
135
|
expect(buildFieldValue({
|
|
137
136
|
data: {
|
|
138
|
-
date:
|
|
137
|
+
date: new Date('2023-08-15'),
|
|
139
138
|
time: '03:30',
|
|
140
139
|
ampm: 'AM',
|
|
141
140
|
utcOffset: '-09:30'
|
|
@@ -150,7 +149,7 @@ describe('date utils', ()=>{
|
|
|
150
149
|
it('preserves quarter-hour offset +05:45 (Nepal)', ()=>{
|
|
151
150
|
expect(buildFieldValue({
|
|
152
151
|
data: {
|
|
153
|
-
date:
|
|
152
|
+
date: new Date('2023-08-15'),
|
|
154
153
|
time: '05:45',
|
|
155
154
|
ampm: 'AM',
|
|
156
155
|
utcOffset: '+05:45'
|
|
@@ -165,7 +164,7 @@ describe('date utils', ()=>{
|
|
|
165
164
|
it('preserves UTC +00:00 offset', ()=>{
|
|
166
165
|
expect(buildFieldValue({
|
|
167
166
|
data: {
|
|
168
|
-
date:
|
|
167
|
+
date: new Date('2023-01-01'),
|
|
169
168
|
time: '00:00',
|
|
170
169
|
ampm: 'AM',
|
|
171
170
|
utcOffset: '+00:00'
|
|
@@ -180,7 +179,7 @@ describe('date utils', ()=>{
|
|
|
180
179
|
it('preserves far-west offset -12:00', ()=>{
|
|
181
180
|
expect(buildFieldValue({
|
|
182
181
|
data: {
|
|
183
|
-
date:
|
|
182
|
+
date: new Date('2023-01-01'),
|
|
184
183
|
time: '23:59',
|
|
185
184
|
ampm: 'PM',
|
|
186
185
|
utcOffset: '-12:00'
|
|
@@ -195,7 +194,7 @@ describe('date utils', ()=>{
|
|
|
195
194
|
it('preserves far-east offset +14:00', ()=>{
|
|
196
195
|
expect(buildFieldValue({
|
|
197
196
|
data: {
|
|
198
|
-
date:
|
|
197
|
+
date: new Date('2023-01-01'),
|
|
199
198
|
time: '01:00',
|
|
200
199
|
ampm: 'AM',
|
|
201
200
|
utcOffset: '+14:00'
|
|
@@ -210,7 +209,7 @@ describe('date utils', ()=>{
|
|
|
210
209
|
it('11:59 PM → 23:59', ()=>{
|
|
211
210
|
expect(buildFieldValue({
|
|
212
211
|
data: {
|
|
213
|
-
date:
|
|
212
|
+
date: new Date('2023-06-01'),
|
|
214
213
|
time: '11:59',
|
|
215
214
|
ampm: 'PM',
|
|
216
215
|
utcOffset: '+00:00'
|
|
@@ -225,7 +224,7 @@ describe('date utils', ()=>{
|
|
|
225
224
|
it('01:00 AM → 01:00 (no shift)', ()=>{
|
|
226
225
|
expect(buildFieldValue({
|
|
227
226
|
data: {
|
|
228
|
-
date:
|
|
227
|
+
date: new Date('2023-06-01'),
|
|
229
228
|
time: '01:00',
|
|
230
229
|
ampm: 'AM',
|
|
231
230
|
utcOffset: '+00:00'
|
|
@@ -281,12 +280,12 @@ describe('date utils', ()=>{
|
|
|
281
280
|
});
|
|
282
281
|
expect(result.date).toBeUndefined();
|
|
283
282
|
});
|
|
284
|
-
it('parses a date-only string
|
|
283
|
+
it('parses a date-only string', ()=>{
|
|
285
284
|
const result = userInputFromDatetime({
|
|
286
285
|
value: '2022-09-16',
|
|
287
286
|
uses12hClock: false
|
|
288
287
|
});
|
|
289
|
-
expect(result.utcOffset).
|
|
288
|
+
expect(result.utcOffset).toBe('+00:00');
|
|
290
289
|
});
|
|
291
290
|
it('preserves raw time and does not shift by system timezone (positive offset)', ()=>{
|
|
292
291
|
const result = userInputFromDatetime({
|
|
@@ -306,7 +305,7 @@ describe('date utils', ()=>{
|
|
|
306
305
|
expect(result.ampm).toBe('AM');
|
|
307
306
|
expect(result.utcOffset).toBe('-05:30');
|
|
308
307
|
});
|
|
309
|
-
it('handles UTC "Z" suffix —
|
|
308
|
+
it('handles UTC "Z" suffix — normalizes offset to +00:00', ()=>{
|
|
310
309
|
const result = userInputFromDatetime({
|
|
311
310
|
value: '2021-06-01T12:00Z',
|
|
312
311
|
uses12hClock: false
|
package/dist/esm/utils/date.js
CHANGED
|
@@ -1,24 +1,34 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { format, getHours, getMinutes, isValid, parse, parseISO, set } from 'date-fns';
|
|
2
2
|
const ZONE_RX = /(Z|[+-]\d{2}[:+]?\d{2})$/;
|
|
3
|
-
function
|
|
4
|
-
return
|
|
5
|
-
hours: 0,
|
|
6
|
-
minutes: 0
|
|
7
|
-
}).format(format);
|
|
3
|
+
function startOfTodayOffset() {
|
|
4
|
+
return format(new Date(), 'xxx');
|
|
8
5
|
}
|
|
9
|
-
function
|
|
6
|
+
function parseUtcOffset(datetimeString) {
|
|
7
|
+
const match = datetimeString.match(ZONE_RX);
|
|
8
|
+
if (!match) return '+00:00';
|
|
9
|
+
return match[1] === 'Z' ? '+00:00' : match[1];
|
|
10
|
+
}
|
|
11
|
+
function fieldValueToDate(datetimeString) {
|
|
10
12
|
if (!datetimeString) {
|
|
11
13
|
return null;
|
|
12
14
|
}
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
datetime.utcOffset(datetimeString);
|
|
16
|
-
}
|
|
17
|
-
return datetime;
|
|
15
|
+
const date = parseISO(datetimeString);
|
|
16
|
+
return isValid(date) ? date : null;
|
|
18
17
|
}
|
|
19
18
|
function timeFromUserInput(input) {
|
|
20
19
|
const timeInput = input.time || '00:00';
|
|
21
|
-
|
|
20
|
+
const parsed = parse(timeInput, 'HH:mm', new Date(0));
|
|
21
|
+
let hours = getHours(parsed);
|
|
22
|
+
const minutes = getMinutes(parsed);
|
|
23
|
+
if (input.ampm === 'PM' && hours < 12) {
|
|
24
|
+
hours += 12;
|
|
25
|
+
} else if (input.ampm === 'AM' && hours === 12) {
|
|
26
|
+
hours = 0;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
hours,
|
|
30
|
+
minutes
|
|
31
|
+
};
|
|
22
32
|
}
|
|
23
33
|
function datetimeFromUserInput(input) {
|
|
24
34
|
if (!input.date) {
|
|
@@ -26,12 +36,14 @@ function datetimeFromUserInput(input) {
|
|
|
26
36
|
valid: null
|
|
27
37
|
};
|
|
28
38
|
}
|
|
29
|
-
const
|
|
30
|
-
const date =
|
|
31
|
-
hours
|
|
32
|
-
minutes
|
|
39
|
+
const { hours, minutes } = timeFromUserInput(input);
|
|
40
|
+
const date = set(input.date, {
|
|
41
|
+
hours,
|
|
42
|
+
minutes,
|
|
43
|
+
seconds: 0,
|
|
44
|
+
milliseconds: 0
|
|
33
45
|
});
|
|
34
|
-
if (
|
|
46
|
+
if (isValid(date)) {
|
|
35
47
|
return {
|
|
36
48
|
valid: date
|
|
37
49
|
};
|
|
@@ -49,34 +61,58 @@ export function buildFieldValue({ data, usesTime, usesTimezone }) {
|
|
|
49
61
|
invalid: true
|
|
50
62
|
};
|
|
51
63
|
}
|
|
52
|
-
|
|
64
|
+
if (!date.valid) {
|
|
65
|
+
return {
|
|
66
|
+
valid: null,
|
|
67
|
+
invalid: false
|
|
68
|
+
};
|
|
69
|
+
}
|
|
53
70
|
if (usesTimezone) {
|
|
54
|
-
|
|
71
|
+
return {
|
|
72
|
+
valid: format(date.valid, "yyyy-MM-dd'T'HH:mm") + data.utcOffset,
|
|
73
|
+
invalid: false
|
|
74
|
+
};
|
|
55
75
|
} else if (usesTime) {
|
|
56
|
-
|
|
76
|
+
return {
|
|
77
|
+
valid: format(date.valid, "yyyy-MM-dd'T'HH:mm"),
|
|
78
|
+
invalid: false
|
|
79
|
+
};
|
|
57
80
|
} else {
|
|
58
|
-
|
|
81
|
+
return {
|
|
82
|
+
valid: format(date.valid, 'yyyy-MM-dd'),
|
|
83
|
+
invalid: false
|
|
84
|
+
};
|
|
59
85
|
}
|
|
60
|
-
return {
|
|
61
|
-
valid: date?.valid ? date.valid.format(format) : null,
|
|
62
|
-
invalid: false
|
|
63
|
-
};
|
|
64
86
|
}
|
|
65
87
|
export function getDefaultAMPM() {
|
|
66
88
|
return 'AM';
|
|
67
89
|
}
|
|
68
90
|
export function getDefaultUtcOffset() {
|
|
69
|
-
return
|
|
91
|
+
return startOfTodayOffset();
|
|
92
|
+
}
|
|
93
|
+
function extractTimeFromIso(value) {
|
|
94
|
+
const match = value.match(/T(\d{2}:\d{2})/);
|
|
95
|
+
return match ? match[1] : undefined;
|
|
70
96
|
}
|
|
71
97
|
export function userInputFromDatetime({ value, uses12hClock }) {
|
|
72
|
-
const datetime =
|
|
73
|
-
if (datetime) {
|
|
74
|
-
const
|
|
98
|
+
const datetime = fieldValueToDate(value);
|
|
99
|
+
if (datetime && value) {
|
|
100
|
+
const rawTime = extractTimeFromIso(value);
|
|
101
|
+
const timeDate = rawTime ? parse(rawTime, 'HH:mm', new Date(0)) : datetime;
|
|
102
|
+
const hours = getHours(timeDate);
|
|
103
|
+
const ampm = hours < 12 ? 'AM' : 'PM';
|
|
104
|
+
let time;
|
|
105
|
+
if (uses12hClock) {
|
|
106
|
+
const h12 = hours % 12 || 12;
|
|
107
|
+
time = `${String(h12).padStart(2, '0')}:${String(getMinutes(timeDate)).padStart(2, '0')}`;
|
|
108
|
+
} else {
|
|
109
|
+
time = rawTime ?? format(datetime, 'HH:mm');
|
|
110
|
+
}
|
|
75
111
|
return {
|
|
76
112
|
date: datetime,
|
|
77
|
-
time
|
|
78
|
-
ampm
|
|
79
|
-
utcOffset:
|
|
113
|
+
time,
|
|
114
|
+
ampm,
|
|
115
|
+
utcOffset: parseUtcOffset(value)
|
|
80
116
|
};
|
|
81
117
|
} else {
|
|
82
118
|
return {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import moment from 'moment';
|
|
3
2
|
export type DatePickerProps = {
|
|
4
|
-
value?:
|
|
5
|
-
onChange: (val:
|
|
3
|
+
value?: Date;
|
|
4
|
+
onChange: (val: Date | undefined) => void;
|
|
6
5
|
disabled?: boolean;
|
|
7
6
|
};
|
|
8
7
|
export declare const DatepickerInput: (props: DatePickerProps) => React.JSX.Element;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -13,7 +13,10 @@ export declare function buildFieldValue({ data, usesTime, usesTimezone, }: {
|
|
|
13
13
|
invalid: boolean;
|
|
14
14
|
valid?: undefined;
|
|
15
15
|
} | {
|
|
16
|
-
valid:
|
|
16
|
+
valid: null;
|
|
17
|
+
invalid: boolean;
|
|
18
|
+
} | {
|
|
19
|
+
valid: string;
|
|
17
20
|
invalid: boolean;
|
|
18
21
|
};
|
|
19
22
|
export declare function getDefaultAMPM(): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/field-editor-date",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.10-canary.2+3e8a82c8",
|
|
4
4
|
"main": "dist/cjs/index.js",
|
|
5
5
|
"module": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@contentful/f36-tokens": "^6.1.2",
|
|
42
42
|
"@contentful/field-editor-shared": "^4.0.0",
|
|
43
43
|
"@emotion/css": "^11.13.5",
|
|
44
|
-
"
|
|
44
|
+
"date-fns": "^2.30.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@babel/core": "^7.7.4",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"registry": "https://npm.pkg.github.com/"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "3e8a82c8de70258d243f76d17b59ed6a2a315946"
|
|
61
61
|
}
|