@cdx-ui/components 0.0.1-beta.140 → 0.0.1-beta.142

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 (148) hide show
  1. package/lib/commonjs/components/BottomSheet/index.js +8 -2
  2. package/lib/commonjs/components/BottomSheet/index.js.map +1 -1
  3. package/lib/commonjs/components/Calendar/CalendarMonthHeader.js +96 -0
  4. package/lib/commonjs/components/Calendar/CalendarMonthHeader.js.map +1 -0
  5. package/lib/commonjs/components/Calendar/CalendarYearPickerGrid.js +74 -0
  6. package/lib/commonjs/components/Calendar/CalendarYearPickerGrid.js.map +1 -0
  7. package/lib/commonjs/components/Calendar/flashCalendarAdapter.js +298 -0
  8. package/lib/commonjs/components/Calendar/flashCalendarAdapter.js.map +1 -0
  9. package/lib/commonjs/components/Calendar/index.js +149 -0
  10. package/lib/commonjs/components/Calendar/index.js.map +1 -0
  11. package/lib/commonjs/components/Calendar/styles.js +70 -0
  12. package/lib/commonjs/components/Calendar/styles.js.map +1 -0
  13. package/lib/commonjs/components/DateField/index.js +147 -0
  14. package/lib/commonjs/components/DateField/index.js.map +1 -0
  15. package/lib/commonjs/components/DateField/styles.js +34 -0
  16. package/lib/commonjs/components/DateField/styles.js.map +1 -0
  17. package/lib/commonjs/components/DatePicker/index.js +163 -0
  18. package/lib/commonjs/components/DatePicker/index.js.map +1 -0
  19. package/lib/commonjs/components/DateRangePicker/index.js +177 -0
  20. package/lib/commonjs/components/DateRangePicker/index.js.map +1 -0
  21. package/lib/commonjs/components/DateTimeField/index.js +96 -0
  22. package/lib/commonjs/components/DateTimeField/index.js.map +1 -0
  23. package/lib/commonjs/components/DateTimeList/index.js +180 -0
  24. package/lib/commonjs/components/DateTimeList/index.js.map +1 -0
  25. package/lib/commonjs/components/DateTimeList/styles.js +55 -0
  26. package/lib/commonjs/components/DateTimeList/styles.js.map +1 -0
  27. package/lib/commonjs/components/DateTimePicker/index.js +187 -0
  28. package/lib/commonjs/components/DateTimePicker/index.js.map +1 -0
  29. package/lib/commonjs/components/MonthCalendar/index.js +229 -0
  30. package/lib/commonjs/components/MonthCalendar/index.js.map +1 -0
  31. package/lib/commonjs/components/MonthPicker/index.js +163 -0
  32. package/lib/commonjs/components/MonthPicker/index.js.map +1 -0
  33. package/lib/commonjs/components/RangeCalendar/index.js +165 -0
  34. package/lib/commonjs/components/RangeCalendar/index.js.map +1 -0
  35. package/lib/commonjs/components/index.js +120 -0
  36. package/lib/commonjs/components/index.js.map +1 -1
  37. package/lib/commonjs/components/picker/PickerTrigger.js +49 -0
  38. package/lib/commonjs/components/picker/PickerTrigger.js.map +1 -0
  39. package/lib/commonjs/components/picker/styles.js +15 -0
  40. package/lib/commonjs/components/picker/styles.js.map +1 -0
  41. package/lib/commonjs/components/picker/surfaces.js +141 -0
  42. package/lib/commonjs/components/picker/surfaces.js.map +1 -0
  43. package/lib/module/components/BottomSheet/index.js +8 -2
  44. package/lib/module/components/BottomSheet/index.js.map +1 -1
  45. package/lib/module/components/Calendar/CalendarMonthHeader.js +92 -0
  46. package/lib/module/components/Calendar/CalendarMonthHeader.js.map +1 -0
  47. package/lib/module/components/Calendar/CalendarYearPickerGrid.js +70 -0
  48. package/lib/module/components/Calendar/CalendarYearPickerGrid.js.map +1 -0
  49. package/lib/module/components/Calendar/flashCalendarAdapter.js +290 -0
  50. package/lib/module/components/Calendar/flashCalendarAdapter.js.map +1 -0
  51. package/lib/module/components/Calendar/index.js +145 -0
  52. package/lib/module/components/Calendar/index.js.map +1 -0
  53. package/lib/module/components/Calendar/styles.js +66 -0
  54. package/lib/module/components/Calendar/styles.js.map +1 -0
  55. package/lib/module/components/DateField/index.js +143 -0
  56. package/lib/module/components/DateField/index.js.map +1 -0
  57. package/lib/module/components/DateField/styles.js +30 -0
  58. package/lib/module/components/DateField/styles.js.map +1 -0
  59. package/lib/module/components/DatePicker/index.js +159 -0
  60. package/lib/module/components/DatePicker/index.js.map +1 -0
  61. package/lib/module/components/DateRangePicker/index.js +173 -0
  62. package/lib/module/components/DateRangePicker/index.js.map +1 -0
  63. package/lib/module/components/DateTimeField/index.js +92 -0
  64. package/lib/module/components/DateTimeField/index.js.map +1 -0
  65. package/lib/module/components/DateTimeList/index.js +176 -0
  66. package/lib/module/components/DateTimeList/index.js.map +1 -0
  67. package/lib/module/components/DateTimeList/styles.js +52 -0
  68. package/lib/module/components/DateTimeList/styles.js.map +1 -0
  69. package/lib/module/components/DateTimePicker/index.js +183 -0
  70. package/lib/module/components/DateTimePicker/index.js.map +1 -0
  71. package/lib/module/components/MonthCalendar/index.js +225 -0
  72. package/lib/module/components/MonthCalendar/index.js.map +1 -0
  73. package/lib/module/components/MonthPicker/index.js +159 -0
  74. package/lib/module/components/MonthPicker/index.js.map +1 -0
  75. package/lib/module/components/RangeCalendar/index.js +161 -0
  76. package/lib/module/components/RangeCalendar/index.js.map +1 -0
  77. package/lib/module/components/index.js +10 -0
  78. package/lib/module/components/index.js.map +1 -1
  79. package/lib/module/components/picker/PickerTrigger.js +45 -0
  80. package/lib/module/components/picker/PickerTrigger.js.map +1 -0
  81. package/lib/module/components/picker/styles.js +12 -0
  82. package/lib/module/components/picker/styles.js.map +1 -0
  83. package/lib/module/components/picker/surfaces.js +135 -0
  84. package/lib/module/components/picker/surfaces.js.map +1 -0
  85. package/lib/typescript/components/BottomSheet/index.d.ts +2 -2
  86. package/lib/typescript/components/BottomSheet/index.d.ts.map +1 -1
  87. package/lib/typescript/components/Calendar/CalendarMonthHeader.d.ts +14 -0
  88. package/lib/typescript/components/Calendar/CalendarMonthHeader.d.ts.map +1 -0
  89. package/lib/typescript/components/Calendar/CalendarYearPickerGrid.d.ts +12 -0
  90. package/lib/typescript/components/Calendar/CalendarYearPickerGrid.d.ts.map +1 -0
  91. package/lib/typescript/components/Calendar/flashCalendarAdapter.d.ts +18 -0
  92. package/lib/typescript/components/Calendar/flashCalendarAdapter.d.ts.map +1 -0
  93. package/lib/typescript/components/Calendar/index.d.ts +19 -0
  94. package/lib/typescript/components/Calendar/index.d.ts.map +1 -0
  95. package/lib/typescript/components/Calendar/styles.d.ts +23 -0
  96. package/lib/typescript/components/Calendar/styles.d.ts.map +1 -0
  97. package/lib/typescript/components/DateField/index.d.ts +43 -0
  98. package/lib/typescript/components/DateField/index.d.ts.map +1 -0
  99. package/lib/typescript/components/DateField/styles.d.ts +15 -0
  100. package/lib/typescript/components/DateField/styles.d.ts.map +1 -0
  101. package/lib/typescript/components/DatePicker/index.d.ts +40 -0
  102. package/lib/typescript/components/DatePicker/index.d.ts.map +1 -0
  103. package/lib/typescript/components/DateRangePicker/index.d.ts +45 -0
  104. package/lib/typescript/components/DateRangePicker/index.d.ts.map +1 -0
  105. package/lib/typescript/components/DateTimeField/index.d.ts +29 -0
  106. package/lib/typescript/components/DateTimeField/index.d.ts.map +1 -0
  107. package/lib/typescript/components/DateTimeList/index.d.ts +19 -0
  108. package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -0
  109. package/lib/typescript/components/DateTimeList/styles.d.ts +17 -0
  110. package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -0
  111. package/lib/typescript/components/DateTimePicker/index.d.ts +40 -0
  112. package/lib/typescript/components/DateTimePicker/index.d.ts.map +1 -0
  113. package/lib/typescript/components/MonthCalendar/index.d.ts +29 -0
  114. package/lib/typescript/components/MonthCalendar/index.d.ts.map +1 -0
  115. package/lib/typescript/components/MonthPicker/index.d.ts +40 -0
  116. package/lib/typescript/components/MonthPicker/index.d.ts.map +1 -0
  117. package/lib/typescript/components/RangeCalendar/index.d.ts +19 -0
  118. package/lib/typescript/components/RangeCalendar/index.d.ts.map +1 -0
  119. package/lib/typescript/components/index.d.ts +10 -0
  120. package/lib/typescript/components/index.d.ts.map +1 -1
  121. package/lib/typescript/components/picker/PickerTrigger.d.ts +25 -0
  122. package/lib/typescript/components/picker/PickerTrigger.d.ts.map +1 -0
  123. package/lib/typescript/components/picker/styles.d.ts +4 -0
  124. package/lib/typescript/components/picker/styles.d.ts.map +1 -0
  125. package/lib/typescript/components/picker/surfaces.d.ts +28 -0
  126. package/lib/typescript/components/picker/surfaces.d.ts.map +1 -0
  127. package/package.json +6 -4
  128. package/src/components/BottomSheet/index.tsx +7 -1
  129. package/src/components/Calendar/CalendarMonthHeader.tsx +124 -0
  130. package/src/components/Calendar/CalendarYearPickerGrid.tsx +101 -0
  131. package/src/components/Calendar/flashCalendarAdapter.ts +362 -0
  132. package/src/components/Calendar/index.tsx +151 -0
  133. package/src/components/Calendar/styles.ts +80 -0
  134. package/src/components/DateField/index.tsx +196 -0
  135. package/src/components/DateField/styles.ts +51 -0
  136. package/src/components/DatePicker/index.tsx +187 -0
  137. package/src/components/DateRangePicker/index.tsx +202 -0
  138. package/src/components/DateTimeField/index.tsx +128 -0
  139. package/src/components/DateTimeList/index.tsx +187 -0
  140. package/src/components/DateTimeList/styles.ts +59 -0
  141. package/src/components/DateTimePicker/index.tsx +233 -0
  142. package/src/components/MonthCalendar/index.tsx +252 -0
  143. package/src/components/MonthPicker/index.tsx +188 -0
  144. package/src/components/RangeCalendar/index.tsx +162 -0
  145. package/src/components/index.ts +10 -0
  146. package/src/components/picker/PickerTrigger.tsx +65 -0
  147. package/src/components/picker/styles.ts +17 -0
  148. package/src/components/picker/surfaces.tsx +174 -0
