react-native-molecules 0.5.0-beta.16 → 0.5.0-beta.18

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.
Files changed (87) hide show
  1. package/components/DateField/DateField.tsx +110 -0
  2. package/components/DateField/index.tsx +6 -0
  3. package/components/{DatePickerInput/inputUtils.ts → DateField/useDateFieldState.ts} +17 -49
  4. package/components/DatePicker/DateCalendar.tsx +83 -0
  5. package/components/DatePicker/DatePickerActions.tsx +73 -0
  6. package/components/DatePicker/DatePickerModal.tsx +234 -0
  7. package/components/DatePicker/DatePickerPopover.tsx +79 -0
  8. package/components/DatePicker/DatePickerProvider.tsx +152 -0
  9. package/components/DatePicker/DatePickerTrigger.tsx +23 -0
  10. package/components/DatePicker/context.tsx +82 -0
  11. package/components/DatePicker/index.tsx +44 -0
  12. package/components/DatePicker/utils.ts +292 -0
  13. package/components/DatePickerInline/DatePickerContext.tsx +1 -0
  14. package/components/DatePickerInline/DatePickerDockedHeader.tsx +113 -0
  15. package/components/DatePickerInline/DatePickerInline.tsx +16 -15
  16. package/components/DatePickerInline/DatePickerInlineBase.tsx +7 -1
  17. package/components/DatePickerInline/Day.tsx +25 -1
  18. package/components/DatePickerInline/DayRange.tsx +2 -4
  19. package/components/DatePickerInline/HeaderItem.tsx +42 -27
  20. package/components/DatePickerInline/Month.tsx +45 -65
  21. package/components/DatePickerInline/MonthPicker.tsx +25 -41
  22. package/components/DatePickerInline/Swiper.native.tsx +21 -4
  23. package/components/DatePickerInline/Swiper.tsx +168 -13
  24. package/components/DatePickerInline/Week.tsx +6 -1
  25. package/components/DatePickerInline/YearPicker.tsx +206 -53
  26. package/components/DatePickerInline/dateUtils.tsx +17 -12
  27. package/components/DatePickerInline/types.ts +3 -0
  28. package/components/DatePickerInline/utils.ts +66 -29
  29. package/components/ListItem/ListItem.tsx +3 -1
  30. package/components/ListItem/utils.ts +1 -1
  31. package/components/LoadingIndicator/index.tsx +1 -1
  32. package/components/Menu/Menu.tsx +3 -18
  33. package/components/Popover/Popover.tsx +122 -145
  34. package/components/Popover/PopoverRoot.tsx +74 -0
  35. package/components/Popover/common.ts +50 -34
  36. package/components/Popover/index.ts +18 -1
  37. package/components/Popover/usePlatformMeasure.native.ts +90 -0
  38. package/components/Popover/usePlatformMeasure.ts +118 -0
  39. package/components/Popover/utils.ts +34 -0
  40. package/components/Select/Select.tsx +7 -8
  41. package/components/Select/context.tsx +72 -0
  42. package/components/Select/index.ts +1 -0
  43. package/components/Select/utils.ts +0 -71
  44. package/components/Slot/compose-refs.tsx +2 -0
  45. package/components/TimeField/TimeField.tsx +75 -0
  46. package/components/TimeField/index.tsx +6 -0
  47. package/components/TimeField/useTimeFieldState.ts +70 -0
  48. package/components/{TimePickerField/sanitizeTime.ts → TimeField/utils.ts} +77 -10
  49. package/components/TimePicker/TimePicker.tsx +53 -9
  50. package/components/TimePicker/TimePickerModal.tsx +186 -0
  51. package/components/TimePicker/context.tsx +17 -0
  52. package/components/TimePicker/index.tsx +15 -3
  53. package/components/TimePicker/utils.ts +50 -0
  54. package/hooks/useActionState.tsx +19 -8
  55. package/package.json +6 -1
  56. package/components/DatePickerDocked/DatePickerDocked.tsx +0 -30
  57. package/components/DatePickerDocked/DatePickerDockedHeader.tsx +0 -129
  58. package/components/DatePickerDocked/index.tsx +0 -17
  59. package/components/DatePickerDocked/types.ts +0 -11
  60. package/components/DatePickerDocked/utils.ts +0 -157
  61. package/components/DatePickerInput/DatePickerInput.tsx +0 -130
  62. package/components/DatePickerInput/DatePickerInputModal.tsx +0 -48
  63. package/components/DatePickerInput/DatePickerInputWithoutModal.tsx +0 -73
  64. package/components/DatePickerInput/DateRangeInput.tsx +0 -88
  65. package/components/DatePickerInput/index.tsx +0 -11
  66. package/components/DatePickerInput/types.ts +0 -26
  67. package/components/DatePickerInput/utils.ts +0 -24
  68. package/components/DatePickerModal/AnimatedCrossView.tsx +0 -94
  69. package/components/DatePickerModal/CalendarEdit.tsx +0 -140
  70. package/components/DatePickerModal/DatePickerModal.tsx +0 -85
  71. package/components/DatePickerModal/DatePickerModalContent.tsx +0 -155
  72. package/components/DatePickerModal/DatePickerModalContentHeader.tsx +0 -213
  73. package/components/DatePickerModal/DatePickerModalHeader.tsx +0 -74
  74. package/components/DatePickerModal/DatePickerModalHeaderBackground.tsx +0 -13
  75. package/components/DatePickerModal/index.tsx +0 -16
  76. package/components/DatePickerModal/types.ts +0 -92
  77. package/components/DatePickerModal/utils.ts +0 -122
  78. package/components/DateTimePicker/DateTimePicker.tsx +0 -172
  79. package/components/DateTimePicker/index.tsx +0 -10
  80. package/components/DateTimePicker/utils.ts +0 -12
  81. package/components/Popover/Popover.native.tsx +0 -185
  82. package/components/TimePickerField/TimePickerField.tsx +0 -154
  83. package/components/TimePickerField/index.tsx +0 -10
  84. package/components/TimePickerField/utils.ts +0 -94
  85. package/components/TimePickerModal/TimePickerModal.tsx +0 -119
  86. package/components/TimePickerModal/index.tsx +0 -10
  87. package/components/TimePickerModal/utils.ts +0 -47
