react-native-molecules 0.5.0-beta.20 → 0.5.0-beta.22

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 (59) hide show
  1. package/components/Card/Card.tsx +1 -1
  2. package/components/Checkbox/CheckboxBase.ios.tsx +9 -16
  3. package/components/Checkbox/CheckboxBase.tsx +11 -18
  4. package/components/DateField/DateField.tsx +4 -3
  5. package/components/DatePicker/DateCalendar.tsx +4 -4
  6. package/components/DatePicker/DatePickerModal.tsx +35 -23
  7. package/components/DatePicker/DatePickerProvider.tsx +8 -2
  8. package/components/DatePicker/context.tsx +2 -1
  9. package/components/DatePicker/index.tsx +1 -0
  10. package/components/DatePickerInline/DatePickerDockedHeader.tsx +11 -7
  11. package/components/DatePickerInline/DatePickerInline.tsx +1 -1
  12. package/components/DatePickerInline/DatePickerInlineBase.tsx +3 -3
  13. package/components/DatePickerInline/DatePickerInlineHeader.tsx +50 -20
  14. package/components/DatePickerInline/DayNames.tsx +13 -10
  15. package/components/DatePickerInline/HeaderItem.tsx +2 -2
  16. package/components/DatePickerInline/Month.tsx +4 -3
  17. package/components/DatePickerInline/MonthPicker.tsx +74 -54
  18. package/components/DatePickerInline/Swiper.native.tsx +2 -2
  19. package/components/DatePickerInline/Swiper.tsx +3 -3
  20. package/components/DatePickerInline/YearPicker.tsx +136 -112
  21. package/components/DatePickerInline/{DatePickerContext.tsx → store.tsx} +7 -3
  22. package/components/DatePickerInline/types.ts +4 -3
  23. package/components/Divider/Divider.tsx +192 -0
  24. package/components/Divider/index.tsx +11 -0
  25. package/components/Drawer/DrawerItemGroup.tsx +3 -7
  26. package/components/IconButton/IconButton.tsx +2 -12
  27. package/components/List/List.tsx +507 -0
  28. package/components/List/context.tsx +28 -0
  29. package/components/List/index.ts +9 -0
  30. package/components/List/types.ts +149 -0
  31. package/components/{ListItem → List}/utils.ts +47 -50
  32. package/components/Menu/Menu.tsx +156 -12
  33. package/components/Menu/index.tsx +11 -7
  34. package/components/Menu/utils.ts +21 -70
  35. package/components/RadioButton/RadioButtonAndroid.tsx +38 -54
  36. package/components/RadioButton/RadioButtonIOS.tsx +2 -16
  37. package/components/Select/Select.tsx +139 -497
  38. package/components/Select/context.tsx +14 -32
  39. package/components/Select/types.ts +44 -53
  40. package/components/Select/utils.ts +15 -47
  41. package/components/Text/textFactory.tsx +17 -5
  42. package/components/TimeField/TimeField.tsx +1 -1
  43. package/components/TimePicker/TimeInput.tsx +2 -7
  44. package/components/TimePicker/TimePickerModal.tsx +15 -15
  45. package/components/TimePicker/utils.ts +0 -4
  46. package/components/TouchableRipple/TouchableRipple.native.tsx +36 -5
  47. package/components/TouchableRipple/TouchableRipple.tsx +53 -19
  48. package/components/TouchableRipple/rippleFromForegroundColor.ts +21 -0
  49. package/package.json +4 -2
  50. package/components/HorizontalDivider/HorizontalDivider.tsx +0 -103
  51. package/components/HorizontalDivider/index.tsx +0 -9
  52. package/components/ListItem/ListItem.tsx +0 -138
  53. package/components/ListItem/ListItemDescription.tsx +0 -25
  54. package/components/ListItem/ListItemTitle.tsx +0 -25
  55. package/components/ListItem/index.tsx +0 -14
  56. package/components/Menu/MenuDivider.tsx +0 -13
  57. package/components/Menu/MenuItem.tsx +0 -128
  58. package/components/VerticalDivider/VerticalDivider.tsx +0 -100
  59. package/components/VerticalDivider/index.tsx +0 -9