@@ -0,0 +1,252 @@
1
+ import { forwardRef, useCallback, useMemo, type ReactNode } from 'react';
2
+ import { Pressable, Text, View, type ViewProps } from 'react-native';
3
+ import {
4
+ dataAttributes,
5
+ mergeDataAttributes,
6
+ MonthCalendarCell as MonthCalendarCellPrimitive,
7
+ MonthCalendarRoot as MonthCalendarRootPrimitive,
8
+ MonthCalendarYearCell as MonthCalendarYearCellPrimitive,
9
+ useMonthCalendarContext,
10
+ type MonthCalendarRootProps,
11
+ } from '@cdx-ui/primitives';
12
+ import {
13
+ compareMonthIds,
14
+ formatMonthCalendarMonth,
15
+ formatMonthCalendarYear,
16
+ formatMonthId,
17
+ formatSelectYear,
18
+ parseMonthId,
19
+ } from '@cdx-ui/formatters';
20
+ import { cn } from '@cdx-ui/utils';
21
+ import { ChevronLeft, ChevronRight, KeyboardArrowDown } from '@cdx-ui/icons';
22
+ import { Icon } from '../Icon';
23
+ import { IconButton } from '../IconButton';
24
+ import {
25
+ calendarMonthHeaderIconVariants,
26
+ calendarMonthHeaderLabelVariants,
27
+ calendarMonthHeaderNavVariants,
28
+ calendarMonthHeaderToggleVariants,
29
+ calendarMonthHeaderVariants,
30
+ calendarPillCellTextVariants,
31
+ calendarPillCellVariants,
32
+ calendarRootVariants,
33
+ calendarYearPickerGridVariants,
34
+ } from '../Calendar/styles';
35
+
36
+ export interface MonthCalendarProps
37
+ extends Omit<MonthCalendarRootProps, 'children'>, Omit<ViewProps, 'children'> {
38
+ className?: string;
39
+ children?: ReactNode;
40
+ }
41
+
42
+ function MonthCalendarDefaultBody() {
43
+ return (
44
+ <>
45
+ <MonthCalendarHeader />
46
+ <MonthCalendarGrid />
47
+ </>
48
+ );
49
+ }
50
+
51
+ const MonthCalendarRoot = forwardRef<View, MonthCalendarProps>(
52
+ ({ className, children, style, ...props }, ref) => (
53
+ <MonthCalendarRootPrimitive
54
+ ref={ref}
55
+ className={cn(calendarRootVariants(), className)}
56
+ style={style}
57
+ {...props}
58
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'month-calendar' }))}
59
+ >
60
+ {children ?? <MonthCalendarDefaultBody />}
61
+ </MonthCalendarRootPrimitive>
62
+ ),
63
+ );
64
+
65
+ MonthCalendarRoot.displayName = 'MonthCalendar';
66
+
67
+ const MonthCalendarHeader = forwardRef<View, ViewProps>(({ className, style, ...props }, ref) => {
68
+ const { view, onViewChange, visibleYear, onVisibleYearChange, disabled, labels } =
69
+ useMonthCalendarContext();
70
+ const isYearView = view === 'year';
71
+
72
+ const shiftYear = useCallback(
73
+ (delta: number) => {
74
+ if (disabled) {
75
+ return;
76
+ }
77
+ onVisibleYearChange(visibleYear + delta);
78
+ },
79
+ [disabled, onVisibleYearChange, visibleYear],
80
+ );
81
+
82
+ const toggleYearView = useCallback(() => {
83
+ if (disabled) {
84
+ return;
85
+ }
86
+ onViewChange(isYearView ? 'month' : 'year');
87
+ }, [disabled, isYearView, onViewChange]);
88
+
89
+ return (
90
+ <View
91
+ ref={ref}
92
+ className={cn(calendarMonthHeaderVariants(), className)}
93
+ style={style}
94
+ {...props}
95
+ >
96
+ <Pressable
97
+ accessibilityRole="button"
98
+ accessibilityLabel={isYearView ? labels.showMonths : labels.showYearPicker}
99
+ disabled={disabled}
100
+ onPress={toggleYearView}
101
+ className={calendarMonthHeaderToggleVariants()}
102
+ >
103
+ <Text className={calendarMonthHeaderLabelVariants({ isYearView })} numberOfLines={1}>
104
+ {formatMonthCalendarYear(visibleYear)}
105
+ </Text>
106
+ <Icon
107
+ as={isYearView ? KeyboardArrowDown : ChevronRight}
108
+ className={calendarMonthHeaderIconVariants({ isYearView })}
109
+ />
110
+ </Pressable>
111
+
112
+ {!isYearView ? (
113
+ <View className={calendarMonthHeaderNavVariants()}>
114
+ <IconButton
115
+ as={ChevronLeft}
116
+ variant="ghost"
117
+ color="neutral"
118
+ size="small"
119
+ accessibilityLabel={labels.previousYear}
120
+ isDisabled={disabled}
121
+ onPress={() => shiftYear(-1)}
122
+ />
123
+ <IconButton
124
+ as={ChevronRight}
125
+ variant="ghost"
126
+ color="neutral"
127
+ size="small"
128
+ accessibilityLabel={labels.nextYear}
129
+ isDisabled={disabled}
130
+ onPress={() => shiftYear(1)}
131
+ />
132
+ </View>
133
+ ) : null}
134
+ </View>
135
+ );
136
+ });
137
+
138
+ MonthCalendarHeader.displayName = 'MonthCalendar.Header';
139
+
140
+ const MonthCalendarGrid = forwardRef<View, ViewProps>(({ className, style, ...props }, ref) => {
141
+ const { view, visibleYear } = useMonthCalendarContext();
142
+
143
+ const years = useMemo(
144
+ () => Array.from({ length: 12 }, (_, i) => visibleYear - 5 + i),
145
+ [visibleYear],
146
+ );
147
+
148
+ if (view === 'year') {
149
+ return (
150
+ <View
151
+ ref={ref}
152
+ className={cn(calendarYearPickerGridVariants(), className)}
153
+ style={style}
154
+ accessibilityRole="none"
155
+ {...props}
156
+ >
157
+ {years.map((year) => (
158
+ <MonthCalendarYearCell key={year} year={year} />
159
+ ))}
160
+ </View>
161
+ );
162
+ }
163
+
164
+ return (
165
+ <View
166
+ ref={ref}
167
+ className={cn(calendarYearPickerGridVariants(), className)}
168
+ style={style}
169
+ accessibilityRole="none"
170
+ {...props}
171
+ >
172
+ {Array.from({ length: 12 }, (_, i) => i + 1).map((month) => (
173
+ <MonthCalendarCell key={month} month={month} />
174
+ ))}
175
+ </View>
176
+ );
177
+ });
178
+
179
+ MonthCalendarGrid.displayName = 'MonthCalendar.Grid';
180
+
181
+ const MonthCalendarCell = forwardRef<
182
+ View,
183
+ { month: number; className?: string; children?: ReactNode }
184
+ >(({ month, className, children, ...props }, ref) => {
185
+ const { visibleYear, value, locale, minValue, maxValue } = useMonthCalendarContext();
186
+ const monthId = formatMonthId({ year: visibleYear, month });
187
+ const selected = value === monthId;
188
+ const disabled =
189
+ (minValue != null && compareMonthIds(monthId, minValue) < 0) ||
190
+ (maxValue != null && compareMonthIds(monthId, maxValue) > 0);
191
+
192
+ return (
193
+ <MonthCalendarCellPrimitive
194
+ ref={ref}
195
+ month={month}
196
+ accessibilityLabel={formatMonthCalendarMonth(month, { locale })}
197
+ className={cn(calendarPillCellVariants({ selected, disabled }), className)}
198
+ {...props}
199
+ >
200
+ {children ?? (
201
+ <Text className={calendarPillCellTextVariants({ selected })}>
202
+ {formatMonthCalendarMonth(month, { locale })}
203
+ </Text>
204
+ )}
205
+ </MonthCalendarCellPrimitive>
206
+ );
207
+ });
208
+
209
+ MonthCalendarCell.displayName = 'MonthCalendar.Cell';
210
+
211
+ const MonthCalendarYearCell = forwardRef<
212
+ View,
213
+ { year: number; className?: string; children?: ReactNode }
214
+ >(({ year, className, children, ...props }, ref) => {
215
+ const { visibleYear, labels, minValue, maxValue } = useMonthCalendarContext();
216
+ const selected = visibleYear === year;
217
+ const minYear = minValue ? parseMonthId(minValue)?.year : undefined;
218
+ const maxYear = maxValue ? parseMonthId(maxValue)?.year : undefined;
219
+ const disabled = (minYear != null && year < minYear) || (maxYear != null && year > maxYear);
220
+
221
+ return (
222
+ <MonthCalendarYearCellPrimitive
223
+ ref={ref}
224
+ year={year}
225
+ accessibilityLabel={formatSelectYear(labels, year)}
226
+ className={cn(calendarPillCellVariants({ selected, disabled }), className)}
227
+ {...props}
228
+ >
229
+ {children ?? (
230
+ <Text className={calendarPillCellTextVariants({ selected })}>
231
+ {formatMonthCalendarYear(year)}
232
+ </Text>
233
+ )}
234
+ </MonthCalendarYearCellPrimitive>
235
+ );
236
+ });
237
+
238
+ MonthCalendarYearCell.displayName = 'MonthCalendar.YearCell';
239
+
240
+ type MonthCalendarCompound = typeof MonthCalendarRoot & {
241
+ Header: typeof MonthCalendarHeader;
242
+ Grid: typeof MonthCalendarGrid;
243
+ Cell: typeof MonthCalendarCell;
244
+ YearCell: typeof MonthCalendarYearCell;
245
+ };
246
+
247
+ export const MonthCalendar = Object.assign(MonthCalendarRoot, {
248
+ Header: MonthCalendarHeader,
249
+ Grid: MonthCalendarGrid,
250
+ Cell: MonthCalendarCell,
251
+ YearCell: MonthCalendarYearCell,
252
+ }) as MonthCalendarCompound;
@@ -0,0 +1,188 @@
1
+ import { forwardRef, useCallback, useMemo, type ReactNode } from 'react';
2
+ import { View, type ViewProps } from 'react-native';
3
+ import { CalendarToday } from '@cdx-ui/icons';
4
+ import {
5
+ dataAttributes,
6
+ mergeDataAttributes,
7
+ MonthPickerContext,
8
+ MonthPickerRoot as MonthPickerRootPrimitive,
9
+ useMonthPickerContext,
10
+ type MonthPickerRootProps as MonthPickerRootPrimitiveProps,
11
+ } from '@cdx-ui/primitives';
12
+ import type { MonthId } from '@cdx-ui/formatters';
13
+ import { cn, ParentContext, useParentContext } from '@cdx-ui/utils';
14
+ import { Dialog } from '../Dialog';
15
+ import { Icon } from '../Icon';
16
+ import { MonthCalendar, type MonthCalendarProps } from '../MonthCalendar';
17
+ import { PickerTrigger, type PickerTriggerProps } from '../picker/PickerTrigger';
18
+ import {
19
+ PickerBottomSheetSurface,
20
+ PickerDialogSurface,
21
+ PickerPopoverSurface,
22
+ } from '../picker/surfaces';
23
+
24
+ export interface MonthPickerProps
25
+ extends MonthPickerRootPrimitiveProps, Omit<ViewProps, 'children'> {
26
+ className?: string;
27
+ children?: ReactNode;
28
+ }
29
+
30
+ const DATE_FIELD_SCOPE = 'DATE_FIELD';
31
+
32
+ const MonthPickerRoot = forwardRef<View, MonthPickerProps>(
33
+ ({ className, children, style, ...props }, ref) => {
34
+ const parent = useParentContext();
35
+ const ctx = useMemo(
36
+ () => ({
37
+ ...parent,
38
+ [DATE_FIELD_SCOPE]: { variant: 'outline', size: 'default', fullWidth: true },
39
+ }),
40
+ [parent],
41
+ );
42
+
43
+ return (
44
+ <ParentContext.Provider value={ctx}>
45
+ <MonthPickerRootPrimitive
46
+ ref={ref}
47
+ className={cn('gap-2', className)}
48
+ style={style}
49
+ {...props}
50
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'month-picker' }))}
51
+ >
52
+ {children}
53
+ </MonthPickerRootPrimitive>
54
+ </ParentContext.Provider>
55
+ );
56
+ },
57
+ );
58
+
59
+ MonthPickerRoot.displayName = 'MonthPicker';
60
+
61
+ type MonthPickerTriggerProps = Omit<
62
+ PickerTriggerProps,
63
+ 'open' | 'setOpen' | 'disabled' | 'triggerRef' | 'accessibilityLabel'
64
+ > & {
65
+ accessibilityLabel?: string;
66
+ };
67
+
68
+ const MonthPickerTrigger = forwardRef<View, MonthPickerTriggerProps>(
69
+ ({ accessibilityLabel, ...props }, ref) => {
70
+ const { open, setOpen, disabled, triggerRef, labels } = useMonthPickerContext();
71
+ return (
72
+ <PickerTrigger
73
+ ref={ref}
74
+ open={open}
75
+ setOpen={setOpen}
76
+ disabled={disabled}
77
+ triggerRef={triggerRef}
78
+ accessibilityLabel={accessibilityLabel ?? labels.openMonthPicker}
79
+ {...props}
80
+ />
81
+ );
82
+ },
83
+ );
84
+
85
+ MonthPickerTrigger.displayName = 'MonthPicker.Trigger';
86
+
87
+ const MonthPickerTriggerIndicator = () => (
88
+ <Icon as={CalendarToday} className="text-content-secondary" />
89
+ );
90
+
91
+ MonthPickerTriggerIndicator.displayName = 'MonthPicker.TriggerIndicator';
92
+
93
+ type MonthPickerCalendarProps = Omit<
94
+ MonthCalendarProps,
95
+ 'value' | 'onValueChange' | 'minValue' | 'maxValue'
96
+ >;
97
+
98
+ const MonthPickerCalendar = forwardRef<View, MonthPickerCalendarProps>(({ ...props }, ref) => {
99
+ const { value, onValueChange, open, setOpen, minValue, maxValue, locale, labels } =
100
+ useMonthPickerContext();
101
+
102
+ const handleValueChange = useCallback(
103
+ (next: MonthId | undefined) => {
104
+ onValueChange?.(next);
105
+ if (open) {
106
+ setOpen(false);
107
+ }
108
+ },
109
+ [onValueChange, open, setOpen],
110
+ );
111
+
112
+ return (
113
+ <MonthCalendar
114
+ ref={ref}
115
+ value={value}
116
+ onValueChange={handleValueChange}
117
+ minValue={minValue}
118
+ maxValue={maxValue}
119
+ locale={locale}
120
+ labels={labels}
121
+ {...props}
122
+ />
123
+ );
124
+ });
125
+
126
+ MonthPickerCalendar.displayName = 'MonthPicker.Calendar';
127
+
128
+ function MonthPickerDialog({ children }: { children: ReactNode }) {
129
+ const ctx = useMonthPickerContext();
130
+ return (
131
+ <PickerDialogSurface
132
+ open={ctx.open}
133
+ setOpen={ctx.setOpen}
134
+ registerSurface={ctx.registerSurface}
135
+ >
136
+ <Dialog.Popup className="p-4">
137
+ <MonthPickerContext.Provider value={ctx}>{children}</MonthPickerContext.Provider>
138
+ </Dialog.Popup>
139
+ </PickerDialogSurface>
140
+ );
141
+ }
142
+
143
+ function MonthPickerBottomSheet({ children }: { children: ReactNode }) {
144
+ const ctx = useMonthPickerContext();
145
+ return (
146
+ <PickerBottomSheetSurface
147
+ open={ctx.open}
148
+ setOpen={ctx.setOpen}
149
+ registerSurface={ctx.registerSurface}
150
+ >
151
+ <MonthPickerContext.Provider value={ctx}>{children}</MonthPickerContext.Provider>
152
+ </PickerBottomSheetSurface>
153
+ );
154
+ }
155
+
156
+ function MonthPickerPopover({ children, className }: { children: ReactNode; className?: string }) {
157
+ const ctx = useMonthPickerContext();
158
+ return (
159
+ <PickerPopoverSurface
160
+ open={ctx.open}
161
+ setOpen={ctx.setOpen}
162
+ registerSurface={ctx.registerSurface}
163
+ anchorRef={ctx.anchorRef}
164
+ triggerRef={ctx.triggerRef}
165
+ className={className}
166
+ >
167
+ <MonthPickerContext.Provider value={ctx}>{children}</MonthPickerContext.Provider>
168
+ </PickerPopoverSurface>
169
+ );
170
+ }
171
+
172
+ type MonthPickerCompound = typeof MonthPickerRoot & {
173
+ Trigger: typeof MonthPickerTrigger;
174
+ TriggerIndicator: typeof MonthPickerTriggerIndicator;
175
+ Calendar: typeof MonthPickerCalendar;
176
+ Popover: typeof MonthPickerPopover;
177
+ Dialog: typeof MonthPickerDialog;
178
+ BottomSheet: typeof MonthPickerBottomSheet;
179
+ };
180
+
181
+ export const MonthPicker = Object.assign(MonthPickerRoot, {
182
+ Trigger: MonthPickerTrigger,
183
+ TriggerIndicator: MonthPickerTriggerIndicator,
184
+ Calendar: MonthPickerCalendar,
185
+ Popover: MonthPickerPopover,
186
+ Dialog: MonthPickerDialog,
187
+ BottomSheet: MonthPickerBottomSheet,
188
+ }) as MonthPickerCompound;
@@ -0,0 +1,162 @@
1
+ import { forwardRef, useCallback, useId, useMemo, type ReactNode } from 'react';
2
+ import { View, type ViewProps } from 'react-native';
3
+ import { Calendar as FlashCalendar } from '@marceloterreiro/flash-calendar';
4
+ import {
5
+ dataAttributes,
6
+ mergeDataAttributes,
7
+ RangeCalendarRoot as RangeCalendarRootPrimitive,
8
+ useRangeCalendarContext,
9
+ type RangeCalendarRootProps,
10
+ } from '@cdx-ui/primitives';
11
+ import { cn } from '@cdx-ui/utils';
12
+ import { CalendarMonthHeader } from '../Calendar/CalendarMonthHeader';
13
+ import { CalendarYearPickerGrid } from '../Calendar/CalendarYearPickerGrid';
14
+ import {
15
+ FLASH_CALENDAR_DAY_HEIGHT,
16
+ FLASH_CALENDAR_RANGE_DAY_SPACING,
17
+ formatFlashCalendarWeekdayShort,
18
+ toCalendarDateId,
19
+ useFlashCalendarRangeTheme,
20
+ } from '../Calendar/flashCalendarAdapter';
21
+ import { calendarRootVariants } from '../Calendar/styles';
22
+
23
+ export interface RangeCalendarProps
24
+ extends Omit<RangeCalendarRootProps, 'children'>, Omit<ViewProps, 'children'> {
25
+ className?: string;
26
+ children?: ReactNode;
27
+ }
28
+
29
+ function RangeCalendarDefaultBody() {
30
+ const { view } = useRangeCalendarContext();
31
+ return (
32
+ <>
33
+ <RangeCalendarHeader />
34
+ {view === 'year' ? <RangeCalendarYearPicker /> : <RangeCalendarGrid />}
35
+ </>
36
+ );
37
+ }
38
+
39
+ const RangeCalendarRoot = forwardRef<View, RangeCalendarProps>(
40
+ ({ className, children, style, ...props }, ref) => (
41
+ <RangeCalendarRootPrimitive
42
+ ref={ref}
43
+ className={cn(calendarRootVariants(), className)}
44
+ style={style}
45
+ {...props}
46
+ {...mergeDataAttributes(props, dataAttributes({ slot: 'range-calendar' }))}
47
+ >
48
+ {children ?? <RangeCalendarDefaultBody />}
49
+ </RangeCalendarRootPrimitive>
50
+ ),
51
+ );
52
+
53
+ RangeCalendarRoot.displayName = 'RangeCalendar';
54
+
55
+ const RangeCalendarHeader = forwardRef<View, ViewProps>(({ className, style, ...props }, ref) => {
56
+ const { monthId, onMonthIdChange, disabled, view, setView, locale, labels } =
57
+ useRangeCalendarContext();
58
+ return (
59
+ <CalendarMonthHeader
60
+ ref={ref}
61
+ monthId={monthId}
62
+ onMonthIdChange={onMonthIdChange}
63
+ disabled={disabled}
64
+ view={view}
65
+ onViewChange={setView}
66
+ locale={locale}
67
+ labels={labels}
68
+ className={className}
69
+ style={style}
70
+ {...props}
71
+ />
72
+ );
73
+ });
74
+
75
+ RangeCalendarHeader.displayName = 'RangeCalendar.Header';
76
+
77
+ const RangeCalendarYearPicker = forwardRef<View, ViewProps>(
78
+ ({ className, style, ...props }, ref) => {
79
+ const { monthId, onMonthIdChange, disabled, setView, labels } = useRangeCalendarContext();
80
+ return (
81
+ <CalendarYearPickerGrid
82
+ ref={ref}
83
+ monthId={monthId}
84
+ onMonthIdChange={onMonthIdChange}
85
+ disabled={disabled}
86
+ onViewChange={setView}
87
+ labels={labels}
88
+ className={className}
89
+ style={style}
90
+ {...props}
91
+ />
92
+ );
93
+ },
94
+ );
95
+
96
+ RangeCalendarYearPicker.displayName = 'RangeCalendar.YearPicker';
97
+
98
+ const RangeCalendarGrid = forwardRef<View, ViewProps>(({ className, style, ...props }, ref) => {
99
+ const { startId, endId, monthId, minValue, maxValue, disabled, onDayPress, locale } =
100
+ useRangeCalendarContext();
101
+ const instanceId = useId();
102
+ const theme = useFlashCalendarRangeTheme({ startId, endId });
103
+
104
+ const formatWeekday = useCallback(
105
+ (date: Date) => formatFlashCalendarWeekdayShort(date, locale),
106
+ [locale],
107
+ );
108
+
109
+ const activeRanges = useMemo(() => {
110
+ if (startId && endId) {
111
+ return [{ startId, endId }];
112
+ }
113
+ if (startId) {
114
+ return [{ startId, endId: startId }];
115
+ }
116
+ return [];
117
+ }, [endId, startId]);
118
+
119
+ const handleDayPress = useCallback(
120
+ (dateId: string) => {
121
+ if (!disabled) {
122
+ onDayPress(toCalendarDateId(dateId));
123
+ }
124
+ },
125
+ [disabled, onDayPress],
126
+ );
127
+
128
+ return (
129
+ <View ref={ref} className={className} style={style} {...props}>
130
+ <FlashCalendar
131
+ calendarInstanceId={instanceId}
132
+ calendarMonthId={monthId}
133
+ calendarActiveDateRanges={activeRanges}
134
+ calendarMinDateId={minValue}
135
+ calendarMaxDateId={maxValue}
136
+ calendarMonthHeaderHeight={0}
137
+ calendarDayHeight={FLASH_CALENDAR_DAY_HEIGHT}
138
+ calendarWeekHeaderHeight={28}
139
+ calendarRowVerticalSpacing={4}
140
+ calendarRowHorizontalSpacing={FLASH_CALENDAR_RANGE_DAY_SPACING}
141
+ getCalendarMonthFormat={() => ''}
142
+ getCalendarWeekDayFormat={formatWeekday}
143
+ onCalendarDayPress={handleDayPress}
144
+ theme={theme}
145
+ />
146
+ </View>
147
+ );
148
+ });
149
+
150
+ RangeCalendarGrid.displayName = 'RangeCalendar.Grid';
151
+
152
+ type RangeCalendarCompound = typeof RangeCalendarRoot & {
153
+ Header: typeof RangeCalendarHeader;
154
+ Grid: typeof RangeCalendarGrid;
155
+ YearPicker: typeof RangeCalendarYearPicker;
156
+ };
157
+
158
+ export const RangeCalendar = Object.assign(RangeCalendarRoot, {
159
+ Header: RangeCalendarHeader,
160
+ Grid: RangeCalendarGrid,
161
+ YearPicker: RangeCalendarYearPicker,
162
+ }) as RangeCalendarCompound;
@@ -11,6 +11,16 @@ export * from './Carousel';
11
11
  export * from './Checkbox';
