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
@@ -51,7 +51,7 @@ const Card = (
51
51
  container: [cardStyles.container, cardStyles.root, style],
52
52
  innerContainer: [cardStyles.innerContainer, touchableContainerStyle],
53
53
  };
54
- // eslint-disable-next-line
54
+ // eslint-disable-next-line react-hooks/exhaustive-deps
55
55
  }, [variant, state, style, touchableContainerStyle]);
56
56
 
57
57
  const elevation = elevationProp === undefined ? elevationLevel ?? 0 : elevationProp;
@@ -1,4 +1,3 @@
1
- import setColor from 'color';
2
1
  import { forwardRef, memo, useCallback, useMemo } from 'react';
3
2
  import { View } from 'react-native';
4
3
 
@@ -37,20 +36,15 @@ const CheckboxIOS = (
37
36
  state: state as States,
38
37
  size,
39
38
  });
39
+ const checkedColor = colorProp;
40
40
 
41
- const { checkedColor, iconSize, rippleColor, rippleContainerStyles, iconContainerStyles } =
42
- useMemo(() => {
43
- const _checkedColor = colorProp;
44
-
45
- return {
46
- checkedColor: _checkedColor,
47
- iconSize: iconSizeMap[size],
48
- rippleColor: setColor(_checkedColor).fade(0.32).rgb().string(),
49
- rippleContainerStyles: [styles.root, style],
50
- iconContainerStyles: { opacity: indeterminate || checked ? 1 : 0 },
51
- };
52
- // eslint-disable-next-line react-hooks/exhaustive-deps
53
- }, [checked, colorProp, indeterminate, style, state, size]);
41
+ const { rippleContainerStyles, iconContainerStyles } = useMemo(() => {
42
+ return {
43
+ rippleContainerStyles: [styles.root, style],
44
+ iconContainerStyles: { opacity: indeterminate || checked ? 1 : 0 },
45
+ };
46
+ // eslint-disable-next-line react-hooks/exhaustive-deps
47
+ }, [checked, colorProp, indeterminate, style, state, size, checkedColor]);
54
48
 
