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
@@ -5,10 +5,7 @@ import { StyleSheet } from 'react-native-unistyles';
5
5
 
6
6
  import { resolveStateVariant } from '../../utils';
7
7
  import { range } from '../../utils/dateTimePicker';
8
- import {
9
- datePickerDockedMonthItemStyles,
10
- datePickerMonthPickerStyles,
11
- } from '../DatePickerDocked/utils';
8
+ import { datePickerMonthItemStyles, datePickerMonthPickerStyles } from '../DatePicker/utils';
12
9
  import { HorizontalDivider } from '../HorizontalDivider';
13
10
  import { Icon } from '../Icon';
14
11
  import { ListItem } from '../ListItem/';
@@ -25,20 +22,6 @@ export default function MonthPicker() {
25
22
  const flatList = useRef<FlatList<number> | null>(null);
26
23
  const months = range(0, 11);
27
24
 
28
- const { containerStyle, monthStyle } = useMemo(() => {
29
- const { backgroundColor, ...rest } = datePickerMonthPickerStyles.root;
30
-
31
- return {
32
- containerStyle: [
33
- StyleSheet.absoluteFill,
34
- styles.root,
35
- { backgroundColor },
36
- selectingMonth ? styles.opacity1 : styles.opacity0,
37
- ],
38
- monthStyle: rest,
39
- };
40
- }, [selectingMonth]);
41
-
42
25
  const handleOnChange = useCallback(
43
26
  (month: number) => {
44
27
  setStore(prev => ({
@@ -56,11 +39,11 @@ export default function MonthPicker() {
56
39
  month={item}
57
40
  selected={localDate.getMonth() === item}
58
41
  onPressMonth={handleOnChange}
59
- monthStyles={monthStyle}
42
+ monthStyles={datePickerMonthPickerStyles.root}
60
43
  />
61
44
  );
62
45
  },
63
- [localDate, handleOnChange, monthStyle],
46
+ [localDate, handleOnChange],
64
47
  );
65
48
 
66
49
  if (!selectingMonth) {
@@ -68,7 +51,14 @@ export default function MonthPicker() {
68
51
  }
69
52
 
70
53
  return (
71
- <View style={containerStyle} pointerEvents={selectingMonth ? 'auto' : 'none'}>
54
+ <View
55
+ style={[
56
+ StyleSheet.absoluteFill,
57
+ styles.root,
58
+ // { backgroundColor },
59
+ selectingMonth ? styles.opacity1 : styles.opacity0,
60
+ ]}
61
+ pointerEvents={selectingMonth ? 'auto' : 'none'}>
72
62
  <HorizontalDivider />
73
63
  <FlatList<number>
74
64
  ref={flatList}
@@ -95,26 +85,20 @@ function MonthPure({
95
85
  const state = resolveStateVariant({
96
86
  selected,
97
87
  });
98
- datePickerDockedMonthItemStyles.useVariants({
88
+
89
+ datePickerMonthItemStyles.useVariants({
99
90
  state: state as any,
100
91
  });
101
- // const montLocalStyles = useComponentStyles('DatePickerDocked_MonthItem', monthStyles, {
102
- // state: resolveStateVariant({
103
- // selected,
104
- // }),
105
- // });
106
- const { monthInnerStyle, monthLabelStyle, monthButtonStyle, accessibilityState } =
107
- useMemo(() => {
108
- const { monthInner, monthLabel, monthButton } = datePickerDockedMonthItemStyles;
109
-
110
- return {
111
- monthInnerStyle: monthInner,
112
- monthLabelStyle: monthLabel,
113
- monthButtonStyle: [monthButton, monthStyles],
114
- accessibilityState: { selected },
115
- };
116
- // eslint-disable-next-line react-hooks/exhaustive-deps
117
- }, [selected, monthStyles, state]);
92
+
93
+ const { monthButtonStyle, accessibilityState } = useMemo(() => {
94
+ const { monthButton } = datePickerMonthItemStyles;
95
+
96
+ return {
97
+ monthButtonStyle: [monthButton, monthStyles],
98
+ accessibilityState: { selected },
99
+ };
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ }, [selected, monthStyles, state]);
118
102
 
119
103
  const handleMonthPress = useCallback(() => {
120
104
  onPressMonth(month);
@@ -137,8 +121,8 @@ function MonthPure({
137
121
  <View style={styles.spacer} />
138
122
  )
139
123
  }>
140
- <View style={monthInnerStyle}>
141
- <Text style={monthLabelStyle} selectable={false}>
124
+ <View style={datePickerMonthItemStyles.monthInner}>
125
+ <Text style={datePickerMonthItemStyles.monthLabel} selectable={false}>
142
126
  {format(new Date(2000, month, 1), 'MMMM')}
143
127
  </Text>
144
128
  </View>
@@ -1,4 +1,4 @@
1
- import { memo, useCallback, useMemo, useRef, useState } from 'react';
1
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import {
3
3
  type NativeScrollEvent,
4
4
  type NativeSyntheticEvent,
@@ -9,7 +9,9 @@ import {
9
9
  } from 'react-native';
10
10
 
11
11
  import AutoSizer from './AutoSizer';
12
- import { beginOffset, estimatedMonthHeight, totalMonths } from './dateUtils';
12
+ import { useDatePickerStore } from './DatePickerContext';
13
+ import { beginOffset, estimatedMonthHeight, getInitialIndex, totalMonths } from './dateUtils';
14
+ import { addMonths, getRealIndex } from './dateUtils';
13
15
  import {
14
16
  getHorizontalMonthOffset,
15
17
  getIndexFromVerticalOffset,
@@ -54,11 +56,16 @@ function SwiperInner({
54
56
  );
55
57
 
56
58
  const parentRef = useRef<ScrollView | null>(null);
59
+ const [{ localDate }, setStore] = useDatePickerStore(state => state);
57
60
 
58
61
  const scrollTo = useCallback(
59
62
  (index: number, animated: boolean) => {
60
63
  idx.current = index;
61
64
  setVisibleIndexes(getVisibleArray(index, { isHorizontal, height }));
65
+ setStore(prev => ({
66
+ ...prev,
67
+ localDate: addMonths(new Date(), getRealIndex(index)),
68
+ }));
62
69
 
63
70
  if (!parentRef.current) {
64
71
  return;
@@ -81,13 +88,19 @@ function SwiperInner({
81
88
  });
82
89
  }
83
90
  },
84
- [parentRef, isHorizontal, width, height],
91
+ [parentRef, isHorizontal, width, height, setStore],
85
92
  );
86
93
 
87
94
  const scrollToInitial = useCallback(() => {
88
95
  scrollTo(idx.current, false);
89
96
  }, [scrollTo]);
90
97
 
98
+ useEffect(() => {
99
+ const targetIndex = getInitialIndex(localDate);
100
+ if (targetIndex === idx.current) return;
101
+ scrollTo(targetIndex, false);
102
+ }, [localDate, scrollTo]);
103
+
91
104
  const onMomentumScrollEnd = useCallback(
92
105
  (e: NativeSyntheticEvent<NativeScrollEvent>) => {
93
106
  const contentOffset = e.nativeEvent.contentOffset;
@@ -103,9 +116,13 @@ function SwiperInner({
103
116
  if (idx.current !== newIndex) {
104
117
  idx.current = newIndex;
105
118
  setVisibleIndexes(getVisibleArray(newIndex, { isHorizontal, height }));
119
+ setStore(prev => ({
120
+ ...prev,
121
+ localDate: addMonths(new Date(), getRealIndex(newIndex)),
122
+ }));
106
123
  }
107
124
  },
108
- [idx, height, isHorizontal],
125
+ [idx, height, isHorizontal, setStore],
109
126
  );
110
127
 
111
128
  const { innerContainerStyle, itemContainerStyle } = useMemo(() => {
@@ -9,11 +9,12 @@ import {
9
9
  useRef,
10
10
  useState,
11
11
  } from 'react';
12
- import { StyleSheet, View } from 'react-native';
13
12
 
14
13
  import { useLatest } from '../../hooks';
15
14
  import AutoSizer from './AutoSizer';
16
- import { beginOffset, estimatedMonthHeight, totalMonths } from './dateUtils';
15
+ import { useDatePickerStore } from './DatePickerContext';
16
+ import { beginOffset, estimatedMonthHeight, getInitialIndex, totalMonths } from './dateUtils';
17
+ import { addMonths, getRealIndex } from './dateUtils';
17
18
  import { getIndexFromVerticalOffset, getMonthHeight, getVerticalMonthsOffset } from './Month';
18
19
  import type { SwiperProps } from './SwiperUtils';
19
20
  import { montHeaderHeight } from './utils';
@@ -25,7 +26,16 @@ function Swiper({ scrollMode, renderItem, renderHeader, renderFooter, initialInd
25
26
  <>
26
27
  {renderHeader && renderHeader()}
27
28
  {isHorizontal ? (
28
- <View style={styles.flex1}>{renderItem(initialIndex)}</View>
29
+ <AutoSizer>
30
+ {({ width, height }) => (
31
+ <HorizontalScroller
32
+ width={width}
33
+ height={height}
34
+ initialIndex={initialIndex}
35
+ renderItem={renderItem}
36
+ />
37
+ )}
38
+ </AutoSizer>
29
39
  ) : (
30
40
  <AutoSizer>
31
41
  {({ width, height }) => (
@@ -45,6 +55,142 @@ function Swiper({ scrollMode, renderItem, renderHeader, renderFooter, initialInd
45
55
  }
46
56
 
47
57
  const visibleArray = (i: number) => [i - 2, i - 1, i, i + 1, i + 2];
58
+ const visibleHorizontalArray = (i: number) => [i - 1, i, i + 1];
59
+
60
+ function HorizontalScroller({
61
+ width,
62
+ height,
63
+ initialIndex,
64
+ renderItem,
65
+ }: {
66
+ renderItem: (index: number) => any;
67
+ width: number;
68
+ height: number;
69
+ initialIndex: number;
70
+ }) {
71
+ const idx = useRef<number>(initialIndex);
72
+ const [visibleIndexes, setVisibleIndexes] = useState<number[]>(
73
+ visibleHorizontalArray(initialIndex),
74
+ );
75
+ const parentRef = useRef<HTMLDivElement | null>(null);
76
+ const [{ localDate }, setStore] = useDatePickerStore(state => state);
77
+ const settleTimerRef = useRef<number | null>(null);
78
+ const isRecenteringRef = useRef(false);
79
+
80
+ const syncIndex = useCallback(
81
+ (index: number) => {
82
+ idx.current = index;
83
+ setVisibleIndexes(visibleHorizontalArray(index));
84
+ setStore(prev => ({
85
+ ...prev,
86
+ localDate: addMonths(new Date(), getRealIndex(index)),
87
+ }));
88
+ },
89
+ [setStore],
90
+ );
91
+
92
+ const scrollToCenter = useCallback(
93
+ (behavior: ScrollBehavior = 'auto') => {
94
+ const element = parentRef.current;
95
+ if (!element) return;
96
+
97
+ isRecenteringRef.current = true;
98
+ element.scrollTo({
99
+ left: width,
100
+ top: 0,
101
+ behavior,
102
+ });
103
+ window.requestAnimationFrame(() => {
104
+ isRecenteringRef.current = false;
105
+ });
106
+ },
107
+ [width],
108
+ );
109
+
110
+ useIsomorphicLayoutEffect(() => {
111
+ scrollToCenter();
112
+ }, [scrollToCenter, visibleIndexes]);
113
+
114
+ useEffect(() => {
115
+ const targetIndex = getInitialIndex(localDate);
116
+ if (targetIndex === idx.current) return;
117
+ idx.current = targetIndex;
118
+ setVisibleIndexes(visibleHorizontalArray(targetIndex));
119
+ }, [localDate]);
120
+
121
+ useEffect(() => {
122
+ return () => {
123
+ if (settleTimerRef.current) {
124
+ window.clearTimeout(settleTimerRef.current);
125
+ }
126
+ };
127
+ }, []);
128
+
129
+ const onScroll = useCallback(
130
+ (e: UIEvent) => {
131
+ if (isRecenteringRef.current) {
132
+ return;
133
+ }
134
+
135
+ const left = e.currentTarget.scrollLeft;
136
+
137
+ if (settleTimerRef.current) {
138
+ window.clearTimeout(settleTimerRef.current);
139
+ }
140
+
141
+ settleTimerRef.current = window.setTimeout(() => {
142
+ const direction = left > width * 1.5 ? 1 : left < width * 0.5 ? -1 : 0;
143
+
144
+ if (direction !== 0) {
145
+ syncIndex(idx.current + direction);
146
+ }
147
+
148
+ scrollToCenter(direction === 0 ? 'smooth' : 'auto');
149
+ }, 120);
150
+ },
151
+ [scrollToCenter, syncIndex, width],
152
+ );
153
+
154
+ const { containerStyle, innerContainerStyle, itemContainerStyle } = useMemo(() => {
155
+ return {
156
+ containerStyle: {
157
+ height,
158
+ width,
159
+ overflowX: 'auto',
160
+ overflowY: 'hidden',
161
+ scrollSnapType: 'x mandatory',
162
+ WebkitOverflowScrolling: 'touch',
163
+ scrollbarWidth: 'none',
164
+ msOverflowStyle: 'none',
165
+ },
166
+ innerContainerStyle: {
167
+ width: width * 3,
168
+ height,
169
+ position: 'relative',
170
+ },
171
+ itemContainerStyle: (vi: number) => ({
172
+ left: width * vi,
173
+ top: 0,
174
+ bottom: 0,
175
+ position: 'absolute',
176
+ width,
177
+ scrollSnapAlign: 'start',
178
+ }),
179
+ };
180
+ }, [height, width]);
181
+
182
+ return (
183
+ <div ref={parentRef} style={containerStyle as CSSProperties} onScroll={onScroll}>
184
+ <div style={innerContainerStyle as CSSProperties}>
185
+ {[0, 1, 2].map(vi => (
186
+ <div key={vi} style={itemContainerStyle(vi) as CSSProperties}>
187
+ {renderItem(visibleIndexes[vi])}
188
+ </div>
189
+ ))}
190
+ </div>
191
+ </div>
192
+ );
193
+ }
48
194
 
49
195
  function VerticalScroller({
50
196
  width,
@@ -63,19 +209,30 @@ function VerticalScroller({
63
209
  const [visibleIndexes, setVisibleIndexes] = useState<number[]>(visibleArray(initialIndex));
64
210
 
65
211
  const parentRef = useRef<HTMLDivElement | null>(null);
212
+ const [{ localDate }, setStore] = useDatePickerStore(state => state);
66
213
 
67
214
  useIsomorphicLayoutEffect(() => {
68
215
  const element = parentRef.current;
69
216
  if (!element) {
70
217
  return;
71
218
  }
72
- const top = getVerticalMonthsOffset(idx.current) - montHeaderHeight;
73
-
74
219
  element.scrollTo({
75
- top,
220
+ top: getVerticalMonthsOffset(idx.current) - montHeaderHeight,
76
221
  });
77
222
  }, [parentRef, idx]);
78
223
 
224
+ useEffect(() => {
225
+ const targetIndex = getInitialIndex(localDate);
226
+ if (targetIndex === idx.current) return;
227
+ idx.current = targetIndex;
228
+ setVisibleIndexes(visibleArray(targetIndex));
229
+ const element = parentRef.current;
230
+ if (!element) return;
231
+ element.scrollTo({
232
+ top: getVerticalMonthsOffset(targetIndex) - montHeaderHeight,
233
+ });
234
+ }, [localDate]);
235
+
79
236
  const setVisibleIndexesThrottled = useDebouncedCallback(setVisibleIndexes);
80
237
 
81
238
  const onScroll = useCallback(
@@ -91,9 +248,13 @@ function VerticalScroller({
91
248
  if (idx.current !== index) {
92
249
  idx.current = index;
93
250
  setVisibleIndexesThrottled(visibleArray(index));
251
+ setStore(prev => ({
252
+ ...prev,
253
+ localDate: addMonths(new Date(), getRealIndex(index)),
254
+ }));
94
255
  }
95
256
  },
96
- [setVisibleIndexesThrottled],
257
+ [setStore, setVisibleIndexesThrottled],
97
258
  );
98
259
 
99
260
  const { containerStyle, innerContainerStyle, itemContainerStyle } = useMemo(() => {
@@ -132,12 +293,6 @@ function VerticalScroller({
132
293
  );
133
294
  }
134
295
 
135
- const styles = StyleSheet.create({
136
- flex1: {
137
- flex: 1,
138
- },
139
- });
140
-
141
296
  export function useDebouncedCallback(callback: any): any {
142
297
  const mounted = useRef<boolean>(true);
143
298
  const latest = useLatest(callback);
@@ -10,6 +10,7 @@ type Props = ViewProps & {
10
10
  generatedDays: {
11
11
  beforeWeekDay: boolean;
12
12
  afterWeekDay: boolean;
13
+ outside: boolean;
13
14
  year: number;
14
15
  month: number;
15
16
  dayOfMonth: number;
@@ -24,6 +25,7 @@ type Props = ViewProps & {
24
25
  }[];
25
26
  onPressDate: (date: Date) => any;
26
27
  disableWeekDays?: DisableWeekDaysType;
28
+ showOutsideDays?: boolean;
27
29
  };
28
30
 
29
31
  const Week = ({
@@ -31,6 +33,7 @@ const Week = ({
31
33
  generatedDays,
32
34
  onPressDate,
33
35
  disableWeekDays,
36
+ showOutsideDays,
34
37
  style,
35
38
  ...rest
36
39
  }: Props) => {
@@ -39,9 +42,10 @@ const Week = ({
39
42
  {generatedDays
40
43
  .filter(gd => showWeekDay(gd.dayIndex, disableWeekDays))
41
44
  .map(gd => {
45
+ const isOutside = gd.beforeWeekDay || gd.afterWeekDay;
42
46
  return (
43
47
  <Fragment key={gd.dayIndex + weekIndex}>
44
- {gd.beforeWeekDay || gd.afterWeekDay ? (
48
+ {isOutside && !showOutsideDays ? (
45
49
  <EmptyDay />
46
50
  ) : (
47
51
  <Day
@@ -55,6 +59,7 @@ const Week = ({
55
59
  onPressDate={onPressDate}
56
60
  isToday={gd.isToday}
57
61
  disabled={gd.disabled}
62
+ outside={isOutside}
58
63
  />
59
64
  )}
60
65
  </Fragment>