@@ -1,7 +1,6 @@
1
1
  import { memo, useMemo } from 'react';
2
2
  import { View } from 'react-native';
3
3
 
4
- import { format } from '../../utils/date-fns';
5
4
  import { Text } from '../Text';
6
5
  import { getCalendarHeaderHeight } from './DatePickerInlineHeader';
7
6
  import {
@@ -43,6 +42,7 @@ function Month(props: MonthSingleProps | MonthRangeProps | MonthMultiProps) {
43
42
  disableWeekDays,
44
43
  validRange,
45
44
  showOutsideDays,
45
+ locale,
46
46
  // customMonthStyles,
47
47
  } = props;
48
48
  const isHorizontal = scrollMode === 'horizontal';
@@ -54,9 +54,10 @@ function Month(props: MonthSingleProps | MonthRangeProps | MonthMultiProps) {
54
54
  const md = addMonths(new Date(), realIndex);
55
55
  const y = md.getFullYear();
56
56
  const m = md.getMonth();
57
+ const name = new Intl.DateTimeFormat(locale, { month: 'long' }).format(md);
57
58
 
58
- return { monthName: format(md, 'LLLL'), month: m, year: y };
59
- }, [index]);
59
+ return { monthName: name, month: m, year: y };
60
+ }, [index, locale]);
60
61
 