55
49
  const onChange = useCallback(() => {
56
50
  onChangeProp?.(!checked);
@@ -62,7 +56,6 @@ const CheckboxIOS = (
62
56
  <TouchableRipple
63
57
  {...rest}
64
58
  borderless
65
- rippleColor={rippleColor}
66
59
  onPress={onChange}
67
60
  disabled={disabled}
68
61
  accessibilityRole="checkbox"
@@ -76,7 +69,7 @@ const CheckboxIOS = (
76
69
  allowFontScaling={false}
77
70
  type="material-community"
78
71
  name={icon}
79
- size={iconSize}
72
+ size={iconSizeMap[size]}
80
73
  color={checkedColor}
81
74
  />
82
75
  </View>
@@ -1,6 +1,5 @@
1
- import setColor from 'color';
2
1
  import { forwardRef, memo, type PropsWithoutRef, useCallback, useMemo } from 'react';
3
- import { Platform, type ViewProps } from 'react-native';
2
+ import { type ViewProps } from 'react-native';
4
3
 
5
4
  import { useActionState } from '../../hooks';
6
5
  import { resolveStateVariant } from '../../utils';
@@ -51,20 +50,15 @@ const CheckboxAndroid = (
51
50
  size,
52
51
  });
53
52
 
54
- const { iconSize, rippleColor, rippleContainerStyles, stateLayerStyle, iconStyle } =
55
- useMemo(() => {
56
- const _color = tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp);
53
+ const { rippleContainerStyles, iconStyle } = useMemo(() => {
54
+ const _color = tokenStylesParser.getColor(checked ? colorProp : uncheckedColorProp);
57
55
 
58
- return {
59
- iconStyle: [styles.icon, _color],
60
- iconSize: iconSizeMap[size],
61
- // TODO - fix this on web
62
- rippleColor:
63
- Platform.OS === 'web' ? undefined : setColor(_color).fade(0.32).rgb().string(),
64
- rippleContainerStyles: [styles.root, style],
65
- stateLayerStyle: [styles.stateLayer, stateLayerProps?.style],
66
- };
67
- }, [checked, colorProp, uncheckedColorProp, size, style, stateLayerProps?.style]);
56
+ return {
57
+ iconStyle: [styles.icon, _color],
58
+ rippleContainerStyles: [styles.root, style],
59
+ };
60
+ // eslint-disable-next-line react-hooks/exhaustive-deps
61
+ }, [checked, colorProp, uncheckedColorProp, style, size, state]);
68
62
 
69
63
  const onChange = useCallback(() => {
70
64
  onChangeProp?.(!checked);
@@ -82,7 +76,6 @@ const CheckboxAndroid = (
82
76
  <TouchableRipple
83
77
  {...rest}
84
78
  borderless
85
- rippleColor={rippleColor}
86
79
  onPress={onChange}
87
80
  disabled={disabled}
88
81
  accessibilityRole="checkbox"
@@ -96,13 +89,13 @@ const CheckboxAndroid = (
96
89
  allowFontScaling={false}
97
90
  type="material-community"
98
91
  name={icon}
99
- size={iconSize}
92
+ size={iconSizeMap[size]}
100
93
  style={iconStyle}
101
94
  />
102
95
  <StateLayer
103
96
  testID={testID ? `${testID}-stateLayer` : ''}
104
97
  {...stateLayerProps}
105
- style={stateLayerStyle}
98
+ style={[styles.stateLayer, stateLayerProps?.style]}
106
99
  />
107
100
  </>
108
101
  </TouchableRipple>
@@ -14,6 +14,7 @@ export type DateFieldProps = Omit<
14
14
  > & {
15
15
  inputMode?: DateFieldInputMode;
16
16
  dateFormat?: string;
17
+ mask?: string;
17
18
  };
18
19
 
19
20
  const DEFAULT_DATE_FORMAT = 'dd/MM/yyyy';
@@ -93,13 +94,13 @@ function DateField({
93
94
 
94
95
  return (
95
96
  <TextInputWithMask
97
+ placeholder={dateFormat}
98
+ keyboardType="number-pad"
99
+ mask={dateFormat}
96
100
  {...rest}
97
101
  ref={ref}
98
102
  disabled={disabled}
99
103
  value={formattedValue}
100
- placeholder={dateFormat}
101
- keyboardType="number-pad"
102
- mask={dateFormat}
103
104
  onChangeText={onChangeText}
104
105
  onBlur={onInnerBlur}
105
106
  onFocus={onInnerFocus}
@@ -11,7 +11,7 @@ import { useOptionalDatePickerContext } from './context';
11
11
 
12
12
  export type DateCalendarProps = DatePickerInlineProps;
13
13
 
14
- const DateCalendarDefault = memo((props: DateCalendarProps) => {
14
+ const DateCalendarDefault = memo(({ headerLayout = 'default', ...props }: DateCalendarProps) => {
15
15
  const ctx = useOptionalDatePickerContext();
16
16
 
17
17
  const hasExplicitState =
@@ -22,7 +22,7 @@ const DateCalendarDefault = memo((props: DateCalendarProps) => {
22
22
  props.onChange !== undefined;
23
23
 
24
24
  const isRange = props.mode === 'range' || ctx?.mode === 'range';
25
- const effectiveHeaderLayout = props.headerLayout ?? (ctx ? 'docked' : 'inline');
25
+ const effectiveHeaderLayout = headerLayout ?? (ctx ? 'docked' : 'inline');
26
26
  const showOutsideDays =
27
27
  props.showOutsideDays ?? (effectiveHeaderLayout === 'docked' && !isRange);
28
28
 
@@ -50,7 +50,7 @@ const DateCalendarDefault = memo((props: DateCalendarProps) => {
50
50
  onChange={onChange}
51
51
  locale={locale}
52
52
  validRange={validRange}
53
- headerLayout={props.headerLayout ?? 'docked'}
53
+ headerLayout={headerLayout}
54
54
  showOutsideDays={showOutsideDays}
55
55
  />
56
56
  );
@@ -72,7 +72,7 @@ const DateCalendarDefault = memo((props: DateCalendarProps) => {
72
72
  onChange={onChange}
73
73
  locale={locale}
74
74
  validRange={validRange}
75
- headerLayout={props.headerLayout ?? 'docked'}
75
+ headerLayout={headerLayout}
76
76
  showOutsideDays={showOutsideDays}
77
77
  />
78
78
  );
@@ -10,14 +10,14 @@ import {
10
10
  } from 'react-native';
11
11
 
12
12
  import { getRegisteredComponentWithFallback } from '../../core';
13
- import { format } from '../../utils/date-fns';
14
13
  import { DateField } from '../DateField';
14
+ import type { DatePickerInlineProps } from '../DatePickerInline/DatePickerInline';
15
15
  import { IconButton } from '../IconButton';
16
16
  import { Modal, type ModalProps } from '../Modal';
17
17
  import { Portal } from '../Portal';
18
18
  import { Text } from '../Text';
19
19
  import { TextInput } from '../TextInput';
20
- import type { DatePickerContextType, DatePickerValue } from './context';
20
+ import type { DatePickerContextType, DatePickerLocale, DatePickerValue } from './context';
21
21
  import {
22
22
  DatePickerContext,
23
23
  useDatePickerContext,
@@ -42,7 +42,13 @@ export type DatePickerModalProps = Omit<ModalProps, 'children' | 'isOpen' | 'onC
42
42
  cancelLabel?: string;
43
43
  editIcon?: string;
44
44
  calendarIcon?: string;
45
- headerLayout?: 'inline' | 'docked';
45
+ emptySummaryLabel?: string;
46
+ editingSummaryLabel?: string;
47
+ dateInputLabel?: string;
48
+ showCalendarAccessibilityLabel?: string;
49
+ enterDateManuallyAccessibilityLabel?: string;
50
+ locale?: DatePickerLocale;
51
+ headerLayout?: DatePickerInlineProps['headerLayout'];
46
52
  /** Override the surface default draft mode. Modal defaults to `true` (staged commit). */
47
53
  draft?: boolean;
48
54
  };
@@ -59,6 +65,11 @@ function DatePickerModalBody({
59
65
  cancelLabel = 'Cancel',
60
66
  editIcon = 'pencil',
61
67
  calendarIcon = 'calendar',
68
+ emptySummaryLabel = 'Select date',
69
+ editingSummaryLabel = 'Enter dates',
70
+ dateInputLabel = 'Date',
71
+ showCalendarAccessibilityLabel = 'Show calendar',
72
+ enterDateManuallyAccessibilityLabel = 'Enter date manually',
62
73
  headerLayout,
63
74
  ...rest
64
75
  }: BodyProps) {
@@ -97,15 +108,19 @@ function DatePickerModalBody({
97
108
  : ctx.draftValue;
98
109
 
99
110
  const summaryLabel = useMemo(() => {
100
- if (editing) return 'Enter dates';
101
- if (!draft) return 'Select date';
102
- return format(draft, 'EEE, MMM d');
103
- }, [draft, editing]);
111
+ if (editing) return editingSummaryLabel;
112
+ if (!draft) return emptySummaryLabel;
113
+ return new Intl.DateTimeFormat(ctx.locale, {
114
+ weekday: 'short',
115
+ month: 'short',
116
+ day: 'numeric',
117
+ }).format(draft);
118
+ }, [ctx.locale, draft, editing, editingSummaryLabel, emptySummaryLabel]);
104
119
 
105
120
  const body = editing ? (
106
121
  <View style={datePickerModalStyles.inputContainer}>
107
122
  <DateField autoFocus>
108
- <TextInput.Label>Date</TextInput.Label>
123
+ <TextInput.Label>{dateInputLabel}</TextInput.Label>
109
124
  </DateField>
110
125
  </View>
111
126
  ) : (
@@ -139,7 +154,9 @@ function DatePickerModalBody({
139
154
  <IconButton
140
155
  name={editing ? calendarIcon : editIcon}
141
156
  accessibilityLabel={
142
- editing ? 'Show calendar' : 'Enter date manually'
157
+ editing
158
+ ? showCalendarAccessibilityLabel
159
+ : enterDateManuallyAccessibilityLabel
143
160
  }
144
161
  onPress={() => setEditing(prev => !prev)}
145
162
  style={datePickerModalStyles.modeToggle}
@@ -161,31 +178,24 @@ function DatePickerModalBody({
161
178
  function DatePickerModalLayer({
162
179
  base,
163
180
  draft: draftProp,
164
- bodyProps,
165
- }: {
181
+ ...rest
182
+ }: BodyProps & {
166
183
  base: DatePickerContextType;
167
184
  draft: boolean | undefined;
168
- bodyProps: BodyProps;
169
185
  }) {
170
186
  const effectiveDraft = draftProp ?? base.providerDraft ?? true;
171
187
  const ctx = useMemo(() => withDraftLayer(base, effectiveDraft), [base, effectiveDraft]);
172
188
  if (!base.open) return null;
173
189
  return (
174
190
  <DatePickerContext value={ctx}>
175
- <DatePickerModalBody {...bodyProps} />
191
+ <DatePickerModalBody {...rest} />
176
192
  </DatePickerContext>
177
193
  );
178
194
  }
179
195
 
180
- function DatePickerModalAdapter({
181
- draft,
182
- bodyProps,
183
- }: {
184
- draft: boolean | undefined;
185
- bodyProps: BodyProps;
186
- }) {
196
+ function DatePickerModalAdapter({ draft, ...rest }: BodyProps & { draft: boolean | undefined }) {
187
197
  const base = useDatePickerContext();
188
- return <DatePickerModalLayer base={base} draft={draft} bodyProps={bodyProps} />;
198
+ return <DatePickerModalLayer base={base} draft={draft} {...rest} />;
189
199
  }
190
200
 
191
201
  function DatePickerModalInner({
@@ -193,6 +203,7 @@ function DatePickerModalInner({
193
203
  onClose: onCloseProp,
194
204
  value: valueProp,
195
205
  onChange: onChangeProp,
206
+ locale,
196
207
  draft: draftProp,
197
208
  ...rest
198
209
  }: DatePickerModalProps) {
@@ -206,7 +217,7 @@ function DatePickerModalInner({
206
217
  );
207
218
 
208
219
  if (outer) {
209
- return <DatePickerModalLayer base={outer} draft={draftProp} bodyProps={rest} />;
220
+ return <DatePickerModalLayer base={outer} draft={draftProp} {...rest} />;
210
221
  }
211
222
 
212
223
  return (
@@ -214,8 +225,9 @@ function DatePickerModalInner({
214
225
  value={valueProp}
215
226
  onChange={onChangeProp}
216
227
  open={isOpenProp}
228
+ locale={locale}
217
229
  onOpenChange={onOpenChange}>
218
- <DatePickerModalAdapter draft={draftProp} bodyProps={rest} />
230
+ <DatePickerModalAdapter draft={draftProp} {...rest} />
219
231
  </DatePickerProvider>
220
232
  );
221
233
  }
@@ -4,7 +4,13 @@ import { memo, useCallback, useMemo, useRef, useState } from 'react';
4
4
  import { getRegisteredComponentWithFallback } from '../../core';
5
5
  import { useControlledValue } from '../../hooks';
6
6
  import type { ValidRangeType } from '../DatePickerInline';
7
- import type { DatePickerContextType, DatePickerMode, DatePickerValue, RangeValue } from './context';
7
+ import type {
8
+ DatePickerContextType,
9
+ DatePickerLocale,
10
+ DatePickerMode,
11
+ DatePickerValue,
12
+ RangeValue,
13
+ } from './context';
8
14
  import { DatePickerContext, withDraftLayer } from './context';
9
15
 
10
16
  const DATE_FORMAT_BY_MODE: Record<DatePickerMode, string> = {
@@ -30,7 +36,7 @@ export type DatePickerProviderProps = {
30
36
  defaultOpen?: boolean;
31
37
  onOpenChange?: (open: boolean) => void;
32
38
 
33
- locale?: string;
39
+ locale?: DatePickerLocale;
34
40
  validRange?: ValidRangeType;
35
41
  is24Hour?: boolean;
36
42
  dateFormat?: string;
@@ -5,6 +5,7 @@ import type { ValidRangeType } from '../DatePickerInline';
5
5
  import { registerPortalContext } from '../Portal';
6
6
 
7
7
  export type DatePickerMode = 'date' | 'time' | 'datetime' | 'range';
8
+ export type DatePickerLocale = Intl.LocalesArgument;
8
9
 
9
10
  export type DateValue = Date | null;
10
11
  export type RangeValue = { start: Date | null; end: Date | null };
@@ -35,7 +36,7 @@ export type DatePickerContextType = {
35
36
  open: boolean;
36
37
  setOpen: (open: boolean) => void;
37
38
  triggerRef: RefObject<any>;
38
- locale?: string;
39
+ locale?: DatePickerLocale;
39
40
  validRange?: ValidRangeType;
40
41
  is24Hour: boolean;
41
42
  dateFormat: string;
@@ -16,6 +16,7 @@ export const DatePicker = {
16
16
 
17
17
  export type {
18
18
  DatePickerContextType,
19
+ DatePickerLocale,
19
20
  DatePickerMode,
20
21
  DatePickerValue,
21
22
  DateValue,
@@ -1,16 +1,17 @@
1
- import { add, format, setYear } from 'date-fns';
1
+ import { add, setYear } from 'date-fns';
2
2
  import { memo, useCallback, useMemo } from 'react';
3
3
  import { View, type ViewStyle } from 'react-native';
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
5
 
6
- import { useDatePickerStoreRef, useDatePickerStoreValue } from './DatePickerContext';
6
+ import type { DatePickerLocale } from '../DatePicker/context';
7
7
  import type { DisableWeekDaysType } from './dateUtils';
8
8
  import DayNames from './DayNames';
9
9
  import HeaderItem from './HeaderItem';
10
+ import { useDatePickerInlineStoreRef, useDatePickerInlineStoreValue } from './store';
10
11
  import { datePickerHeaderStyles } from './utils';
11
12
 
12
13
  export type DockedHeaderProps = {
13
- locale?: string;
14
+ locale?: DatePickerLocale;
14
15
  scrollMode: 'horizontal' | 'vertical';
15
16
  disableWeekDays?: DisableWeekDaysType;
16
17
  style?: ViewStyle;
@@ -22,16 +23,19 @@ function DatePickerDockedHeader({
22
23
  disableWeekDays,
23
24
  style: styleProp,
24
25
  }: DockedHeaderProps) {
25
- const setStore = useDatePickerStoreRef().set;
26
- const { localDate, pickerType } = useDatePickerStoreValue(state => ({
26
+ const setStore = useDatePickerInlineStoreRef().set;
27
+ const { localDate, pickerType } = useDatePickerInlineStoreValue(state => ({
27
28
  localDate: state.localDate,
28
29
  pickerType: state.pickerType,
29
30
  }));
30
31
  const isHorizontal = scrollMode === 'horizontal';
31
32
 
32
33
  const { monthName, year } = useMemo(
33
- () => ({ monthName: format(localDate, 'LLL'), year: localDate.getFullYear() }),
34
- [localDate],
34
+ () => ({
35
+ monthName: new Intl.DateTimeFormat(locale, { month: 'short' }).format(localDate),
36
+ year: localDate.getFullYear(),
37
+ }),
38
+ [localDate, locale],
35
39
  );
36
40
 
37
41
  const handleMonthDropdown = useCallback(() => {
@@ -25,7 +25,7 @@ const DatePickerInline = ({
25
25
  onChange,
26
26
  locale = 'en',
27
27
  mode = 'single',
28
- headerLayout = 'inline',
28
+ headerLayout = 'default',
29
29
  HeaderComponent,
30
30
  containerStyle: containerStyleProp,
31
31
  ...rest
@@ -2,10 +2,10 @@ import { memo, useCallback, useEffect, useMemo } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
 
4
4
  import { useLatest } from '../../hooks';
5
- import { defaultValue, Provider, useDatePickerStore } from './DatePickerContext';
6
5
  import { areDatesOnSameDay, dateToUnix, getEndOfDay, getInitialIndex } from './dateUtils';
7
6
  import Month from './Month';
8
7
  import MonthPicker from './MonthPicker';
8
+ import { defaultValue, Provider, useDatePickerInlineStore } from './store';
9
9
  import Swiper from './Swiper';
10
10
  import type {
11
11
  CalendarDate,
@@ -46,7 +46,7 @@ function DatePickerInlineBaseChild(props: DatePickerInlineBaseProps) {
46
46
  showOutsideDays,
47
47
  headerLayout,
48
48
  } = props;
49
- const [pickerType, setStore] = useDatePickerStore(state => state.pickerType);
49
+ const [pickerType, setStore] = useDatePickerInlineStore(state => state.pickerType);
50
50
 
51
51
  const scrollMode = mode === 'range' || mode === 'multiple' ? 'vertical' : 'horizontal';
52
52
  const isHorizontal = scrollMode === 'horizontal';
@@ -173,7 +173,7 @@ function DatePickerInlineBaseChild(props: DatePickerInlineBaseProps) {
173
173
  {isHorizontal && pickerType === 'year' && (
174
174
  <YearPicker layout={headerLayout === 'docked' ? 'list' : 'grid'} />
175
175
  )}
176
- {isHorizontal && pickerType === 'month' && <MonthPicker />}
176
+ {isHorizontal && pickerType === 'month' && <MonthPicker locale={locale} />}
177
177
  </View>
178
178
  );
179
179
  }
@@ -1,11 +1,12 @@
1
- import { add, format } from 'date-fns';
1
+ import { add, setYear } from 'date-fns';
2
2
  import { memo, useCallback, useMemo } from 'react';
3
3
  import { type StyleProp, View, type ViewStyle } from 'react-native';
4
4
 
5
- import { useDatePickerStore, useDatePickerStoreValue } from './DatePickerContext';
5
+ import type { DatePickerLocale } from '../DatePicker/context';
6
6
  import type { DisableWeekDaysType } from './dateUtils';
7
7
  import DayNames from './DayNames';
8
8
  import HeaderItem from './HeaderItem';
9
+ import { useDatePickerInlineStore, useDatePickerInlineStoreValue } from './store';
9
10
  import { datePickerHeaderStyles, dayNamesHeight } from './utils';
10
11
 
11
12
  const buttonContainerHeight = 56;
@@ -13,7 +14,7 @@ const buttonContainerMarginTop = 4;
13
14
  const buttonContainerMarginBottom = 8;
14
15
 
15
16
  export type CalendarHeaderProps = {
16
- locale?: string;
17
+ locale?: DatePickerLocale;
17
18
  scrollMode: 'horizontal' | 'vertical';
18
19
  disableWeekDays?: DisableWeekDaysType;
19
20
  style?: ViewStyle;
@@ -25,18 +26,21 @@ function DatePickerInlineHeader({
25
26
  disableWeekDays,
26
27
  style: styleProp,
27
28
  }: CalendarHeaderProps) {
28
- const [_, setStore] = useDatePickerStore(state => state);
29
- const { localDate, isYearPickerType } = useDatePickerStoreValue(state => ({
29
+ const [_, setStore] = useDatePickerInlineStore(state => state);
30
+ const { localDate, pickerType } = useDatePickerInlineStoreValue(state => ({
30
31
  localDate: state.localDate,
31
- isYearPickerType: state.pickerType === 'year',
32
+ pickerType: state.pickerType,
32
33
  }));
33
34
  const isHorizontal = scrollMode === 'horizontal';
34
35
 
35
36
  const { monthName, year } = useMemo(() => {
36
37
  const y = localDate.getFullYear();
37
38
 
38
- return { monthName: format(localDate, 'LLLL'), year: y };
39
- }, [localDate]);
39
+ return {
40
+ monthName: new Intl.DateTimeFormat(locale, { month: 'long' }).format(localDate),
41
+ year: y,
42
+ };
43
+ }, [localDate, locale]);
40
44
 
41
45
  const { containerStyle } = useMemo(() => {
42
46
  // const { datePickerHeader, buttonContainer, buttonWrapper, spacer, ...rest } =
@@ -54,36 +58,62 @@ function DatePickerInlineHeader({
54
58
  };
55
59
  }, [styleProp]);
56
60
 
61
+ const handleOnMonthPress = useCallback(() => {
62
+ isHorizontal &&
63
+ setStore(prev => ({
64
+ pickerType: prev.pickerType === 'month' ? undefined : 'month',
65
+ }));
66
+ }, [isHorizontal, setStore]);
67
+
57
68
  const handleOnYearPress = useCallback(() => {
58
- isHorizontal && setStore(prev => ({ pickerType: prev.pickerType ? undefined : 'year' }));
69
+ isHorizontal &&
70
+ setStore(prev => ({
71
+ pickerType: prev.pickerType === 'year' ? undefined : 'year',
72
+ }));
59
73
  }, [isHorizontal, setStore]);
60
74
 
61
- const handleOnPrev = useCallback(() => {
75
+ const handleMonthPrev = useCallback(() => {
62
76
  setStore(prev => ({ localDate: add(prev.localDate, { months: -1 }) }));
63
77
  }, [setStore]);
64
78
 
65
- const handleOnNext = useCallback(() => {
79
+ const handleMonthNext = useCallback(() => {
66
80
  setStore(prev => ({ localDate: add(prev.localDate, { months: 1 }) }));
67
81
  }, [setStore]);
68
82
 
83
+ const handleYearPrev = useCallback(() => {
84
+ setStore(prev => ({
85
+ localDate: setYear(prev.localDate, prev.localDate.getFullYear() - 1),
86
+ }));
87
+ }, [setStore]);
88
+
89
+ const handleYearNext = useCallback(() => {
90
+ setStore(prev => ({
91
+ localDate: setYear(prev.localDate, prev.localDate.getFullYear() + 1),
92
+ }));
93
+ }, [setStore]);
94
+
69
95
  return (
70
96
  <View pointerEvents={'box-none'}>
71
97
  <>
72
98
  {isHorizontal && (
73
99
  <View style={containerStyle}>
74
100
  <HeaderItem
75
- onPressDropdown={handleOnYearPress}
76
- type="year"
77
- value={`${monthName} ${year}`}
78
- pickerType="year"
79
- selecting={isYearPickerType}
101
+ onPrev={handleMonthPrev}
102
+ onNext={handleMonthNext}
103
+ onPressDropdown={handleOnMonthPress}
104
+ type="month"
105
+ value={monthName}
106
+ pickerType={pickerType}
107
+ selecting={pickerType === 'month'}
80
108
  />
81
109
  <HeaderItem
82
- onNext={handleOnNext}
83
- onPrev={handleOnPrev}
84
- selecting={false}
110
+ onNext={handleYearNext}
111
+ onPrev={handleYearPrev}
112
+ onPressDropdown={handleOnYearPress}
113
+ type="year"
114
+ selecting={pickerType === 'year'}
85
115
  value={year}
86
- pickerType="year"
116
+ pickerType={pickerType}
87
117
  />
88
118
  </View>
89
119
  )}
@@ -1,23 +1,26 @@
1
- import { memo } from 'react';
1
+ import { memo, useMemo } from 'react';
2
2
  import { View } from 'react-native';
3
3
 
4
- import { addDays, format, startOfWeek } from '../../utils/date-fns';
4
+ import { addDays, startOfWeek } from '../../utils/date-fns';
5
+ import type { DatePickerLocale } from '../DatePicker/context';
5
6
  import { type DisableWeekDaysType, showWeekDay } from './dateUtils';
6
7
  import DayName from './DayName';
7
8
  import { dateDayNameStyles } from './utils';
8
9
 
9
- const shortDayNames = (() => {
10
- const firstDOW = startOfWeek(new Date());
11
- return Array.from(Array(7)).map((_, i) => format(addDays(firstDOW, i), 'EEEEE'));
12
- })();
13
-
14
10
  function DayNames({
15
11
  disableWeekDays,
16
- }: // locale,
17
- {
12
+ locale,
13
+ }: {
18
14
  disableWeekDays?: DisableWeekDaysType;
19
- locale?: string;
15
+ locale?: DatePickerLocale;
20
16
  }) {
17
+ const shortDayNames = useMemo(() => {
18
+ const firstDOW = startOfWeek(new Date());
19
+ return Array.from(Array(7)).map((_, i) =>
20
+ new Intl.DateTimeFormat(locale, { weekday: 'narrow' }).format(addDays(firstDOW, i)),
21
+ );
22
+ }, [locale]);
23
+
21
24
  return (
22
25
  <View style={dateDayNameStyles.container} pointerEvents={'none'}>
23
26
  {shortDayNames
@@ -6,7 +6,7 @@ import { Icon } from '../Icon';
6
6
  import { IconButton } from '../IconButton';
7
7
  import { Text } from '../Text';
8
8
  import { TouchableRipple } from '../TouchableRipple';
9
- import { useDatePickerStoreValue } from './DatePickerContext';
9
+ import { useDatePickerInlineStoreValue } from './store';
10
10
 
11
11
  function HeaderItem({
12
12
  value,
@@ -25,7 +25,7 @@ function HeaderItem({
25
25
  onNext?: (type: 'month' | 'year' | undefined) => void;
26
26
  onPrev?: (type: 'month' | 'year' | undefined) => void;
27
27
  }) {
28
- const { startDateYear, endDateYear } = useDatePickerStoreValue(state => ({
28
+ const { startDateYear, endDateYear } = useDatePickerInlineStoreValue(state => ({
29
29
  startDateYear: state.startDateYear,
30
30
  endDateYear: state.endDateYear,
31
31
  }));