ferns-ui 0.44.2 → 0.45.1
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/Button.d.ts +1 -1
- package/dist/Button.js +1 -2
- package/dist/Button.js.map +1 -1
- package/dist/Common.d.ts +16 -1
- package/dist/DateTimeActionSheet.d.ts +1 -1
- package/dist/DateTimeActionSheet.js +130 -95
- package/dist/DateTimeActionSheet.js.map +1 -1
- package/dist/DateTimeField.android.d.ts +1 -1
- package/dist/DateTimeField.android.js +20 -20
- package/dist/DateTimeField.android.js.map +1 -1
- package/dist/DateTimeField.ios.d.ts +1 -1
- package/dist/DateTimeField.ios.js +21 -21
- package/dist/DateTimeField.ios.js.map +1 -1
- package/dist/DateUtilities.d.ts +50 -0
- package/dist/DateUtilities.js +240 -0
- package/dist/DateUtilities.js.map +1 -0
- package/dist/DateUtilities.test.d.ts +1 -0
- package/dist/DateUtilities.test.js +270 -0
- package/dist/DateUtilities.test.js.map +1 -0
- package/dist/Field.d.ts +1 -1
- package/dist/Field.js +2 -2
- package/dist/Field.js.map +1 -1
- package/dist/TextField.d.ts +1 -1
- package/dist/TextField.js +36 -34
- package/dist/TextField.js.map +1 -1
- package/dist/TextFieldNumberActionSheet.js +1 -2
- package/dist/TextFieldNumberActionSheet.js.map +1 -1
- package/dist/TimezonePicker.d.ts +3 -0
- package/dist/TimezonePicker.js +26 -0
- package/dist/TimezonePicker.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/setupTests.d.ts +0 -0
- package/dist/setupTests.js +3 -0
- package/dist/setupTests.js.map +1 -0
- package/package.json +37 -54
- package/src/Button.tsx +0 -2
- package/src/Common.ts +19 -1
- package/src/DateTimeActionSheet.tsx +178 -119
- package/src/DateTimeField.android.tsx +18 -17
- package/src/DateTimeField.ios.tsx +19 -18
- package/src/DateUtilities.test.ts +417 -0
- package/src/DateUtilities.tsx +296 -0
- package/src/Field.tsx +2 -0
- package/src/TextField.tsx +34 -31
- package/src/TextFieldNumberActionSheet.tsx +1 -2
- package/src/TimezonePicker.tsx +52 -0
- package/src/index.tsx +1 -0
- package/src/setupTests.ts +1 -0
- package/dist/dayjsExtended.d.ts +0 -2
- package/dist/dayjsExtended.js +0 -10
- package/dist/dayjsExtended.js.map +0 -1
- package/src/dayjsExtended.ts +0 -11
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import {Picker} from "@react-native-picker/picker";
|
|
2
|
+
import {getCalendars} from "expo-localization";
|
|
2
3
|
import range from "lodash/range";
|
|
4
|
+
import {DateTime} from "luxon";
|
|
3
5
|
import React, {useContext, useEffect, useState} from "react";
|
|
4
6
|
import {Platform, StyleProp, TextInput, TextStyle, View} from "react-native";
|
|
5
7
|
import {Calendar} from "react-native-calendars";
|
|
6
8
|
|
|
7
9
|
import {Box} from "./Box";
|
|
8
10
|
import {DateTimeActionSheetProps} from "./Common";
|
|
9
|
-
import dayjs from "./dayjsExtended";
|
|
10
11
|
import {Heading} from "./Heading";
|
|
11
12
|
import {IconButton} from "./IconButton";
|
|
12
13
|
import {isMobileDevice} from "./MediaQuery";
|
|
13
14
|
import {Modal} from "./Modal";
|
|
14
15
|
import {SelectList} from "./SelectList";
|
|
15
16
|
import {ThemeContext} from "./Theme";
|
|
17
|
+
import {TimezonePicker} from "./TimezonePicker";
|
|
16
18
|
|
|
17
19
|
const TIME_PICKER_HEIGHT = 104;
|
|
18
20
|
const INPUT_HEIGHT = 40;
|
|
@@ -107,7 +109,7 @@ const CalendarHeader = ({
|
|
|
107
109
|
addMonth: (num: number) => void;
|
|
108
110
|
month: Date[];
|
|
109
111
|
}): React.ReactElement => {
|
|
110
|
-
const displayDate =
|
|
112
|
+
const displayDate = DateTime.fromJSDate(month[0]).toFormat("MMM yyyy");
|
|
111
113
|
return (
|
|
112
114
|
<Box alignItems="center" direction="row" height={40} justifyContent="between" width="100%">
|
|
113
115
|
<IconButton
|
|
@@ -156,11 +158,13 @@ const CalendarHeader = ({
|
|
|
156
158
|
};
|
|
157
159
|
|
|
158
160
|
// For mobile, renders all components in an action sheet. For web, renders all components in a
|
|
159
|
-
// modal. For mobile:
|
|
160
|
-
// If mode is "
|
|
161
|
-
//
|
|
162
|
-
// renders a
|
|
163
|
-
//
|
|
161
|
+
// modal. For mobile:
|
|
162
|
+
// If mode is "time", renders a spinner picker for time picker on both platforms.
|
|
163
|
+
// If mode is "date", renders our custom calendar on both platforms.
|
|
164
|
+
// If mode is "datetime",renders a spinner picker for time picker and our custom calendar on both
|
|
165
|
+
// platforms.
|
|
166
|
+
// For web, renders a simplistic text box for time picker and a calendar for date picker
|
|
167
|
+
// in a modal In the future, web time picker should be a typeahead dropdown like Google calendar.
|
|
164
168
|
export const DateTimeActionSheet = ({
|
|
165
169
|
// actionSheetRef,
|
|
166
170
|
mode,
|
|
@@ -168,103 +172,121 @@ export const DateTimeActionSheet = ({
|
|
|
168
172
|
onChange,
|
|
169
173
|
visible,
|
|
170
174
|
onDismiss,
|
|
175
|
+
transformValue,
|
|
171
176
|
}: DateTimeActionSheetProps) => {
|
|
172
177
|
const {theme} = useContext(ThemeContext);
|
|
173
178
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
const calendar = getCalendars()[0];
|
|
180
|
+
const originalTimezone = transformValue?.options?.timezone || calendar?.timeZone;
|
|
181
|
+
const [timezone, setTimezone] = useState<string | undefined>(originalTimezone);
|
|
182
|
+
if (!timezone) {
|
|
183
|
+
console.error(
|
|
184
|
+
"Could not automatically determine timezone and none was provided to DateTimeActionSheet."
|
|
185
|
+
);
|
|
180
186
|
}
|
|
181
187
|
|
|
182
|
-
if (
|
|
183
|
-
|
|
188
|
+
if (typeof value !== "string" && typeof value !== "undefined") {
|
|
189
|
+
console.error(`Datetime only accepts string or undefined value, not ${typeof value}: ${value}`);
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
|
|
187
|
-
if (hr === 0) {
|
|
188
|
-
hr = 12;
|
|
189
|
-
}
|
|
192
|
+
// Accept ISO 8601, HH:mm, or hh:mm A formats. We may want only HH:mm or hh:mm A for mode=time
|
|
190
193
|
|
|
191
|
-
const [hour, setHour] = useState<number>(
|
|
192
|
-
const [minute, setMinute] = useState<number>(
|
|
193
|
-
const [amPm, setAmPm] = useState<"am" | "pm">(
|
|
194
|
-
const [date, setDate] = useState<string>(
|
|
194
|
+
const [hour, setHour] = useState<number>(0);
|
|
195
|
+
const [minute, setMinute] = useState<number>(0);
|
|
196
|
+
const [amPm, setAmPm] = useState<"am" | "pm">("am");
|
|
197
|
+
const [date, setDate] = useState<string>("");
|
|
195
198
|
|
|
196
199
|
// If the value changes in the props, update the state for the date and time.
|
|
197
200
|
useEffect(() => {
|
|
198
201
|
let datetime;
|
|
199
202
|
if (value) {
|
|
200
|
-
datetime =
|
|
203
|
+
datetime = DateTime.fromISO(value).setZone(timezone).set({millisecond: 0, second: 0});
|
|
201
204
|
} else {
|
|
202
|
-
datetime =
|
|
205
|
+
datetime = DateTime.now().setZone(timezone).set({millisecond: 0, second: 0});
|
|
206
|
+
}
|
|
207
|
+
if (!datetime.isValid) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Invalid date/time value ${value}, datetime ${datetime} timezone: ${timezone}`
|
|
210
|
+
);
|
|
203
211
|
}
|
|
204
|
-
|
|
212
|
+
|
|
213
|
+
let h = datetime.hour % 12;
|
|
205
214
|
if (h === 0) {
|
|
206
215
|
h = 12;
|
|
207
216
|
}
|
|
208
217
|
setHour(h);
|
|
209
|
-
setMinute(
|
|
210
|
-
setAmPm(
|
|
211
|
-
setDate(
|
|
212
|
-
|
|
218
|
+
setMinute(datetime.minute);
|
|
219
|
+
setAmPm(datetime.toFormat("a") === "am" ? "am" : "pm");
|
|
220
|
+
setDate(datetime.toISO());
|
|
221
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
222
|
+
}, [value, transformValue, transformValue?.options?.timezone]);
|
|
213
223
|
|
|
214
224
|
// TODO Support 24 hour time for time picker.
|
|
215
225
|
const renderMobileTime = () => {
|
|
216
226
|
return (
|
|
217
|
-
<Box
|
|
218
|
-
<Box
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
<
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
227
|
+
<Box>
|
|
228
|
+
<Box direction="row" width="100%">
|
|
229
|
+
<Box paddingY={2} width="35%">
|
|
230
|
+
<Picker
|
|
231
|
+
itemStyle={{
|
|
232
|
+
height: TIME_PICKER_HEIGHT,
|
|
233
|
+
}}
|
|
234
|
+
selectedValue={hour}
|
|
235
|
+
style={{
|
|
236
|
+
height: TIME_PICKER_HEIGHT,
|
|
237
|
+
backgroundColor: "#FFFFFF",
|
|
238
|
+
}}
|
|
239
|
+
onValueChange={(itemValue) => setHour(itemValue)}
|
|
240
|
+
>
|
|
241
|
+
{hours.map((n) => (
|
|
242
|
+
<Picker.Item key={String(n)} label={String(n)} value={String(n)} />
|
|
243
|
+
))}
|
|
244
|
+
</Picker>
|
|
245
|
+
</Box>
|
|
246
|
+
<Box paddingY={2} width="35%">
|
|
247
|
+
<Picker
|
|
248
|
+
itemStyle={{
|
|
249
|
+
height: TIME_PICKER_HEIGHT,
|
|
250
|
+
}}
|
|
251
|
+
selectedValue={minute}
|
|
252
|
+
style={{
|
|
253
|
+
height: TIME_PICKER_HEIGHT,
|
|
254
|
+
backgroundColor: "#FFFFFF",
|
|
255
|
+
}}
|
|
256
|
+
onValueChange={(itemValue) => setMinute(itemValue)}
|
|
257
|
+
>
|
|
258
|
+
{minutes.map((n) => (
|
|
259
|
+
<Picker.Item key={String(n)} label={String(n)} value={String(n)} />
|
|
260
|
+
))}
|
|
261
|
+
</Picker>
|
|
262
|
+
</Box>
|
|
263
|
+
<Box paddingY={2} width="30%">
|
|
264
|
+
<Picker
|
|
265
|
+
itemStyle={{
|
|
266
|
+
height: TIME_PICKER_HEIGHT,
|
|
267
|
+
}}
|
|
268
|
+
selectedValue={amPm}
|
|
269
|
+
style={{
|
|
270
|
+
height: TIME_PICKER_HEIGHT,
|
|
271
|
+
backgroundColor: "#FFFFFF",
|
|
272
|
+
}}
|
|
273
|
+
onValueChange={(itemValue) => setAmPm(itemValue)}
|
|
274
|
+
>
|
|
275
|
+
<Picker.Item key="am" label="am" value="am" />
|
|
276
|
+
<Picker.Item key="pm" label="pm" value="pm" />
|
|
277
|
+
</Picker>
|
|
278
|
+
</Box>
|
|
267
279
|
</Box>
|
|
280
|
+
{Boolean(mode === "time" || mode === "datetime") && (
|
|
281
|
+
<Box paddingY={2}>
|
|
282
|
+
<TimezonePicker
|
|
283
|
+
showLabel={false}
|
|
284
|
+
timezone={timezone}
|
|
285
|
+
width="100%"
|
|
286
|
+
onChange={setTimezone}
|
|
287
|
+
/>
|
|
288
|
+
</Box>
|
|
289
|
+
)}
|
|
268
290
|
</Box>
|
|
269
291
|
);
|
|
270
292
|
};
|
|
@@ -289,7 +311,7 @@ export const DateTimeActionSheet = ({
|
|
|
289
311
|
<TimeInput type="minute" value={minute} onChange={(v) => setMinute(v)} />
|
|
290
312
|
</Box>
|
|
291
313
|
|
|
292
|
-
<Box width={60}>
|
|
314
|
+
<Box marginRight={2} width={60}>
|
|
293
315
|
<SelectList
|
|
294
316
|
options={[
|
|
295
317
|
{label: "am", value: "am"},
|
|
@@ -302,6 +324,11 @@ export const DateTimeActionSheet = ({
|
|
|
302
324
|
}}
|
|
303
325
|
/>
|
|
304
326
|
</Box>
|
|
327
|
+
{Boolean(mode === "time" || mode === "datetime") && (
|
|
328
|
+
<Box>
|
|
329
|
+
<TimezonePicker showLabel={false} timezone={timezone} onChange={setTimezone} />
|
|
330
|
+
</Box>
|
|
331
|
+
)}
|
|
305
332
|
</Box>
|
|
306
333
|
);
|
|
307
334
|
};
|
|
@@ -317,17 +344,42 @@ export const DateTimeActionSheet = ({
|
|
|
317
344
|
|
|
318
345
|
// Note: do not call this if waiting on a state change.
|
|
319
346
|
const sendOnChange = () => {
|
|
320
|
-
const
|
|
347
|
+
const militaryHour = amPm === "pm" && hour !== 12 ? Number(hour) + 12 : Number(hour);
|
|
348
|
+
|
|
321
349
|
if (mode === "date") {
|
|
322
|
-
|
|
350
|
+
const v = DateTime.fromISO(date)
|
|
351
|
+
.setZone("UTC")
|
|
352
|
+
.set({hour: 0, minute: 0, second: 0, millisecond: 0})
|
|
353
|
+
.toISO();
|
|
354
|
+
if (!v || !DateTime.fromISO(v).isValid) {
|
|
355
|
+
throw new Error(`Invalid date: ${date}`);
|
|
356
|
+
}
|
|
357
|
+
onChange({value: v});
|
|
323
358
|
} else if (mode === "time") {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
359
|
+
const v = DateTime.fromISO(date)
|
|
360
|
+
.setZone(timezone)
|
|
361
|
+
.set({hour: militaryHour, minute, second: 0, millisecond: 0})
|
|
362
|
+
.setZone(timezone)
|
|
363
|
+
.setZone("UTC")
|
|
364
|
+
.toISO();
|
|
365
|
+
if (!v || !DateTime.fromISO(v).isValid) {
|
|
366
|
+
throw new Error(`Invalid date: ${date}`);
|
|
367
|
+
}
|
|
368
|
+
onChange({value: v});
|
|
327
369
|
} else if (mode === "datetime") {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
370
|
+
const v = DateTime.fromISO(date)
|
|
371
|
+
.setZone(timezone)
|
|
372
|
+
// Take from the original zone
|
|
373
|
+
// Set the value on the screen
|
|
374
|
+
.set({hour: militaryHour, minute, second: 0, millisecond: 0})
|
|
375
|
+
// Put that in the new timezone on the screen
|
|
376
|
+
// We always send back in UTC
|
|
377
|
+
.setZone("UTC")
|
|
378
|
+
.toISO();
|
|
379
|
+
if (!v || !DateTime.fromISO(v).isValid) {
|
|
380
|
+
throw new Error(`Invalid date: ${date}`);
|
|
381
|
+
}
|
|
382
|
+
onChange({value: v});
|
|
331
383
|
}
|
|
332
384
|
onDismiss();
|
|
333
385
|
};
|
|
@@ -342,47 +394,54 @@ export const DateTimeActionSheet = ({
|
|
|
342
394
|
// Renders our custom calendar component on mobile or web.
|
|
343
395
|
const renderDateCalendar = () => {
|
|
344
396
|
const markedDates: {[id: string]: {selected: boolean; selectedColor: string}} = {};
|
|
397
|
+
|
|
398
|
+
// Check if the date is T00:00:00.000Z (it should be), otherwise treat it as a date in the
|
|
399
|
+
// current timezone.
|
|
400
|
+
const dt = DateTime.fromISO(date).setZone("UTC");
|
|
401
|
+
let dateString: string;
|
|
402
|
+
if (dt.hour === 0 && dt.minute === 0 && dt.second === 0) {
|
|
403
|
+
dateString = dt.toISO()!;
|
|
404
|
+
} else {
|
|
405
|
+
dateString = dt.setZone().toISO()!;
|
|
406
|
+
}
|
|
407
|
+
|
|
345
408
|
if (date) {
|
|
346
|
-
markedDates[
|
|
409
|
+
markedDates[DateTime.fromISO(dateString).toFormat("yyyy-MM-dd")] = {
|
|
347
410
|
selected: true,
|
|
348
411
|
selectedColor: theme.primary,
|
|
349
412
|
};
|
|
350
413
|
}
|
|
351
414
|
return (
|
|
352
|
-
<
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
415
|
+
<Box width="100%">
|
|
416
|
+
<Box marginBottom={4} width="100%">
|
|
417
|
+
<Calendar
|
|
418
|
+
customHeader={CalendarHeader}
|
|
419
|
+
initialDate={dateString}
|
|
420
|
+
markedDates={markedDates}
|
|
421
|
+
onDayPress={(day) => {
|
|
422
|
+
setDate(day.dateString);
|
|
423
|
+
// If mode is just date, we can shortcut and close right away.
|
|
424
|
+
// time and datetime need to wait for the primary button.
|
|
425
|
+
if (mode === "date") {
|
|
426
|
+
onChange({value: day.dateString});
|
|
427
|
+
onDismiss();
|
|
428
|
+
}
|
|
429
|
+
}}
|
|
430
|
+
/>
|
|
431
|
+
</Box>
|
|
432
|
+
</Box>
|
|
366
433
|
);
|
|
367
434
|
};
|
|
368
435
|
|
|
369
436
|
const renderContent = (): React.ReactElement => {
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
return renderDateTime();
|
|
377
|
-
}
|
|
437
|
+
if (mode === "date") {
|
|
438
|
+
return renderDateCalendar();
|
|
439
|
+
} else if (mode === "time" && isMobileDevice()) {
|
|
440
|
+
return renderMobileTime();
|
|
441
|
+
} else if (mode === "time" && !isMobileDevice()) {
|
|
442
|
+
return renderWebTime();
|
|
378
443
|
} else {
|
|
379
|
-
|
|
380
|
-
return renderDateCalendar();
|
|
381
|
-
} else if (mode === "time") {
|
|
382
|
-
return renderWebTime();
|
|
383
|
-
} else {
|
|
384
|
-
return renderDateTime();
|
|
385
|
-
}
|
|
444
|
+
return renderDateTime();
|
|
386
445
|
}
|
|
387
446
|
};
|
|
388
447
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import DateTimePicker from "@react-native-community/datetimepicker";
|
|
2
|
-
import
|
|
2
|
+
import {getCalendars} from "expo-localization";
|
|
3
|
+
import React, {ReactElement, useContext, useState} from "react";
|
|
3
4
|
import {TextInput} from "react-native";
|
|
4
5
|
|
|
5
6
|
import {DateTimeFieldProps} from "./Common";
|
|
6
|
-
import
|
|
7
|
+
import {printDate, printDateAndTime, printTime} from "./DateUtilities";
|
|
7
8
|
import {ThemeContext} from "./Theme";
|
|
8
9
|
import {WithLabel} from "./WithLabel";
|
|
9
10
|
|
|
@@ -13,7 +14,6 @@ export const DateTimeField = ({
|
|
|
13
14
|
onChange,
|
|
14
15
|
errorMessage,
|
|
15
16
|
pickerType = "default",
|
|
16
|
-
dateFormat,
|
|
17
17
|
errorMessageColor,
|
|
18
18
|
}: DateTimeFieldProps): ReactElement => {
|
|
19
19
|
// const [showCalendar, setShowCalendar] = useState(false);
|
|
@@ -25,19 +25,20 @@ export const DateTimeField = ({
|
|
|
25
25
|
|
|
26
26
|
const showCalendarFirst = mode === "datetime" || mode === "date";
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
const calendar = getCalendars()[0];
|
|
29
|
+
const timezone = calendar?.timeZone;
|
|
30
|
+
|
|
31
|
+
let formattedValue;
|
|
32
|
+
if (mode === "date") {
|
|
33
|
+
formattedValue = printDate(value.toISOString(), {showTimezone: true, ignoreTime: true});
|
|
34
|
+
} else if (mode === "time") {
|
|
35
|
+
formattedValue = printTime(value.toISOString(), {
|
|
36
|
+
showTimezone: true,
|
|
37
|
+
timezone: timezone ?? "America/New_York",
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
formattedValue = printDateAndTime(value.toISOString(), {showTimezone: true});
|
|
41
|
+
}
|
|
41
42
|
|
|
42
43
|
const showMode = (currentMode: "date" | "time") => {
|
|
43
44
|
setShowPicker(true);
|
|
@@ -74,7 +75,7 @@ export const DateTimeField = ({
|
|
|
74
75
|
fontFamily: theme.primaryFont,
|
|
75
76
|
borderWidth: 1,
|
|
76
77
|
}}
|
|
77
|
-
value={
|
|
78
|
+
value={formattedValue}
|
|
78
79
|
onPressIn={() => {
|
|
79
80
|
showCalendarFirst ? showDatePicker() : showTimePicker();
|
|
80
81
|
}}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import DateTimePicker from "@react-native-community/datetimepicker";
|
|
2
|
-
import
|
|
2
|
+
import {getCalendars} from "expo-localization";
|
|
3
|
+
import React, {ReactElement, useContext, useState} from "react";
|
|
3
4
|
import {TextInput} from "react-native";
|
|
4
5
|
|
|
5
6
|
import {DateTimeFieldProps} from "./Common";
|
|
6
|
-
import
|
|
7
|
+
import {printDate, printDateAndTime, printTime} from "./DateUtilities";
|
|
7
8
|
import {ThemeContext} from "./Theme";
|
|
8
9
|
import {WithLabel} from "./WithLabel";
|
|
9
10
|
|
|
@@ -13,26 +14,26 @@ export const DateTimeField = ({
|
|
|
13
14
|
onChange,
|
|
14
15
|
errorMessage,
|
|
15
16
|
errorMessageColor,
|
|
16
|
-
dateFormat,
|
|
17
17
|
pickerType = "inline",
|
|
18
18
|
label,
|
|
19
19
|
}: DateTimeFieldProps): ReactElement => {
|
|
20
20
|
const [showPicker, setShowPicker] = useState(false);
|
|
21
21
|
const {theme} = useContext(ThemeContext);
|
|
22
22
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
const calendar = getCalendars()[0];
|
|
24
|
+
const timezone = calendar?.timeZone;
|
|
25
|
+
|
|
26
|
+
let formattedValue;
|
|
27
|
+
if (mode === "date") {
|
|
28
|
+
formattedValue = printDate(value.toISOString(), {showTimezone: true, ignoreTime: true});
|
|
29
|
+
} else if (mode === "time") {
|
|
30
|
+
formattedValue = printTime(value.toISOString(), {
|
|
31
|
+
timezone: timezone ?? "America/New_York",
|
|
32
|
+
showTimezone: true,
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
formattedValue = printDateAndTime(value.toISOString(), {showTimezone: true});
|
|
36
|
+
}
|
|
36
37
|
|
|
37
38
|
return (
|
|
38
39
|
<WithLabel label={label} labelSize="lg">
|
|
@@ -56,7 +57,7 @@ export const DateTimeField = ({
|
|
|
56
57
|
fontFamily: theme.primaryFont,
|
|
57
58
|
borderWidth: 1,
|
|
58
59
|
}}
|
|
59
|
-
value={
|
|
60
|
+
value={formattedValue}
|
|
60
61
|
onPressIn={() => {
|
|
61
62
|
setShowPicker(!showPicker);
|
|
62
63
|
}}
|
|
@@ -69,7 +70,7 @@ export const DateTimeField = ({
|
|
|
69
70
|
mode={mode}
|
|
70
71
|
style={{alignSelf: "flex-start"}}
|
|
71
72
|
testID="dateTimePicker"
|
|
72
|
-
value={
|
|
73
|
+
value={value}
|
|
73
74
|
onChange={(event: any, date: any) => {
|
|
74
75
|
if (!date) {
|
|
75
76
|
return;
|