12
12
  export * from './Chip';
13
13
  export * from './CurrencyInput';
14
+ export * from './DateField';
15
+ export * from './Calendar';
16
+ export * from './RangeCalendar';
17
+ export * from './MonthCalendar';
18
+ export * from './DatePicker';
19
+ export * from './DateRangePicker';
20
+ export * from './MonthPicker';
21
+ export * from './DateTimeField';
22
+ export * from './DateTimeList';
23
+ export * from './DateTimePicker';
14
24
  export * from './Dialog';
15
25
  export * from './Divider';
16
26
  export * from './Field';
@@ -0,0 +1,65 @@
1
+ import { forwardRef, type ReactNode, type RefObject } from 'react';
2
+ import { Pressable, type PressableProps, type View } from 'react-native';
3
+ import { Slot } from '@cdx-ui/primitives';
4
+ import { composeEventHandlers, mergeRefs } from '@cdx-ui/utils';
5
+
6
+ export interface PickerTriggerProps extends PressableProps {
7
+ /**
8
+ * Merge press / a11y props onto the single child instead of rendering a `Pressable`.
9
+ * Use when the trigger should be another interactive host (e.g. `IconButton`).
10
+ * For a larger field hit target, prefer wrapping `DateField.Group` as children
11
+ * without `asChild` so the outer `Pressable` covers the whole input chrome.
12
+ */
13
+ asChild?: boolean;
14
+ children?: ReactNode;
15
+ open: boolean;
16
+ setOpen: (open: boolean) => void;
17
+ disabled?: boolean;
18
+ triggerRef: RefObject<View | null>;
19
+ accessibilityLabel: string;
20
+ }
21
+
22
+ /**
23
+ * Shared open/close press host for date / month / date-time picker compounds.
24
+ * Compose two ways:
25
+ * - **Icon-only** (editable segmented fields): wrap `TriggerIndicator` inside `DateField.Suffix`
26
+ * - **Full-field** (range or overlay-primary selection): wrap the entire `DateField.Group`
27
+ */
28
+ export const PickerTrigger = forwardRef<View, PickerTriggerProps>(
29
+ (
30
+ {
31
+ asChild = false,
32
+ children,
33
+ className,
34
+ style,
35
+ onPress,
36
+ open,
37
+ setOpen,
38
+ disabled,
39
+ triggerRef,
40
+ accessibilityLabel,
41
+ ...props
42
+ },
43
+ ref,
44
+ ) => {
45
+ const Comp = asChild ? Slot : Pressable;
46
+
47
+ return (
48
+ <Comp
49
+ ref={mergeRefs(ref, triggerRef)}
50
+ className={className}
51
+ style={style}
52
+ accessibilityRole="button"
53
+ accessibilityLabel={accessibilityLabel}
54
+ accessibilityState={{ disabled: !!disabled, expanded: open }}
55
+ disabled={disabled}
56
+ onPress={composeEventHandlers(onPress, () => setOpen(!open))}
57
+ {...props}
58
+ >
59
+ {children}
60
+ </Comp>
61
+ );
62
+ },
63
+ );
64
+
65
+ PickerTrigger.displayName = 'PickerTrigger';
@@ -0,0 +1,17 @@
1
+ import { Platform } from 'react-native';
2
+ import { cva } from '@cdx-ui/utils';
3
+
4
+ /** Keeps the picker popover above app chrome and other absolutely-positioned surfaces. */
5
+ export const PICKER_POPOVER_Z_INDEX = 9999;
6
+
7
+ export const pickerPopoverContentVariants = cva([
8
+ 'bg-surface-primary',
9
+ 'border border-stroke-secondary',
10
+ 'overflow-hidden',
11
+ 'rounded-[var(--border-radius-default)]',
12
+ Platform.select({
13
+ web: 'shadow-sm',
14
+ default: '',
15
+ }),
16
+ 'p-3',
17
+ ]);