61
62
  const grid = useMemo(
62
63
  () =>
@@ -1,29 +1,35 @@
1
- import { format, setMonth } from 'date-fns';
2
- import { memo, useCallback, useMemo, useRef } from 'react';
3
- import { FlatList, View, type ViewStyle } from 'react-native';
1
+ import { setMonth } from 'date-fns';
2
+ import { memo, useCallback, useMemo } from 'react';
3
+ import { FlatList, type FlatListProps, View, type ViewStyle } from 'react-native';
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
5
 
6
6
  import { resolveStateVariant } from '../../utils';
7
7
  import { range } from '../../utils/dateTimePicker';
8
+ import type { DatePickerLocale } from '../DatePicker/context';
8
9
  import { datePickerMonthItemStyles, datePickerMonthPickerStyles } from '../DatePicker/utils';
9
- import { HorizontalDivider } from '../HorizontalDivider';
10
+ import { Divider } from '../Divider';
10
11
  import { Icon } from '../Icon';
11
- import { ListItem } from '../ListItem/';
12
+ import { List, type ListContentProcessPropsArgs, useListContextValue } from '../List';
12
13
  import { Text } from '../Text';
13
- import { useDatePickerStore, useDatePickerStoreValue } from './DatePickerContext';
14
+ import { useDatePickerInlineStore, useDatePickerInlineStoreValue } from './store';
14
15
 
15
- export default function MonthPicker() {
16
- const [_, setStore] = useDatePickerStore(state => state);
17
- const { localDate, selectingMonth } = useDatePickerStoreValue(state => ({
16
+ type MonthListItem = { id: number; label: string };
17
+
18
+ export default function MonthPicker({ locale }: { locale?: DatePickerLocale }) {
19
+ const [_, setStore] = useDatePickerInlineStore(state => state);
20
+ const { localDate, selectingMonth } = useDatePickerInlineStoreValue(state => ({
18
21
  localDate: state.localDate,
19
22
  selectingMonth: state.pickerType === 'month',
20
23
  }));
21
- // const monthPickerStyles = useComponentStyles('DatePickerDocked_MonthPicker');
22
- const flatList = useRef<FlatList<number> | null>(null);
23
24
  const months = range(0, 11);
25
+ const monthItems = useMemo<MonthListItem[]>(
26
+ () => months.map(month => ({ id: month, label: String(month) })),
27
+ [months],
28
+ );
24
29
 
25
30
  const handleOnChange = useCallback(
26
- (month: number) => {
31
+ (month: number | null) => {
32
+ if (month === null) return;
27
33
  setStore(prev => ({
28
34
  localDate: setMonth(prev.localDate, month),
29
35
  pickerType: undefined,
@@ -32,18 +38,26 @@ export default function MonthPicker() {
32
38
  [setStore],
33
39
  );
34
40
 
35
- const renderItem = useCallback(
36
- ({ item }: { item: number }) => {
37
- return (
41
+ const processFlatListProps = useCallback(
42
+ ({
43
+ props,
44
+ items,
45
+ }: ListContentProcessPropsArgs<
46
+ MonthListItem,
47
+ Omit<FlatListProps<MonthListItem>, 'children' | 'ref'>
48
+ >): FlatListProps<MonthListItem> => ({
49
+ ...props,
50
+ data: items,
51
+ renderItem: ({ item }) => (
38
52
  <Month
39
- month={item}
40
- selected={localDate.getMonth() === item}
41
- onPressMonth={handleOnChange}
53
+ month={item.id}
42
54
  monthStyles={datePickerMonthPickerStyles.root}
55
+ locale={locale}
43
56
  />
44
- );
45
- },
46
- [localDate, handleOnChange],
57
+ ),
58
+ keyExtractor: item => `${item.id}`,
59
+ }),
60
+ [locale],
47
61
  );
48
62
 
49
63
  if (!selectingMonth) {
@@ -51,39 +65,45 @@ export default function MonthPicker() {
51
65
  }
52
66
 
53
67
  return (
54
- <View
55
- style={[
56
- StyleSheet.absoluteFill,
57
- styles.root,
58
- // { backgroundColor },
59
- selectingMonth ? styles.opacity1 : styles.opacity0,
60
- ]}
61
- pointerEvents={selectingMonth ? 'auto' : 'none'}>
62
- <HorizontalDivider />
63
- <FlatList<number>
64
- ref={flatList}
65
- style={styles.list}
66
- data={months}
67
- renderItem={renderItem}
68
- keyExtractor={item => `${item}`}
69
- />
70
- </View>
68
+ <List
69
+ items={monthItems}
70
+ multiple={false}
71
+ value={localDate.getMonth()}
72
+ onChange={handleOnChange}>
73
+ <View
74
+ style={[
75
+ StyleSheet.absoluteFill,
76
+ styles.root,
77
+ selectingMonth ? styles.opacity1 : styles.opacity0,
78
+ ]}
79
+ pointerEvents={selectingMonth ? 'auto' : 'none'}>
80
+ <Divider />
81
+ <List.Content<MonthListItem, typeof FlatList<MonthListItem>>
82
+ ContainerComponent={FlatList<MonthListItem>}
83
+ style={styles.list}
84
+ processProps={processFlatListProps}
85
+ />
86
+ </View>
87
+ </List>
71
88
  );
72
89
  }
73
90
 
74
91
  function MonthPure({
75
92
  month,
76
- selected,
77
- onPressMonth,
78
93
  monthStyles,
94
+ locale,
79
95
  }: {
80
96
  month: number;
81
- selected: boolean;
82
- onPressMonth: (newMonth: number) => any;
83
97
  monthStyles: ViewStyle;
98
+ locale?: DatePickerLocale;
84
99
  }) {
100
+ const isSelected = useListContextValue(state => {
101
+ const selectedValue = state.value as any;
102
+ return (selectedValue?.id ?? selectedValue) === month;
103
+ });
104
+
85
105
  const state = resolveStateVariant({
86
- selected,
106
+ selected: isSelected,
87
107
  });
88
108
 
89
109
  datePickerMonthItemStyles.useVariants({
@@ -95,25 +115,25 @@ function MonthPure({
95
115
 
96
116
  return {
97
117
  monthButtonStyle: [monthButton, monthStyles],
98
- accessibilityState: { selected },
118
+ accessibilityState: { selected: isSelected },
99
119
  };
100
- // eslint-disable-next-line react-hooks/exhaustive-deps
101
- }, [selected, monthStyles, state]);
120
+ }, [isSelected, monthStyles]);
102
121
 
103
- const handleMonthPress = useCallback(() => {
104
- onPressMonth(month);
105
- }, [onPressMonth, month]);
122
+ const monthLabel = useMemo(
123
+ () => new Intl.DateTimeFormat(locale, { month: 'long' }).format(new Date(2000, month, 1)),
124
+ [locale, month],
125
+ );
106
126
 
107
127
  return (
108
- <ListItem
109
- onPress={handleMonthPress}
128
+ <List.Item
129
+ value={month}
110
130
  accessibilityRole="button"
111
131
  accessibilityLabel={String(month)}
112
132
  accessibilityState={accessibilityState}
113
133
  style={monthButtonStyle}
114
134
  testID={`pick-month-${month}`}
115
135
  left={
116
- selected ? (
136
+ isSelected ? (
117
137
  <View style={styles.checkIconView}>
118
138
  <Icon name="check" size={24} />
119
139
  </View>
@@ -123,10 +143,10 @@ function MonthPure({
123
143
  }>
124
144
  <View style={datePickerMonthItemStyles.monthInner}>
125
145
  <Text style={datePickerMonthItemStyles.monthLabel} selectable={false}>
126
- {format(new Date(2000, month, 1), 'MMMM')}
146
+ {monthLabel}
127
147
  </Text>
128
148
  </View>
129
- </ListItem>
149
+ </List.Item>
130
150
  );
131
151
  }
132
152
  const Month = memo(MonthPure);
@@ -9,7 +9,6 @@ import {
9
9
  } from 'react-native';
10
10
 
11
11
  import AutoSizer from './AutoSizer';
12
- import { useDatePickerStore } from './DatePickerContext';
13
12
  import { beginOffset, estimatedMonthHeight, getInitialIndex, totalMonths } from './dateUtils';
14
13
  import { addMonths, getRealIndex } from './dateUtils';
15
14
  import {
@@ -18,6 +17,7 @@ import {
18
17
  getMonthHeight,
19
18
  getVerticalMonthsOffset,
20
19
  } from './Month';
20
+ import { useDatePickerInlineStore } from './store';
21
21
  import type { SwiperProps } from './SwiperUtils';
22
22
  import { montHeaderHeight } from './utils';
23
23
 
@@ -56,7 +56,7 @@ function SwiperInner({
56
56
  );
57
57
 
58
58
  const parentRef = useRef<ScrollView | null>(null);
59
- const [{ localDate }, setStore] = useDatePickerStore(state => state);
59
+ const [{ localDate }, setStore] = useDatePickerInlineStore(state => state);
60
60
 
61
61
  const scrollTo = useCallback(
62
62
  (index: number, animated: boolean) => {
@@ -12,10 +12,10 @@ import {
12
12
 
13
13
  import { useLatest } from '../../hooks';
14
14
  import AutoSizer from './AutoSizer';
15
- import { useDatePickerStore } from './DatePickerContext';
16
15
  import { beginOffset, estimatedMonthHeight, getInitialIndex, totalMonths } from './dateUtils';
17
16
  import { addMonths, getRealIndex } from './dateUtils';
18
17
  import { getIndexFromVerticalOffset, getMonthHeight, getVerticalMonthsOffset } from './Month';
18
+ import { useDatePickerInlineStore } from './store';
19
19
  import type { SwiperProps } from './SwiperUtils';
20
20
  import { montHeaderHeight } from './utils';
21
21
 
@@ -73,7 +73,7 @@ function HorizontalScroller({
73
73
  visibleHorizontalArray(initialIndex),
74
74
  );
75
75
  const parentRef = useRef<HTMLDivElement | null>(null);
76
- const [{ localDate }, setStore] = useDatePickerStore(state => state);
76
+ const [{ localDate }, setStore] = useDatePickerInlineStore(state => state);
77
77
  const settleTimerRef = useRef<number | null>(null);
78
78
  const isRecenteringRef = useRef(false);
79
79
 
@@ -209,7 +209,7 @@ function VerticalScroller({
209
209
  const [visibleIndexes, setVisibleIndexes] = useState<number[]>(visibleArray(initialIndex));
210
210
 
211
211
  const parentRef = useRef<HTMLDivElement | null>(null);
212
- const [{ localDate }, setStore] = useDatePickerStore(state => state);
212
+ const [{ localDate }, setStore] = useDatePickerInlineStore(state => state);
213
213
 
214
214
  useIsomorphicLayoutEffect(() => {
215
215
  const element = parentRef.current;
@@ -1,16 +1,18 @@
1
1
  import { setYear } from 'date-fns';
2
2
  import { memo, useCallback, useLayoutEffect, useMemo, useRef } from 'react';
3
- import { FlatList, ScrollView, StyleSheet, View } from 'react-native';
3
+ import { FlatList, type FlatListProps, StyleSheet, View } from 'react-native';
4
4
 
5
5
  import { getYearRange, resolveStateVariant } from '../../utils';
6
6
  import { datePickerMonthItemStyles, datePickerMonthPickerStyles } from '../DatePicker/utils';
7
- import { HorizontalDivider } from '../HorizontalDivider';
7
+ import { Divider } from '../Divider';
8
8
  import { Icon } from '../Icon';
9
- import { ListItem } from '../ListItem';
9
+ import { List, type ListContentProcessPropsArgs, useListContextValue } from '../List';
10
10
  import { Text } from '../Text';
11
- import { useDatePickerStore } from './DatePickerContext';
11
+ import { useDatePickerInlineStore } from './store';
12
12
  import { datePickerYearItemStyles, datePickerYearPickerStyles } from './utils';
13
13
 
14
+ type YearListItem = { id: number; label: string };
15
+
14
16
  const GRID_ITEM_HEIGHT = 62;
15
17
  const NUM_COLUMNS = 3;
16
18
  const LIST_ITEM_HEIGHT = 46;
@@ -25,23 +27,19 @@ export default function YearPicker({ layout = 'grid' }: YearPickerProps) {
25
27
  }
26
28
 
27
29
  function YearPickerGrid() {
28
- const [{ startDateYear, endDateYear, localDate, pickerType }, setStore] = useDatePickerStore(
29
- state => state,
30
- );
30
+ const [{ startDateYear, endDateYear, localDate, pickerType }, setStore] =
31
+ useDatePickerInlineStore(state => state);
31
32
  const years = useMemo(
32
33
  () => getYearRange(startDateYear, endDateYear),
33
34
  [startDateYear, endDateYear],
34
35
  );
35
- const rows = useMemo(() => {
36
- const chunks: number[][] = [];
37
- for (let i = 0; i < years.length; i += NUM_COLUMNS) {
38
- chunks.push(years.slice(i, i + NUM_COLUMNS));
39
- }
40
- return chunks;
41
- }, [years]);
36
+ const yearItems = useMemo(
37
+ () => years.map(year => ({ id: year, label: String(year) })),
38
+ [years],
39
+ );
42
40
  const selectingYear = pickerType === 'year';
43
41
  const selectedYear = localDate.getFullYear();
44
- const scrollRef = useRef<ScrollView | null>(null);
42
+ const flatListRef = useRef<FlatList<YearListItem> | null>(null);
45
43
 
46
44
  const initialScrollOffset = useMemo(() => {
47
45
  if (years.length === 0) return 0;
@@ -53,7 +51,7 @@ function YearPickerGrid() {
53
51
 
54
52
  useLayoutEffect(() => {
55
53
  if (!selectingYear) return;
56
- scrollRef.current?.scrollTo({ y: initialScrollOffset, animated: false });
54
+ flatListRef.current?.scrollToOffset({ offset: initialScrollOffset, animated: false });
57
55
  }, [selectingYear, initialScrollOffset]);
58
56
 
59
57
  const { containerStyle, yearStyle } = useMemo(() => {
@@ -71,7 +69,8 @@ function YearPickerGrid() {
71
69
  }, [selectingYear]);
72
70
 
73
71
  const handleOnChange = useCallback(
74
- (year: number) => {
72
+ (year: number | null) => {
73
+ if (year === null) return;
75
74
  setStore(prev => ({
76
75
  localDate: setYear(prev.localDate, year),
77
76
  pickerType: undefined,
@@ -80,83 +79,103 @@ function YearPickerGrid() {
80
79
  [setStore],
81
80
  );
82
81
 
83
- return (
84
- <View style={containerStyle} pointerEvents={selectingYear ? 'auto' : 'none'}>
85
- <HorizontalDivider />
86
- <ScrollView
87
- ref={scrollRef}
88
- style={gridStyles.list}
89
- contentOffset={{ x: 0, y: initialScrollOffset }}
90
- removeClippedSubviews>
91
- <View style={gridStyles.grid}>
92
- {rows.map((row, rowIdx) => (
93
- <View key={rowIdx} style={gridStyles.row}>
94
- {row.map(year => (
95
- <View key={year} style={gridStyles.cell}>
96
- <YearPill
97
- year={year}
98
- selected={selectedYear === year}
99
- onPressYear={handleOnChange}
100
- yearStyles={yearStyle}
101
- />
102
- </View>
103
- ))}
104
- {row.length < NUM_COLUMNS &&
105
- Array.from({ length: NUM_COLUMNS - row.length }).map((_, i) => (
106
- <View key={`pad-${i}`} style={gridStyles.cell} />
107
- ))}
108
- </View>
109
- ))}
82
+ const getRowLayout = useCallback(
83
+ (_data: ArrayLike<YearListItem> | null | undefined, index: number) => ({
84
+ length: GRID_ITEM_HEIGHT,
85
+ offset: GRID_ITEM_HEIGHT * index,
86
+ index,
87
+ }),
88
+ [],
89
+ );
90
+
91
+ const processGridFlatListProps = useCallback(
92
+ ({
93
+ props,
94
+ items,
95
+ isEmpty,
96
+ emptyState,
97
+ }: ListContentProcessPropsArgs<
98
+ YearListItem,
99
+ Omit<FlatListProps<YearListItem>, 'children' | 'ref'>
100
+ >): FlatListProps<YearListItem> => ({
101
+ ...props,
102
+ data: items,
103
+ numColumns: NUM_COLUMNS,
104
+ contentContainerStyle: gridStyles.grid,
105
+ columnWrapperStyle: gridStyles.row,
106
+ renderItem: ({ item }) => (
107
+ <View style={gridStyles.cell}>
108
+ <YearPill year={item.id} yearStyles={yearStyle} />
110
109
  </View>
111
- </ScrollView>
112
- </View>
110
+ ),
111
+ keyExtractor: item => `${item.id}`,
112
+ getItemLayout: getRowLayout,
113
+ initialScrollIndex: Math.floor(initialScrollOffset / GRID_ITEM_HEIGHT),
114
+ removeClippedSubviews: true,
115
+ ListEmptyComponent: isEmpty
116
+ ? function GridListEmpty() {
117
+ return <>{emptyState}</>;
118
+ }
119
+ : undefined,
120
+ }),
121
+ [getRowLayout, initialScrollOffset, yearStyle],
122
+ );
123
+
124
+ return (
125
+ <List items={yearItems} multiple={false} value={selectedYear} onChange={handleOnChange}>
126
+ <View style={containerStyle} pointerEvents={selectingYear ? 'auto' : 'none'}>
127
+ <Divider />
128
+ <List.Content<YearListItem, typeof FlatList<YearListItem>>
129
+ ref={flatListRef}
130
+ ContainerComponent={FlatList<YearListItem>}
131
+ style={gridStyles.list}
132
+ processProps={processGridFlatListProps}
133
+ />
134
+ </View>
135
+ </List>
113
136
  );
114
137
  }
115
138
 
116
- function YearPillPure({
117
- year,
118
- selected,
119
- onPressYear,
120
- yearStyles,
121
- }: {
122
- year: number;
123
- selected: boolean;
124
- onPressYear: (newYear: number) => any;
125
- yearStyles: Record<string, any>;
126
- }) {
127
- datePickerYearItemStyles.useVariants({
128
- state: resolveStateVariant({ selected }) as any,
139
+ function YearPillPure({ year, yearStyles }: { year: number; yearStyles: Record<string, any> }) {
140
+ const isSelected = useListContextValue(state => {
141
+ const selectedValue = state.value as any;
142
+ return (selectedValue?.id ?? selectedValue) === year;
129
143
  });
130
144
 
131
- const handlePressYear = useCallback(() => {
132
- onPressYear(year);
133
- }, [year, onPressYear]);
145
+ datePickerYearItemStyles.useVariants({
146
+ state: resolveStateVariant({ selected: isSelected }) as any,
147
+ });
134
148
 
135
149
  return (
136
- <ListItem
150
+ <List.Item
151
+ value={year}
137
152
  contentStyle={datePickerYearItemStyles.content}
138
- onPress={handlePressYear}
139
153
  accessibilityRole="button"
140
154
  accessibilityLabel={String(year)}
141
155
  style={[yearStyles, datePickerYearItemStyles.yearButton]}
142
156
  testID={`pick-year-${year}`}>
143
- <ListItem.Title style={datePickerYearItemStyles.yearLabel} selectable={false}>
157
+ <Text
158
+ typescale="bodyLarge"
159
+ style={datePickerYearItemStyles.yearLabel}
160
+ selectable={false}>
144
161
  {year}
145
- </ListItem.Title>
146
- </ListItem>
162
+ </Text>
163
+ </List.Item>
147
164
  );
148
165
  }
149
166
  const YearPill = memo(YearPillPure);
150
167
 
151
168
  function YearPickerList() {
152
- const [{ startDateYear, endDateYear, localDate, pickerType }, setStore] = useDatePickerStore(
153
- state => state,
154
- );
155
- const flatList = useRef<FlatList<number> | null>(null);
169
+ const [{ startDateYear, endDateYear, localDate, pickerType }, setStore] =
170
+ useDatePickerInlineStore(state => state);
156
171
  const years = useMemo(
157
172
  () => getYearRange(startDateYear, endDateYear),
158
173
  [startDateYear, endDateYear],
159
174
  );
175
+ const yearItems = useMemo<YearListItem[]>(
176
+ () => years.map(year => ({ id: year, label: String(year) })),
177
+ [years],
178
+ );
160
179
  const selectingYear = pickerType === 'year';
161
180
  const selectedYear = localDate.getFullYear();
162
181
 
@@ -167,7 +186,8 @@ function YearPickerList() {
167
186
  }, [selectedYear, years]);
168
187
 
169
188
  const handleOnChange = useCallback(
170
- (year: number) => {
189
+ (year: number | null) => {
190
+ if (year === null) return;
171
191
  setStore(prev => ({
172
192
  localDate: setYear(prev.localDate, year),
173
193
  pickerType: undefined,
@@ -176,15 +196,8 @@ function YearPickerList() {
176
196
  [setStore],
177
197
  );
178
198
 
179
- const renderItem = useCallback(
180
- ({ item }: { item: number }) => (
181
- <YearRow year={item} selected={selectedYear === item} onPressYear={handleOnChange} />
182
- ),
183
- [selectedYear, handleOnChange],
184
- );
185
-
186
199
  const getItemLayout = useCallback(
187
- (_data: any, index: number) => ({
200
+ (_data: ArrayLike<YearListItem> | null | undefined, index: number) => ({
188
201
  length: LIST_ITEM_HEIGHT,
189
202
  offset: LIST_ITEM_HEIGHT * index,
190
203
  index,
@@ -192,51 +205,59 @@ function YearPickerList() {
192
205
  [],
193
206
  );
194
207
 
208
+ const processFlatListProps = useCallback(
209
+ ({
210
+ props,
211
+ items,
212
+ }: ListContentProcessPropsArgs<
213
+ YearListItem,
214
+ Omit<FlatListProps<YearListItem>, 'children' | 'ref'>
215
+ >): FlatListProps<YearListItem> => ({
216
+ ...props,
217
+ data: items,
218
+ renderItem: ({ item }) => <YearRow year={item.id} />,
219
+ keyExtractor: item => `${item.id}`,
220
+ initialScrollIndex,
221
+ getItemLayout,
222
+ }),
223
+ [getItemLayout, initialScrollIndex],
224
+ );
225
+
195
226
  if (!selectingYear) return null;
196
227
 
197
228
  return (
198
- <View style={[StyleSheet.absoluteFill, listStyles.root]} pointerEvents="auto">
199
- <HorizontalDivider />
200
- <FlatList<number>
201
- ref={flatList}
202
- style={listStyles.list}
203
- data={years}
204
- renderItem={renderItem}
205
- keyExtractor={item => `${item}`}
206
- initialScrollIndex={initialScrollIndex}
207
- getItemLayout={getItemLayout}
208
- />
209
- </View>
229
+ <List items={yearItems} multiple={false} value={selectedYear} onChange={handleOnChange}>
230
+ <View style={[StyleSheet.absoluteFill, listStyles.root]} pointerEvents="auto">
231
+ <Divider />
232
+ <List.Content<YearListItem, typeof FlatList<YearListItem>>
233
+ ContainerComponent={FlatList<YearListItem>}
234
+ style={listStyles.list}
235
+ processProps={processFlatListProps}
236
+ />
237
+ </View>
238
+ </List>
210
239
  );
211
240
  }
212
241
 
213
- function YearRowPure({
214
- year,
215
- selected,
216
- onPressYear,
217
- }: {
218
- year: number;
219
- selected: boolean;
220
- onPressYear: (newYear: number) => any;
221
- }) {
222
- datePickerMonthItemStyles.useVariants({
223
- state: resolveStateVariant({ selected }) as any,
242
+ function YearRowPure({ year }: { year: number }) {
243
+ const isSelected = useListContextValue(state => {
244
+ const selectedValue = state.value as any;
245
+ return (selectedValue?.id ?? selectedValue) === year;
224
246
  });
225
247
 
226
- const handlePressYear = useCallback(() => {
227
- onPressYear(year);
228
- }, [year, onPressYear]);
248
+ datePickerMonthItemStyles.useVariants({
249
+ state: resolveStateVariant({ selected: isSelected }) as any,
250
+ });
229
251
 
230
252
  return (
231
- <ListItem
232
- onPress={handlePressYear}
253
+ <List.Item
254
+ value={year}
233
255
  accessibilityRole="button"
234
256
  accessibilityLabel={String(year)}
235
- accessibilityState={{ selected }}
236
257
  style={datePickerMonthItemStyles.monthButton}
237
258
  testID={`pick-year-${year}`}
238
259
  left={
239
- selected ? (
260
+ isSelected ? (
240
261
  <View style={listStyles.checkIconView}>
241
262
  <Icon name="check" size={24} />
242
263
  </View>
@@ -249,7 +270,7 @@ function YearRowPure({
249
270
  {year}
250
271
  </Text>
251
272
  </View>
252
- </ListItem>
273
+ </List.Item>
253
274
  );
254
275
  }
255
276
  const YearRow = memo(YearRowPure);
@@ -260,7 +281,10 @@ const gridStyles = StyleSheet.create({
260
281
  top: 56,
261
282
  zIndex: 100,
262
283
  },
263
- list: { flex: 1 },
284
+ list: {
285
+ flex: 1,
286
+ width: '100%',
287
+ },
264
288
  grid: { alignItems: 'center' },
265
289
  row: {
266
290
  flexDirection: 'row',
@@ -1,4 +1,5 @@
1
1
  import { createFastContext } from '../../fast-context';
2
+ import { registerPortalContext } from '../Portal/Portal';
2
3
 
3
4
  export type Store = {
4
5
  localDate: Date;
@@ -16,7 +17,10 @@ export const defaultValue = {
16
17
 
17
18
  export const {
18
19
  Provider,
19
- useContext: useDatePickerStore,
20
- useContextValue: useDatePickerStoreValue,
21
- useStoreRef: useDatePickerStoreRef,
20
+ useContext: useDatePickerInlineStore,
21
+ useContextValue: useDatePickerInlineStoreValue,
22
+ useStoreRef: useDatePickerInlineStoreRef,
23
+ Context: DatePickerInlineStoreContext,
22
24
  } = createFastContext<Store>();
25
+
26
+ registerPortalContext(DatePickerInlineStoreContext);