Skip to content

Commit 5fcce6d

Browse files
Implement imperative MaterialRangePicker
Since this is a new module, I think we should only support the imperative API the same way we encourage developers to use the imperative API for the date and time pickers.
1 parent af8a7b7 commit 5fcce6d

File tree

7 files changed

+146
-1
lines changed

7 files changed

+146
-1
lines changed

src/MaterialRangePicker.android.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* @format
3+
* @flow strict-local
4+
*/
5+
6+
import type {MaterialRangeProps, Range} from './types';
7+
import RNMaterialRangePickerAndroid from './specs/NativeModuleMaterialRangePicker';
8+
import {RANGE_SET_ACTION} from './constants';
9+
import {createDismissEvtParams, createRangeSetEvtParams} from './eventCreators';
10+
11+
async function open({
12+
onChange,
13+
value = {},
14+
maximumDate,
15+
minimumDate,
16+
onError,
17+
...props
18+
}: MaterialRangeProps = {}) {
19+
try {
20+
const result = await RNMaterialRangePickerAndroid.open({
21+
startTimestamp: value.start?.getTime(),
22+
endTimestamp: value.end?.getTime(),
23+
maximumDate: maximumDate?.getTime(),
24+
minimumDate: minimumDate?.getTime(),
25+
...props,
26+
});
27+
28+
const {action, startTimestamp, endTimestamp, utcOffset} = result;
29+
30+
switch (action) {
31+
case RANGE_SET_ACTION: {
32+
const range: Range = {
33+
start: new Date(startTimestamp),
34+
end: new Date(endTimestamp),
35+
};
36+
37+
const event = createRangeSetEvtParams(range, utcOffset);
38+
onChange?.(event, range);
39+
break;
40+
}
41+
case DISMISS_ACTION:
42+
default: {
43+
const [event] = createDismissEvtParams(originalValue, utcOffset);
44+
onChange?.(event);
45+
break;
46+
}
47+
}
48+
} catch (error) {
49+
onError?.(error);
50+
}
51+
}
52+
53+
function dismiss(): Promise<boolean> {
54+
return RNMaterialRangePickerAndroid.dismiss();
55+
}
56+
57+
export const MaterialRangePicker = {open, dismiss};

src/MaterialRangePicker.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* @format
3+
* @flow strict-local
4+
*/
5+
import {Platform} from 'react-native';
6+
7+
const warn = () => {
8+
console.warn(`MaterialRangePicker is not supported on: ${Platform.OS}`);
9+
};
10+
11+
export const MaterialRangePicker = {open: warn, dismiss: warn};

src/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const DAY_OF_WEEK = Object.freeze({
5555

5656
export const DATE_SET_ACTION = 'dateSetAction';
5757
export const TIME_SET_ACTION = 'timeSetAction';
58+
export const RANGE_SET_ACTION = 'rangeSetAction';
5859
export const DISMISS_ACTION = 'dismissedAction';
5960

6061
export const NEUTRAL_BUTTON_ACTION = 'neutralButtonAction';

src/eventCreators.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @flow strict-local
33
*/
4-
import type {DateTimePickerEvent} from './types';
4+
import type {DateTimePickerEvent, RangePickerEvent, Range} from './types';
55
import {ANDROID_EVT_TYPE, EVENT_TYPE_SET} from './constants';
66

77
export const createDateTimeSetEvtParams = (
@@ -20,6 +20,20 @@ export const createDateTimeSetEvtParams = (
2020
];
2121
};
2222

23+
export const createRangeSetEvtParams = (
24+
range: Range,
25+
utcOffset: number,
26+
): RangePickerEvent => {
27+
return {
28+
type: EVENT_TYPE_SET,
29+
nativeEvent: {
30+
startTimestamp: range.start ? range.start.getTime() : 0,
31+
endTimestamp: range.end ? range.end.getTime() : 0,
32+
utcOffset,
33+
},
34+
};
35+
};
36+
2337
export const createDismissEvtParams = (
2438
date: Date,
2539
utcOffset: number,

src/index.d.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,28 @@ export type AndroidNativeProps = Readonly<
206206
}
207207
>;
208208

209+
export type MaterialRangePickerProps = {
210+
title?: string;
211+
maximumDate?: Date;
212+
minimumDate?: Date;
213+
timeZoneOffsetInMinutes?: number;
214+
timeZoneName?: string;
215+
testID?: string;
216+
initialInputMode?: string;
217+
dialogButtons?: {
218+
positive: {label: string};
219+
negative: {label: string};
220+
};
221+
fullscreen?: boolean;
222+
value?: Range;
223+
onChange?: (event: RangePickerEvent, range?: Range) => void;
224+
};
225+
226+
type Range = {
227+
start?: Date;
228+
end?: Date;
229+
};
230+
209231
export type DatePickerOptions = DateOptions & {
210232
display?: Display;
211233
};
@@ -251,9 +273,15 @@ declare namespace DateTimePickerAndroidType {
251273
const dismiss: (mode: AndroidNativeProps['mode']) => Promise<boolean>;
252274
}
253275

276+
declare namespace MaterialRangePickerType {
277+
const open: (args: MaterialRangePickerProps) => void;
278+
const dismiss: () => Promise<boolean>;
279+
}
280+
254281
declare const RNDateTimePicker: FC<
255282
IOSNativeProps | AndroidNativeProps | WindowsNativeProps
256283
>;
257284

258285
export default RNDateTimePicker;
259286
export const DateTimePickerAndroid: typeof DateTimePickerAndroidType;
287+
export const MaterialRangePicker: typeof MaterialRangePickerType;

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
import RNDateTimePicker from './datetimepicker';
66
export * from './eventCreators';
77
export {DateTimePickerAndroid} from './DateTimePickerAndroid';
8+
export {MaterialRangePicker} from './MaterialRangePicker';
89

910
export default RNDateTimePicker;

src/types.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,36 @@ export type WindowsNativeProps = $ReadOnly<{|
297297
minuteInterval?: number,
298298
accessibilityLabel?: string,
299299
|}>;
300+
301+
export type MaterialRangeProps = $ReadOnly<{|
302+
title?: string,
303+
maximumDate?: Date,
304+
minimumDate?: Date,
305+
timeZoneOffsetInMinutes?: number,
306+
timeZoneName?: string,
307+
testID?: string,
308+
initialInputMode?: string,
309+
dialogButtons?: {
310+
positive: {label: string},
311+
negative: {label: string},
312+
},
313+
fullscreen?: boolean,
314+
value?: Range,
315+
onChange?: ?(event: RangePickerEvent, range?: Range) => void,
316+
|}>;
317+
318+
export type RangePickerEvent = {
319+
type: 'set' | 'dismiss',
320+
nativeEvent: $ReadOnly<{
321+
startTimestamp: number,
322+
endTimestamp: number,
323+
utcOffset: number,
324+
...
325+
}>,
326+
...
327+
};
328+
329+
export type Range = $ReadOnly<{|
330+
start?: Date,
331+
end?: Date,
332+
|}>;

0 commit comments

Comments
 (0)