@@ -1,19 +1,61 @@
1
- export const sanitizeTime = (value: string, is24hour = false) => {
2
- // Remove all non-numeric and non-colon characters except am/pm
1
+ import { format, parse, set } from 'date-fns';
2
+
3
+ export const timeMask24Hour = (text: string = '') => {
4
+ const cleanTime = text.replace(/\D+/g, '');
5
+
6
+ const hourFirstDigit = /[012]/;
7
+ let hourSecondDigit = /\d/;
8
+
9
+ if (cleanTime.charAt(0) === '2') {
10
+ hourSecondDigit = /[0123]/;
11
+ }
12
+
13
+ return [hourFirstDigit, hourSecondDigit, ':', /[012345]/, /\d/];
14
+ };
15
+
16
+ export const timeMask12Hour = (text: string = '') => {
17
+ const cleanTime = text.replace(/\D+/g, '');
18
+
19
+ const hourFirstDigit = /[01]/;
20
+ let hourSecondDigit = /\d/;
21
+
22
+ if (cleanTime.charAt(0) === '1') {
23
+ hourSecondDigit = /[012]/;
24
+ }
25
+
26
+ return [hourFirstDigit, hourSecondDigit, ':', /[012345]/, /\d/, /[ap]/, 'm'];
27
+ };
28
+
29
+ export const timeFormat = {
30
+ '24': {
31
+ format: 'HH:mm',
32
+ mask: timeMask24Hour,
33
+ },
34
+ '12': {
35
+ format: 'hh:mmaaa',
36
+ mask: timeMask12Hour,
37
+ },
38
+ };
39
+
40
+ const referenceDate = new Date('2022-01-01T00:00:00.000Z');
41
+
42
+ export const sanitizeTimeString = (time: string): string => time.replace(/[^\d:]/g, '');
43
+
44
+ export const sanitizeTime = (value: string, is24Hour = false) => {
3
45
  const sanitizedValue = value.replace(/[^0-9:apm]/gi, '');
4
46
 
5
47
  const singleDigitHour = sanitizedValue.match(/^(\d{1,2})$/);
6
48
  if (singleDigitHour) {
7
49
  const hours = parseInt(singleDigitHour[1], 10);
8
- if (!is24hour && hours >= 1 && hours <= 12) {
50
+ if (!is24Hour && hours >= 1 && hours <= 12) {
9
51
  return `${hours}:00am`;
10
- } else if (is24hour && hours >= 0 && hours < 24) {
52
+ }
53
+ if (is24Hour && hours >= 0 && hours < 24) {
11
54
  return `${hours.toString().padStart(2, '0')}:00`;
12
55
  }
13
56
  }
14
57
 
15
- if (is24hour) {
16
- // Check if it's a valid 24-hour time format (HH:MM)
58
+ if (is24Hour) {
17
59
  const match24Hour = sanitizedValue.match(/^(\d{1,2}):?(\d{2})$/);
18
60
 
19
61
  if (match24Hour) {
@@ -27,7 +69,6 @@ export const sanitizeTime = (value: string, is24hour = false) => {
27
69
  }
28
70
  }
29
71
 
30
- // Convert 12-hour time to 24-hour time format if necessary
31
72
  const match12Hour = sanitizedValue.match(/^(\d{1,2}):?(\d{2})\s*([ap]m)$/i);
32
73
  if (match12Hour) {
33
74
  let hours = parseInt(match12Hour[1], 10);
@@ -40,15 +81,16 @@ export const sanitizeTime = (value: string, is24hour = false) => {
40
81
  } else if (period === 'am' && hours === 12) {
41
82
  hours = 0;
42
83
  }
84
+
43
85
  return `${hours.toString().padStart(2, '0')}:${minutes
44
86
  .toString()
45
87
  .padStart(2, '0')}`;
46
88
  }
47
89
  }
90
+
48
91
  return '';
49
92
  }
50
93
 
51
- // Convert 24-hour time to 12-hour time format if necessary
52
94
  const match24Hour = sanitizedValue.match(/^(\d{1,2}):?(\d{2})$/);
53
95
  if (match24Hour) {
54
96
  let hours = parseInt(match24Hour[1], 10);
@@ -68,7 +110,6 @@ export const sanitizeTime = (value: string, is24hour = false) => {
68
110
  }
69
111
  }
70
112
 
71
- // Check if it's a valid 12-hour time format (HH:MM am/pm)
72
113
  const match12Hour = sanitizedValue.match(/^(\d{1,2}):?(\d{2})\s*([ap]m)$/i);
73
114
  if (match12Hour) {
74
115
  const hours = parseInt(match12Hour[1], 10);
@@ -80,6 +121,32 @@ export const sanitizeTime = (value: string, is24hour = false) => {
80
121
  }
81
122
  }
82
123
 
83
- // If no match, return empty string as invalid input
84
124
  return '';
85
125
  };
126
+
127
+ export const getFormattedTime = ({ time, is24Hour }: { time: string; is24Hour: boolean }) => {
128
+ if (!time) return '';
129
+
130
+ const [hour = '0', minute = '0'] = sanitizeTimeString(time).split(':');
131
+
132
+ return format(
133
+ set(referenceDate, { hours: +hour.padStart(2, '0'), minutes: +minute.padStart(2, '0') }),
134
+ timeFormat[is24Hour ? '24' : '12'].format,
135
+ );
136
+ };
137
+
138
+ export const getOutputTime = ({ time, is24Hour }: { time: string; is24Hour: boolean }) => {
139
+ if (!time) return '';
140
+
141
+ const formattedTime = sanitizeTimeString(getFormattedTime({ time, is24Hour }));
142
+ const isPM = time.replace(/[\d:]/g, '').includes('p');
143
+
144
+ return format(
145
+ parse(
146
+ formattedTime + (is24Hour ? '' : isPM ? 'pm' : 'am'),
147
+ timeFormat[is24Hour ? '24' : '12'].format,
148
+ referenceDate,
149
+ ),
150
+ 'HH:mm',
151
+ );
152
+ };
@@ -1,9 +1,12 @@
1
1
  import { memo, useCallback, useMemo, useState } from 'react';
2
2
  import { type StyleProp, View, type ViewStyle } from 'react-native';
3
3
 
4
+ import { getRegisteredComponentWithFallback } from '../../core';
4
5
  import { useControlledValue } from '../../hooks';
5
6
  import { format, parse } from '../../utils/date-fns';
7
+ import { useOptionalDatePickerContext } from '../DatePicker/context';
6
8
  import AnalogClock from './AnalogClock';
9
+ import { useOptionalTimePickerContext } from './context';
7
10
  import { DisplayModeContext } from './DisplayModeContext';
8
11
  import TimeInputs from './TimeInputs';
9
12
  import {
@@ -26,10 +29,10 @@ type onChangeFunc = ({
26
29
 
27
30
  export type Props = {
28
31
  /**
29
- * hh:mm format
32
+ * hh:mm format. Optional when mounted inside a DatePickerProvider — the provider's Date is read instead.
30
33
  * */
31
- time: string;
32
- onTimeChange: (params: { time: string; focused?: undefined | PossibleClockTypes }) => any;
34
+ time?: string;
35
+ onTimeChange?: (params: { time: string; focused?: undefined | PossibleClockTypes }) => any;
33
36
 
34
37
  is24Hour?: boolean;
35
38
  inputType?: PossibleInputTypes;
@@ -40,16 +43,54 @@ export type Props = {
40
43
  style?: StyleProp<ViewStyle>;
41
44
  };
42
45
 
46
+ const toTimeString = (value: Date | null | undefined): string => {
47
+ if (!value) return '';
48
+ const h = value.getHours().toString().padStart(2, '0');
49
+ const m = value.getMinutes().toString().padStart(2, '0');
50
+ return `${h}:${m}`;
51
+ };
52
+
53
+ const applyTimeToDate = (base: Date | null | undefined, time: string): Date | null => {
54
+ if (!time) return null;
55
+ const [h, m] = time.split(':').map(n => parseInt(n, 10));
56
+ if (Number.isNaN(h) || Number.isNaN(m)) return base ?? null;
57
+ const next = base ? new Date(base) : new Date();
58
+ next.setHours(h, m, 0, 0);
59
+ return next;
60
+ };
61
+
43
62
  function TimePicker({
44
- is24Hour = false,
45
- time,
63
+ time: timeProp,
64
+ onTimeChange: onTimeChangeProp,
65
+ is24Hour: is24HourProp,
46
66
  focused: focusedProp,
47
67
  onFocusInput: onFocusInputProp,
48
- inputType = 'keyboard',
49
- onTimeChange,
68
+ inputType: inputTypeProp,
50
69
  isLandscape = false,
51
70
  style,
52
71
  }: Props) {
72
+ const ctx = useOptionalDatePickerContext();
73
+ const tpCtx = useOptionalTimePickerContext();
74
+
75
+ const ctxDate =
76
+ ctx && ctx.draftValue && typeof ctx.draftValue === 'object' && 'start' in ctx.draftValue
77
+ ? null
78
+ : (ctx?.draftValue as Date | null | undefined);
79
+
80
+ const time = timeProp ?? toTimeString(ctxDate);
81
+ const is24Hour = is24HourProp ?? ctx?.is24Hour ?? false;
82
+ const inputType = inputTypeProp ?? tpCtx?.inputType ?? (ctx ? 'picker' : 'keyboard');
83
+
84
+ const onTimeChange = useMemo(
85
+ () =>
86
+ onTimeChangeProp ??
87
+ ((params: { time: string; focused?: undefined | PossibleClockTypes }) => {
88
+ if (!ctx) return;
89
+ ctx.setValue(applyTimeToDate(ctxDate, params.time));
90
+ }),
91
+ [onTimeChangeProp, ctx, ctxDate],
92
+ );
93
+
53
94
  const { hours, minutes } = useMemo(() => {
54
95
  const date = time ? parse(time, 'HH:mm', new Date()) : new Date();
55
96
 
@@ -62,7 +103,6 @@ function TimePicker({
62
103
  onChange: onFocusInputProp,
63
104
  });
64
105
 
65
- // Initialize display Mode according the hours value
66
106
  const [displayMode, setDisplayMode] = useState<'AM' | 'PM' | undefined>(() =>
67
107
  !is24Hour ? (hours >= 12 ? 'PM' : 'AM') : undefined,
68
108
  );
@@ -127,4 +167,8 @@ function TimePicker({
127
167
  );
128
168
  }
129
169
 
130
- export default memo(TimePicker);
170
+ const TimePickerDefault = memo(TimePicker);
171
+
172
+ export default TimePickerDefault;
173
+
174
+ export const TimePickerClock = getRegisteredComponentWithFallback('TimePicker', TimePickerDefault);
@@ -0,0 +1,186 @@
1
+ import type { ReactNode } from 'react';
2
+ import { memo, useMemo } from 'react';
3
+ import { KeyboardAvoidingView, Platform, View } from 'react-native';
4
+
5
+ import { getRegisteredComponentWithFallback } from '../../core';
6
+ import { useControlledValue } from '../../hooks';
7
+ import { DatePickerActions, DatePickerProvider } from '../DatePicker';
8
+ import type { DatePickerContextType, DatePickerValue } from '../DatePicker/context';
9
+ import {
10
+ DatePickerContext,
11
+ useDatePickerContext,
12
+ useOptionalDatePickerContext,
13
+ withDraftLayer,
14
+ } from '../DatePicker/context';
15
+ import { IconButton } from '../IconButton';
16
+ import { Modal, type ModalProps } from '../Modal';
17
+ import { Portal } from '../Portal';
18
+ import { Text } from '../Text';
19
+ import { TimePickerContext } from './context';
20
+ import { TimePickerClock } from './TimePicker';
21
+ import {
22
+ getTimeInputTypeIcon,
23
+ inputTypes,
24
+ type PossibleInputTypes,
25
+ reverseInputTypes,
26
+ } from './timeUtils';
27
+ import { timePickerModalStyles as styles } from './utils';
28
+
29
+ export type TimePickerModalProps = Omit<ModalProps, 'children' | 'isOpen' | 'onClose'> & {
30
+ children?: ReactNode;
31
+ isOpen?: boolean;
32
+ onClose?: () => void;
33
+ value?: DatePickerValue;
34
+ onChange?: (value: DatePickerValue) => void;
35
+ is24Hour?: boolean;
36
+ inputType?: PossibleInputTypes;
37
+ defaultInputType?: PossibleInputTypes;
38
+ onInputTypeChange?: (next: PossibleInputTypes) => void;
39
+ label?: string;
40
+ uppercase?: boolean;
41
+ cancelLabel?: string;
42
+ confirmLabel?: string;
43
+ keyboardIcon?: string;
44
+ clockIcon?: string;
45
+ /** Override the surface default draft mode. Modal defaults to `true` (staged commit). */
46
+ draft?: boolean;
47
+ };
48
+
49
+ type BodyProps = Omit<
50
+ TimePickerModalProps,
51
+ 'isOpen' | 'onClose' | 'value' | 'onChange' | 'is24Hour' | 'draft'
52
+ >;
53
+
54
+ function TimePickerModalBody({
55
+ children,
56
+ style,
57
+ label = 'Select time',
58
+ uppercase = false,
59
+ cancelLabel = 'Cancel',
60
+ confirmLabel = 'OK',
61
+ keyboardIcon = 'keyboard-outline',
62
+ clockIcon = 'clock-outline',
63
+ inputType: inputTypeProp,
64
+ defaultInputType = inputTypes.picker,
65
+ onInputTypeChange,
66
+ ...rest
67
+ }: BodyProps) {
68
+ const ctx = useDatePickerContext();
69
+ const [inputType, setInputType] = useControlledValue<PossibleInputTypes>({
70
+ value: inputTypeProp,
71
+ defaultValue: defaultInputType,
72
+ onChange: onInputTypeChange,
73
+ });
74
+
75
+ const tpContextValue = useMemo(() => ({ inputType, setInputType }), [inputType, setInputType]);
76
+
77
+ const modalStyle = useMemo(() => [styles.modalContent, style], [style]);
78
+
79
+ return (
80
+ <Portal>
81
+ <TimePickerContext value={tpContextValue}>
82
+ <Modal {...rest} isOpen={ctx.open} onClose={ctx.cancel} style={modalStyle}>
83
+ <KeyboardAvoidingView
84
+ style={styles.keyboardView}
85
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}>
86
+ <View style={styles.frame}>
87
+ <View style={styles.labelContainer}>
88
+ <Text style={styles.label}>
89
+ {uppercase ? label.toUpperCase() : label}
90
+ </Text>
91
+ </View>
92
+ <View style={styles.timePickerContainer}>
93
+ {children ?? <TimePickerClock />}
94
+ </View>
95
+ <View style={styles.footer}>
96
+ <IconButton
97
+ name={getTimeInputTypeIcon(inputType, {
98
+ keyboard: keyboardIcon,
99
+ picker: clockIcon,
100
+ })}
101
+ onPress={() => setInputType(reverseInputTypes[inputType])}
102
+ style={styles.inputTypeToggle}
103
+ accessibilityLabel="toggle keyboard"
104
+ />
105
+ <View style={styles.fill} />
106
+ <DatePickerActions
107
+ cancelLabel={cancelLabel}
108
+ confirmLabel={confirmLabel}
109
+ />
110
+ </View>
111
+ </View>
112
+ </KeyboardAvoidingView>
113
+ </Modal>
114
+ </TimePickerContext>
115
+ </Portal>
116
+ );
117
+ }
118
+
119
+ function TimePickerModalLayer({
120
+ base,
121
+ draft: draftProp,
122
+ bodyProps,
123
+ }: {
124
+ base: DatePickerContextType;
125
+ draft: boolean | undefined;
126
+ bodyProps: BodyProps;
127
+ }) {
128
+ const effectiveDraft = draftProp ?? base.providerDraft ?? true;
129
+ const ctx = useMemo(() => withDraftLayer(base, effectiveDraft), [base, effectiveDraft]);
130
+ if (!base.open) return null;
131
+ return (
132
+ <DatePickerContext value={ctx}>
133
+ <TimePickerModalBody {...bodyProps} />
134
+ </DatePickerContext>
135
+ );
136
+ }
137
+
138
+ function TimePickerModalAdapter({
139
+ draft,
140
+ bodyProps,
141
+ }: {
142
+ draft: boolean | undefined;
143
+ bodyProps: BodyProps;
144
+ }) {
145
+ const base = useDatePickerContext();
146
+ return <TimePickerModalLayer base={base} draft={draft} bodyProps={bodyProps} />;
147
+ }
148
+
149
+ const TimePickerModalDefault = memo(
150
+ ({
151
+ isOpen: isOpenProp,
152
+ onClose: onCloseProp,
153
+ value: valueProp,
154
+ onChange: onChangeProp,
155
+ is24Hour,
156
+ draft: draftProp,
157
+ ...rest
158
+ }: TimePickerModalProps) => {
159
+ const outer = useOptionalDatePickerContext();
160
+
161
+ if (outer) {
162
+ return <TimePickerModalLayer base={outer} draft={draftProp} bodyProps={rest} />;
163
+ }
164
+
165
+ return (
166
+ <DatePickerProvider
167
+ mode="time"
168
+ value={valueProp}
169
+ onChange={onChangeProp}
170
+ open={isOpenProp}
171
+ onOpenChange={next => {
172
+ if (!next) onCloseProp?.();
173
+ }}
174
+ is24Hour={is24Hour}>
175
+ <TimePickerModalAdapter draft={draftProp} bodyProps={rest} />
176
+ </DatePickerProvider>
177
+ );
178
+ },
179
+ );
180
+
181
+ export const TimePickerModal = getRegisteredComponentWithFallback(
182
+ 'TimePickerModal',
183
+ TimePickerModalDefault,
184
+ );
185
+
186
+ export default TimePickerModal;
@@ -0,0 +1,17 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ import { registerPortalContext } from '../Portal';
4
+ import type { PossibleInputTypes } from './timeUtils';
5
+
6
+ export type TimePickerContextType = {
7
+ inputType: PossibleInputTypes;
8
+ setInputType: (next: PossibleInputTypes) => void;
9
+ };
10
+
11
+ export const TimePickerContext = createContext<TimePickerContextType | null>(null);
12
+
13
+ export function useOptionalTimePickerContext(): TimePickerContextType | null {
14
+ return useContext(TimePickerContext);
15
+ }
16
+
17
+ registerPortalContext(TimePickerContext);
@@ -1,9 +1,20 @@
1
- import { getRegisteredComponentWithFallback } from '../../core';
2
- import TimePickerDefault from './TimePicker';
1
+ import { DatePickerProvider, DatePickerTrigger } from '../DatePicker';
2
+ import { TimePickerClock } from './TimePicker';
3
+ import { TimePickerModal } from './TimePickerModal';
3
4
 
4
- export const TimePicker = getRegisteredComponentWithFallback('TimePicker', TimePickerDefault);
5
+ export const TimePicker = {
6
+ Provider: DatePickerProvider,
7
+ Trigger: DatePickerTrigger,
8
+ Clock: TimePickerClock,
9
+ Modal: TimePickerModal,
10
+ };
5
11
 
12
+ export type { TimePickerContextType } from './context';
13
+ export { TimePickerContext, useOptionalTimePickerContext } from './context';
6
14
  export type { Props as TimePickerProps } from './TimePicker';
15
+ export { TimePickerClock } from './TimePicker';
16
+ export type { TimePickerModalProps } from './TimePickerModal';
17
+ export { TimePickerModal } from './TimePickerModal';
7
18
  export {
8
19
  timePickerAmPmSwitcherStyles,
9
20
  timePickerClockHoursStyles,
@@ -11,5 +22,6 @@ export {
11
22
  timePickerClockStyles,
12
23
  timePickerInputsStyles,
13
24
  timePickerInputStyles,
25
+ timePickerModalStyles,
14
26
  timePickerStyles,
15
27
  } from './utils';
@@ -218,6 +218,52 @@ const timePickerClockMinutesStylesDefault = StyleSheet.create(theme => ({
218
218
  textWhite: { color: '#fff' },
219
219
  }));
220
220
 
221
+ const timePickerModalStylesDefault = StyleSheet.create(theme => ({
222
+ keyboardView: {
223
+ justifyContent: 'center',
224
+ alignItems: 'center',
225
+ flex: 1,
226
+ },
227
+ modalContent: {
228
+ minWidth: 287,
229
+ width: undefined,
230
+ maxWidth: undefined,
231
+ flex: undefined,
232
+ borderRadius: theme.shapes.corner.extraLarge,
233
+ overflow: 'hidden',
234
+ },
235
+ frame: {
236
+ backgroundColor: theme.colors.surface,
237
+ },
238
+ labelContainer: {
239
+ minHeight: 56,
240
+ justifyContent: 'flex-end',
241
+ paddingLeft: theme.spacings['6'],
242
+ paddingRight: theme.spacings['6'],
243
+ paddingTop: theme.spacings['6'],
244
+ paddingBottom: theme.spacings['2'],
245
+ alignSelf: 'flex-start',
246
+ },
247
+ label: {
248
+ letterSpacing: 1,
249
+ fontSize: theme.typescale.labelLarge.fontSize,
250
+ color: theme.colors.onSurface,
251
+ fontWeight: theme.typescale.labelLarge.fontWeight,
252
+ },
253
+ timePickerContainer: {
254
+ padding: theme.spacings['6'],
255
+ paddingTop: theme.spacings['2'],
256
+ },
257
+ footer: {
258
+ flexDirection: 'row',
259
+ alignItems: 'center',
260
+ padding: theme.spacings['2'],
261
+ width: '100%',
262
+ },
263
+ inputTypeToggle: { margin: theme.spacings['1'] },
264
+ fill: { flex: 1 },
265
+ }));
266
+
221
267
  const timePickerAmPmSwitcherStylesDefault = StyleSheet.create(theme => ({
222
268
  container: {
223
269
  width: 50,
@@ -291,3 +337,7 @@ export const timePickerAmPmSwitcherStyles = getRegisteredComponentStylesWithFall
291
337
  'TimePicker_AmPmSwitcher',
292
338
  timePickerAmPmSwitcherStylesDefault,
293
339
  );
340
+ export const timePickerModalStyles = getRegisteredComponentStylesWithFallback(
341
+ 'TimePickerModal',
342
+ timePickerModalStylesDefault,
343
+ );
@@ -1,4 +1,4 @@
1
- import { type RefObject, useRef } from 'react';
1
+ import { type RefObject, useCallback, useRef } from 'react';
2
2
 
3
3
  import { useActive } from './useActive';
4
4
  import { useFocus } from './useFocus';
@@ -14,16 +14,27 @@ export type UseActionStateProps = {
14
14
  export const useActionState = (
15
15
  props: UseActionStateProps & { ref?: RefObject<any> | React.ForwardedRef<any> } = {},
16
16
  ) => {
17
- const ref = useRef(null);
18
- const actionsRef = (
19
- (props.ref as any)?.current === undefined ? ref : props.ref
20
- ) as RefObject<any>;
17
+ const internalRef = useRef(null);
18
+ const externalRef = props.ref;
19
+
20
+ const actionsRef = useCallback(
21
+ (node: any) => {
22
+ internalRef.current = node;
23
+ if (typeof externalRef === 'function') {
24
+ externalRef(node);
25
+ } else if (externalRef) {
26
+ (externalRef as RefObject<any>).current = node;
27
+ }
28
+ },
29
+ [externalRef],
30
+ ) as unknown as RefObject<any>;
31
+
21
32
  const hovered =
22
- useHover(actionsRef, props.actionsToListen?.includes('hover')) || !!props.hovered;
33
+ useHover(internalRef, props.actionsToListen?.includes('hover')) || !!props.hovered;
23
34
  const pressed =
24
- useActive(actionsRef, props.actionsToListen?.includes('press')) || !!props.pressed;
35
+ useActive(internalRef, props.actionsToListen?.includes('press')) || !!props.pressed;
25
36
  const focused =
26
- useFocus(actionsRef, props.actionsToListen?.includes('focus')) || !!props.focused;
37
+ useFocus(internalRef, props.actionsToListen?.includes('focus')) || !!props.focused;
27
38
 
28
39
  return {
29
40
  actionsRef,
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "react-native-molecules",
3
- "version": "0.5.0-beta.16",
3
+ "version": "0.5.0-beta.18",
4
4
  "author": "Thet Aung <thetaung.dev@gmail.com>",
5
5
  "license": "MIT",
6
6
  "main": "index.ts",
7
+ "sideEffects": [
8
+ "components/DatePicker/context.tsx",
9
+ "components/Select/context.tsx",
10
+ "components/TimePicker/context.tsx"
11
+ ],
7
12
  "files": [
8
13
  "components",
9
14
  "context-bridge",
@@ -1,30 +0,0 @@
1
- import { memo } from 'react';
2
-
3
- import DatePickerInlineBase from '../DatePickerInline/DatePickerInlineBase';
4
- import { Popover } from '../Popover';
5
- import DatePickerDockedHeader from './DatePickerDockedHeader';
6
- import type { DatePickerDockedProps } from './types';
7
- import { datePickerDockedMonthStyles } from './utils';
8
-
9
- const DatePickerDocked = (props: DatePickerDockedProps) => {
10
- const { triggerRef, isOpen, onToggle, onClose } = props;
11
-
12
- return (
13
- <Popover
14
- triggerRef={triggerRef}
15
- isOpen={isOpen}
16
- onClose={onClose}
17
- {...props.popoverContentProps}>
18
- <DatePickerInlineBase
19
- {...props}
20
- // TODO - fix ts issues
21
- // @ts-ignore
22
- HeaderComponent={DatePickerDockedHeader}
23
- onToggle={onToggle}
24
- monthStyle={datePickerDockedMonthStyles}
25
- />
26
- </Popover>
27
- );
28
- };
29
-
30
- export default memo(DatePickerDocked);