@cdx-ui/components 0.0.1-beta.140 → 0.0.1-beta.141
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.
- package/lib/commonjs/components/BottomSheet/index.js +8 -2
- package/lib/commonjs/components/BottomSheet/index.js.map +1 -1
- package/lib/commonjs/components/Calendar/CalendarMonthHeader.js +96 -0
- package/lib/commonjs/components/Calendar/CalendarMonthHeader.js.map +1 -0
- package/lib/commonjs/components/Calendar/CalendarYearPickerGrid.js +74 -0
- package/lib/commonjs/components/Calendar/CalendarYearPickerGrid.js.map +1 -0
- package/lib/commonjs/components/Calendar/flashCalendarAdapter.js +298 -0
- package/lib/commonjs/components/Calendar/flashCalendarAdapter.js.map +1 -0
- package/lib/commonjs/components/Calendar/index.js +149 -0
- package/lib/commonjs/components/Calendar/index.js.map +1 -0
- package/lib/commonjs/components/Calendar/styles.js +70 -0
- package/lib/commonjs/components/Calendar/styles.js.map +1 -0
- package/lib/commonjs/components/DateField/index.js +147 -0
- package/lib/commonjs/components/DateField/index.js.map +1 -0
- package/lib/commonjs/components/DateField/styles.js +34 -0
- package/lib/commonjs/components/DateField/styles.js.map +1 -0
- package/lib/commonjs/components/DatePicker/index.js +163 -0
- package/lib/commonjs/components/DatePicker/index.js.map +1 -0
- package/lib/commonjs/components/DateRangePicker/index.js +177 -0
- package/lib/commonjs/components/DateRangePicker/index.js.map +1 -0
- package/lib/commonjs/components/DateTimeField/index.js +96 -0
- package/lib/commonjs/components/DateTimeField/index.js.map +1 -0
- package/lib/commonjs/components/DateTimeList/index.js +180 -0
- package/lib/commonjs/components/DateTimeList/index.js.map +1 -0
- package/lib/commonjs/components/DateTimeList/styles.js +55 -0
- package/lib/commonjs/components/DateTimeList/styles.js.map +1 -0
- package/lib/commonjs/components/DateTimePicker/index.js +187 -0
- package/lib/commonjs/components/DateTimePicker/index.js.map +1 -0
- package/lib/commonjs/components/MonthCalendar/index.js +229 -0
- package/lib/commonjs/components/MonthCalendar/index.js.map +1 -0
- package/lib/commonjs/components/MonthPicker/index.js +163 -0
- package/lib/commonjs/components/MonthPicker/index.js.map +1 -0
- package/lib/commonjs/components/RangeCalendar/index.js +165 -0
- package/lib/commonjs/components/RangeCalendar/index.js.map +1 -0
- package/lib/commonjs/components/index.js +120 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/components/picker/PickerTrigger.js +49 -0
- package/lib/commonjs/components/picker/PickerTrigger.js.map +1 -0
- package/lib/commonjs/components/picker/styles.js +15 -0
- package/lib/commonjs/components/picker/styles.js.map +1 -0
- package/lib/commonjs/components/picker/surfaces.js +141 -0
- package/lib/commonjs/components/picker/surfaces.js.map +1 -0
- package/lib/module/components/BottomSheet/index.js +8 -2
- package/lib/module/components/BottomSheet/index.js.map +1 -1
- package/lib/module/components/Calendar/CalendarMonthHeader.js +92 -0
- package/lib/module/components/Calendar/CalendarMonthHeader.js.map +1 -0
- package/lib/module/components/Calendar/CalendarYearPickerGrid.js +70 -0
- package/lib/module/components/Calendar/CalendarYearPickerGrid.js.map +1 -0
- package/lib/module/components/Calendar/flashCalendarAdapter.js +290 -0
- package/lib/module/components/Calendar/flashCalendarAdapter.js.map +1 -0
- package/lib/module/components/Calendar/index.js +145 -0
- package/lib/module/components/Calendar/index.js.map +1 -0
- package/lib/module/components/Calendar/styles.js +66 -0
- package/lib/module/components/Calendar/styles.js.map +1 -0
- package/lib/module/components/DateField/index.js +143 -0
- package/lib/module/components/DateField/index.js.map +1 -0
- package/lib/module/components/DateField/styles.js +30 -0
- package/lib/module/components/DateField/styles.js.map +1 -0
- package/lib/module/components/DatePicker/index.js +159 -0
- package/lib/module/components/DatePicker/index.js.map +1 -0
- package/lib/module/components/DateRangePicker/index.js +173 -0
- package/lib/module/components/DateRangePicker/index.js.map +1 -0
- package/lib/module/components/DateTimeField/index.js +92 -0
- package/lib/module/components/DateTimeField/index.js.map +1 -0
- package/lib/module/components/DateTimeList/index.js +176 -0
- package/lib/module/components/DateTimeList/index.js.map +1 -0
- package/lib/module/components/DateTimeList/styles.js +52 -0
- package/lib/module/components/DateTimeList/styles.js.map +1 -0
- package/lib/module/components/DateTimePicker/index.js +183 -0
- package/lib/module/components/DateTimePicker/index.js.map +1 -0
- package/lib/module/components/MonthCalendar/index.js +225 -0
- package/lib/module/components/MonthCalendar/index.js.map +1 -0
- package/lib/module/components/MonthPicker/index.js +159 -0
- package/lib/module/components/MonthPicker/index.js.map +1 -0
- package/lib/module/components/RangeCalendar/index.js +161 -0
- package/lib/module/components/RangeCalendar/index.js.map +1 -0
- package/lib/module/components/index.js +10 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/picker/PickerTrigger.js +45 -0
- package/lib/module/components/picker/PickerTrigger.js.map +1 -0
- package/lib/module/components/picker/styles.js +12 -0
- package/lib/module/components/picker/styles.js.map +1 -0
- package/lib/module/components/picker/surfaces.js +135 -0
- package/lib/module/components/picker/surfaces.js.map +1 -0
- package/lib/typescript/components/BottomSheet/index.d.ts +2 -2
- package/lib/typescript/components/BottomSheet/index.d.ts.map +1 -1
- package/lib/typescript/components/Calendar/CalendarMonthHeader.d.ts +14 -0
- package/lib/typescript/components/Calendar/CalendarMonthHeader.d.ts.map +1 -0
- package/lib/typescript/components/Calendar/CalendarYearPickerGrid.d.ts +12 -0
- package/lib/typescript/components/Calendar/CalendarYearPickerGrid.d.ts.map +1 -0
- package/lib/typescript/components/Calendar/flashCalendarAdapter.d.ts +18 -0
- package/lib/typescript/components/Calendar/flashCalendarAdapter.d.ts.map +1 -0
- package/lib/typescript/components/Calendar/index.d.ts +19 -0
- package/lib/typescript/components/Calendar/index.d.ts.map +1 -0
- package/lib/typescript/components/Calendar/styles.d.ts +23 -0
- package/lib/typescript/components/Calendar/styles.d.ts.map +1 -0
- package/lib/typescript/components/DateField/index.d.ts +43 -0
- package/lib/typescript/components/DateField/index.d.ts.map +1 -0
- package/lib/typescript/components/DateField/styles.d.ts +15 -0
- package/lib/typescript/components/DateField/styles.d.ts.map +1 -0
- package/lib/typescript/components/DatePicker/index.d.ts +40 -0
- package/lib/typescript/components/DatePicker/index.d.ts.map +1 -0
- package/lib/typescript/components/DateRangePicker/index.d.ts +45 -0
- package/lib/typescript/components/DateRangePicker/index.d.ts.map +1 -0
- package/lib/typescript/components/DateTimeField/index.d.ts +29 -0
- package/lib/typescript/components/DateTimeField/index.d.ts.map +1 -0
- package/lib/typescript/components/DateTimeList/index.d.ts +19 -0
- package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -0
- package/lib/typescript/components/DateTimeList/styles.d.ts +17 -0
- package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -0
- package/lib/typescript/components/DateTimePicker/index.d.ts +40 -0
- package/lib/typescript/components/DateTimePicker/index.d.ts.map +1 -0
- package/lib/typescript/components/MonthCalendar/index.d.ts +29 -0
- package/lib/typescript/components/MonthCalendar/index.d.ts.map +1 -0
- package/lib/typescript/components/MonthPicker/index.d.ts +40 -0
- package/lib/typescript/components/MonthPicker/index.d.ts.map +1 -0
- package/lib/typescript/components/RangeCalendar/index.d.ts +19 -0
- package/lib/typescript/components/RangeCalendar/index.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +10 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/components/picker/PickerTrigger.d.ts +25 -0
- package/lib/typescript/components/picker/PickerTrigger.d.ts.map +1 -0
- package/lib/typescript/components/picker/styles.d.ts +4 -0
- package/lib/typescript/components/picker/styles.d.ts.map +1 -0
- package/lib/typescript/components/picker/surfaces.d.ts +28 -0
- package/lib/typescript/components/picker/surfaces.d.ts.map +1 -0
- package/package.json +6 -4
- package/src/components/BottomSheet/index.tsx +7 -1
- package/src/components/Calendar/CalendarMonthHeader.tsx +124 -0
- package/src/components/Calendar/CalendarYearPickerGrid.tsx +101 -0
- package/src/components/Calendar/flashCalendarAdapter.ts +362 -0
- package/src/components/Calendar/index.tsx +151 -0
- package/src/components/Calendar/styles.ts +80 -0
- package/src/components/DateField/index.tsx +196 -0
- package/src/components/DateField/styles.ts +51 -0
- package/src/components/DatePicker/index.tsx +187 -0
- package/src/components/DateRangePicker/index.tsx +202 -0
- package/src/components/DateTimeField/index.tsx +128 -0
- package/src/components/DateTimeList/index.tsx +187 -0
- package/src/components/DateTimeList/styles.ts +59 -0
- package/src/components/DateTimePicker/index.tsx +233 -0
- package/src/components/MonthCalendar/index.tsx +252 -0
- package/src/components/MonthPicker/index.tsx +188 -0
- package/src/components/RangeCalendar/index.tsx +162 -0
- package/src/components/index.ts +10 -0
- package/src/components/picker/PickerTrigger.tsx +65 -0
- package/src/components/picker/styles.ts +17 -0
- package/src/components/picker/surfaces.tsx +174 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { forwardRef, useCallback, useMemo, type ComponentRef } from 'react';
|
|
2
|
+
import { Pressable, Text, View, type ViewProps } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
formatMonthId,
|
|
5
|
+
formatSelectYear,
|
|
6
|
+
parseMonthId,
|
|
7
|
+
resolveDateComponentLabels,
|
|
8
|
+
type DateComponentLabels,
|
|
9
|
+
type MonthId,
|
|
10
|
+
} from '@cdx-ui/formatters';
|
|
11
|
+
import { cn } from '@cdx-ui/utils';
|
|
12
|
+
import type { CalendarNavigationView } from './CalendarMonthHeader';
|
|
13
|
+
import {
|
|
14
|
+
calendarPillCellTextVariants,
|
|
15
|
+
calendarPillCellVariants,
|
|
16
|
+
calendarYearPickerGridVariants,
|
|
17
|
+
} from './styles';
|
|
18
|
+
|
|
19
|
+
const YEAR_RANGE_BEFORE = 9;
|
|
20
|
+
const YEAR_RANGE_AFTER = 11;
|
|
21
|
+
|
|
22
|
+
export interface CalendarYearPickerGridProps extends ViewProps {
|
|
23
|
+
monthId: MonthId;
|
|
24
|
+
onMonthIdChange: (monthId: MonthId) => void;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
onViewChange: (view: CalendarNavigationView) => void;
|
|
27
|
+
labels?: Partial<DateComponentLabels>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const CalendarYearPickerGrid = forwardRef<
|
|
31
|
+
ComponentRef<typeof View>,
|
|
32
|
+
CalendarYearPickerGridProps
|
|
33
|
+
>(
|
|
34
|
+
(
|
|
35
|
+
{
|
|
36
|
+
monthId,
|
|
37
|
+
onMonthIdChange,
|
|
38
|
+
disabled = false,
|
|
39
|
+
onViewChange,
|
|
40
|
+
labels: labelsProp,
|
|
41
|
+
className,
|
|
42
|
+
style,
|
|
43
|
+
...props
|
|
44
|
+
},
|
|
45
|
+
ref,
|
|
46
|
+
) => {
|
|
47
|
+
const labels = useMemo(() => resolveDateComponentLabels(labelsProp), [labelsProp]);
|
|
48
|
+
const parts = parseMonthId(monthId);
|
|
49
|
+
const currentYear = parts?.year ?? new Date().getFullYear();
|
|
50
|
+
const currentMonth = parts?.month ?? 1;
|
|
51
|
+
|
|
52
|
+
const years = useMemo(
|
|
53
|
+
() =>
|
|
54
|
+
Array.from(
|
|
55
|
+
{ length: YEAR_RANGE_BEFORE + YEAR_RANGE_AFTER + 1 },
|
|
56
|
+
(_, i) => currentYear - YEAR_RANGE_BEFORE + i,
|
|
57
|
+
),
|
|
58
|
+
[currentYear],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const selectYear = useCallback(
|
|
62
|
+
(year: number) => {
|
|
63
|
+
if (disabled) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
onMonthIdChange(formatMonthId({ year, month: currentMonth }));
|
|
67
|
+
onViewChange('month');
|
|
68
|
+
},
|
|
69
|
+
[currentMonth, disabled, onMonthIdChange, onViewChange],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<View
|
|
74
|
+
ref={ref}
|
|
75
|
+
className={cn(calendarYearPickerGridVariants(), className)}
|
|
76
|
+
style={style}
|
|
77
|
+
accessibilityRole="none"
|
|
78
|
+
{...props}
|
|
79
|
+
>
|
|
80
|
+
{years.map((year) => {
|
|
81
|
+
const selected = year === currentYear;
|
|
82
|
+
return (
|
|
83
|
+
<Pressable
|
|
84
|
+
key={year}
|
|
85
|
+
accessibilityRole="button"
|
|
86
|
+
accessibilityState={{ selected, disabled }}
|
|
87
|
+
accessibilityLabel={formatSelectYear(labels, year)}
|
|
88
|
+
disabled={disabled}
|
|
89
|
+
onPress={() => selectYear(year)}
|
|
90
|
+
className={calendarPillCellVariants({ selected, disabled })}
|
|
91
|
+
>
|
|
92
|
+
<Text className={calendarPillCellTextVariants({ selected })}>{String(year)}</Text>
|
|
93
|
+
</Pressable>
|
|
94
|
+
);
|
|
95
|
+
})}
|
|
96
|
+
</View>
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
CalendarYearPickerGrid.displayName = 'CalendarYearPickerGrid';
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useCSSVariable } from 'uniwind';
|
|
3
|
+
import type { CalendarTheme } from '@marceloterreiro/flash-calendar';
|
|
4
|
+
import type { DateId } from '@cdx-ui/formatters';
|
|
5
|
+
|
|
6
|
+
const DAY_SIZE = 36;
|
|
7
|
+
const DAY_RADIUS = DAY_SIZE / 2;
|
|
8
|
+
|
|
9
|
+
const CALENDAR_COLOR_VARS = [
|
|
10
|
+
'--color-surface-action-strong',
|
|
11
|
+
'--color-content-action-on-strong',
|
|
12
|
+
'--color-content-tertiary',
|
|
13
|
+
'--color-surface-action-subtle',
|
|
14
|
+
'--color-content-primary',
|
|
15
|
+
'--color-stroke-action',
|
|
16
|
+
'--color-surface-action-subtle-hover',
|
|
17
|
+
'--color-content-action',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const CALENDAR_LENGTH_VARS = ['--text-sm', '--border-width-selected', '--opacity-disabled'];
|
|
21
|
+
|
|
22
|
+
const DAY_FONT_SIZE_FALLBACK = 14;
|
|
23
|
+
const TODAY_BORDER_WIDTH_FALLBACK = 2;
|
|
24
|
+
const DISABLED_OPACITY_FALLBACK = 0.5;
|
|
25
|
+
const ROOT_FONT_SIZE_PX = 16;
|
|
26
|
+
|
|
27
|
+
interface CalendarColors {
|
|
28
|
+
surfaceActionStrong: string;
|
|
29
|
+
contentActionOnStrong: string;
|
|
30
|
+
contentTertiary: string;
|
|
31
|
+
surfaceActionSubtle: string;
|
|
32
|
+
contentPrimary: string;
|
|
33
|
+
strokeAction: string;
|
|
34
|
+
surfaceActionSubtleHover: string;
|
|
35
|
+
contentAction: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CalendarLengths {
|
|
39
|
+
dayFontSize: number;
|
|
40
|
+
todayBorderWidth: number;
|
|
41
|
+
disabledOpacity: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function tokenValue(resolved: string | number | undefined, cssVar: string): string {
|
|
45
|
+
return resolved != null ? String(resolved) : `var(${cssVar})`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Coerces a length token to the numeric px RN `fontSize`/`borderWidth` require.
|
|
50
|
+
*
|
|
51
|
+
* `useCSSVariable` resolves lengths differently per platform: native returns a
|
|
52
|
+
* number already in px, while web returns the raw CSS string (`0.875rem`). Only
|
|
53
|
+
* the rem form needs the root-font-size conversion — scaling the native value
|
|
54
|
+
* would multiply it a second time.
|
|
55
|
+
*/
|
|
56
|
+
function lengthToPx(resolved: string | number | undefined, fallback: number): number {
|
|
57
|
+
if (typeof resolved === 'number') {
|
|
58
|
+
return Number.isFinite(resolved) ? resolved : fallback;
|
|
59
|
+
}
|
|
60
|
+
if (typeof resolved !== 'string') {
|
|
61
|
+
return fallback;
|
|
62
|
+
}
|
|
63
|
+
const trimmed = resolved.trim();
|
|
64
|
+
const numeric = Number.parseFloat(trimmed);
|
|
65
|
+
if (!Number.isFinite(numeric)) {
|
|
66
|
+
return fallback;
|
|
67
|
+
}
|
|
68
|
+
return trimmed.endsWith('rem') ? numeric * ROOT_FONT_SIZE_PX : numeric;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function useFlashCalendarColors(): CalendarColors {
|
|
72
|
+
const [
|
|
73
|
+
surfaceActionStrong,
|
|
74
|
+
contentActionOnStrong,
|
|
75
|
+
contentTertiary,
|
|
76
|
+
surfaceActionSubtle,
|
|
77
|
+
contentPrimary,
|
|
78
|
+
strokeAction,
|
|
79
|
+
surfaceActionSubtleHover,
|
|
80
|
+
contentAction,
|
|
81
|
+
] = useCSSVariable(CALENDAR_COLOR_VARS);
|
|
82
|
+
|
|
83
|
+
return useMemo(
|
|
84
|
+
() => ({
|
|
85
|
+
surfaceActionStrong: tokenValue(surfaceActionStrong, '--color-surface-action-strong'),
|
|
86
|
+
contentActionOnStrong: tokenValue(contentActionOnStrong, '--color-content-action-on-strong'),
|
|
87
|
+
contentTertiary: tokenValue(contentTertiary, '--color-content-tertiary'),
|
|
88
|
+
surfaceActionSubtle: tokenValue(surfaceActionSubtle, '--color-surface-action-subtle'),
|
|
89
|
+
contentPrimary: tokenValue(contentPrimary, '--color-content-primary'),
|
|
90
|
+
strokeAction: tokenValue(strokeAction, '--color-stroke-action'),
|
|
91
|
+
surfaceActionSubtleHover: tokenValue(
|
|
92
|
+
surfaceActionSubtleHover,
|
|
93
|
+
'--color-surface-action-subtle-hover',
|
|
94
|
+
),
|
|
95
|
+
contentAction: tokenValue(contentAction, '--color-content-action'),
|
|
96
|
+
}),
|
|
97
|
+
[
|
|
98
|
+
contentAction,
|
|
99
|
+
contentActionOnStrong,
|
|
100
|
+
contentPrimary,
|
|
101
|
+
contentTertiary,
|
|
102
|
+
strokeAction,
|
|
103
|
+
surfaceActionStrong,
|
|
104
|
+
surfaceActionSubtle,
|
|
105
|
+
surfaceActionSubtleHover,
|
|
106
|
+
],
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function useFlashCalendarLengths(): CalendarLengths {
|
|
111
|
+
const [textSm, borderWidthSelected, opacityDisabled] = useCSSVariable(CALENDAR_LENGTH_VARS);
|
|
112
|
+
|
|
113
|
+
return useMemo(
|
|
114
|
+
() => ({
|
|
115
|
+
dayFontSize: lengthToPx(textSm, DAY_FONT_SIZE_FALLBACK),
|
|
116
|
+
todayBorderWidth: lengthToPx(borderWidthSelected, TODAY_BORDER_WIDTH_FALLBACK),
|
|
117
|
+
disabledOpacity: lengthToPx(opacityDisabled, DISABLED_OPACITY_FALLBACK),
|
|
118
|
+
}),
|
|
119
|
+
[borderWidthSelected, opacityDisabled, textSm],
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function isSelectedDay(id: string, selectedDateId?: DateId): boolean {
|
|
124
|
+
return selectedDateId !== undefined && id === selectedDateId;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function selectedDayStyles(colors: CalendarColors) {
|
|
128
|
+
return {
|
|
129
|
+
container: {
|
|
130
|
+
borderRadius: DAY_RADIUS,
|
|
131
|
+
borderWidth: 0,
|
|
132
|
+
backgroundColor: colors.surfaceActionStrong,
|
|
133
|
+
},
|
|
134
|
+
content: {
|
|
135
|
+
color: colors.contentActionOnStrong,
|
|
136
|
+
fontWeight: '600' as const,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function useFlashCalendarTheme(selectedDateId?: DateId): CalendarTheme {
|
|
142
|
+
const colors = useFlashCalendarColors();
|
|
143
|
+
const lengths = useFlashCalendarLengths();
|
|
144
|
+
const selected = useMemo(() => selectedDayStyles(colors), [colors]);
|
|
145
|
+
|
|
146
|
+
return useMemo(
|
|
147
|
+
() => ({
|
|
148
|
+
rowMonth: {
|
|
149
|
+
container: {
|
|
150
|
+
height: 0,
|
|
151
|
+
paddingVertical: 0,
|
|
152
|
+
overflow: 'hidden' as const,
|
|
153
|
+
},
|
|
154
|
+
content: {
|
|
155
|
+
fontSize: 0,
|
|
156
|
+
height: 0,
|
|
157
|
+
opacity: 0,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
rowWeek: {
|
|
161
|
+
container: {
|
|
162
|
+
paddingBottom: 4,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
itemWeekName: {
|
|
166
|
+
content: {
|
|
167
|
+
fontSize: 13,
|
|
168
|
+
fontWeight: '500' as const,
|
|
169
|
+
color: colors.contentTertiary,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
itemDay: {
|
|
173
|
+
base: () => ({
|
|
174
|
+
container: {
|
|
175
|
+
borderRadius: DAY_RADIUS,
|
|
176
|
+
alignItems: 'center' as const,
|
|
177
|
+
justifyContent: 'center' as const,
|
|
178
|
+
},
|
|
179
|
+
content: {
|
|
180
|
+
fontSize: lengths.dayFontSize,
|
|
181
|
+
fontWeight: '500' as const,
|
|
182
|
+
},
|
|
183
|
+
}),
|
|
184
|
+
idle: ({ id, isPressed }) =>
|
|
185
|
+
isSelectedDay(id, selectedDateId)
|
|
186
|
+
? selected
|
|
187
|
+
: {
|
|
188
|
+
container: {
|
|
189
|
+
borderRadius: DAY_RADIUS,
|
|
190
|
+
backgroundColor: isPressed ? colors.surfaceActionSubtle : 'transparent',
|
|
191
|
+
},
|
|
192
|
+
content: {
|
|
193
|
+
color: colors.contentPrimary,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
today: ({ id, isPressed }) =>
|
|
197
|
+
isSelectedDay(id, selectedDateId)
|
|
198
|
+
? selected
|
|
199
|
+
: {
|
|
200
|
+
container: {
|
|
201
|
+
borderRadius: DAY_RADIUS,
|
|
202
|
+
borderWidth: lengths.todayBorderWidth,
|
|
203
|
+
borderColor: colors.strokeAction,
|
|
204
|
+
backgroundColor: isPressed ? colors.surfaceActionSubtle : 'transparent',
|
|
205
|
+
},
|
|
206
|
+
content: {
|
|
207
|
+
color: colors.contentPrimary,
|
|
208
|
+
fontWeight: '600' as const,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
active: () => selected,
|
|
212
|
+
disabled: () => ({
|
|
213
|
+
container: {
|
|
214
|
+
borderRadius: DAY_RADIUS,
|
|
215
|
+
backgroundColor: 'transparent',
|
|
216
|
+
},
|
|
217
|
+
content: {
|
|
218
|
+
color: colors.contentPrimary,
|
|
219
|
+
opacity: lengths.disabledOpacity,
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
[colors, lengths, selected, selectedDateId],
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function useFlashCalendarRangeTheme(range?: {
|
|
229
|
+
startId?: DateId;
|
|
230
|
+
endId?: DateId;
|
|
231
|
+
}): CalendarTheme {
|
|
232
|
+
const colors = useFlashCalendarColors();
|
|
233
|
+
const lengths = useFlashCalendarLengths();
|
|
234
|
+
const selected = useMemo(() => selectedDayStyles(colors), [colors]);
|
|
235
|
+
|
|
236
|
+
return useMemo(
|
|
237
|
+
() => ({
|
|
238
|
+
rowMonth: {
|
|
239
|
+
container: {
|
|
240
|
+
height: 0,
|
|
241
|
+
paddingVertical: 0,
|
|
242
|
+
overflow: 'hidden' as const,
|
|
243
|
+
},
|
|
244
|
+
content: {
|
|
245
|
+
fontSize: 0,
|
|
246
|
+
height: 0,
|
|
247
|
+
opacity: 0,
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
rowWeek: {
|
|
251
|
+
container: {
|
|
252
|
+
paddingBottom: 4,
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
itemWeekName: {
|
|
256
|
+
content: {
|
|
257
|
+
fontSize: 13,
|
|
258
|
+
fontWeight: '500' as const,
|
|
259
|
+
color: colors.contentTertiary,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
itemDayContainer: {
|
|
263
|
+
activeDayFiller: {
|
|
264
|
+
backgroundColor: colors.surfaceActionSubtleHover,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
itemDay: {
|
|
268
|
+
base: () => ({
|
|
269
|
+
container: {
|
|
270
|
+
borderRadius: 0,
|
|
271
|
+
padding: 0,
|
|
272
|
+
alignItems: 'center' as const,
|
|
273
|
+
justifyContent: 'center' as const,
|
|
274
|
+
},
|
|
275
|
+
content: {
|
|
276
|
+
fontSize: lengths.dayFontSize,
|
|
277
|
+
fontWeight: '500' as const,
|
|
278
|
+
},
|
|
279
|
+
}),
|
|
280
|
+
idle: ({ isPressed }) => ({
|
|
281
|
+
container: {
|
|
282
|
+
borderRadius: DAY_RADIUS,
|
|
283
|
+
backgroundColor: isPressed ? colors.surfaceActionSubtle : 'transparent',
|
|
284
|
+
},
|
|
285
|
+
content: {
|
|
286
|
+
color: colors.contentPrimary,
|
|
287
|
+
},
|
|
288
|
+
}),
|
|
289
|
+
today: ({ id, isPressed }) => {
|
|
290
|
+
const isEndpoint =
|
|
291
|
+
(range?.startId !== undefined && id === range.startId) ||
|
|
292
|
+
(range?.endId !== undefined && id === range.endId);
|
|
293
|
+
if (isEndpoint) {
|
|
294
|
+
return selected;
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
container: {
|
|
298
|
+
borderRadius: DAY_RADIUS,
|
|
299
|
+
borderWidth: lengths.todayBorderWidth,
|
|
300
|
+
borderColor: colors.strokeAction,
|
|
301
|
+
backgroundColor: isPressed ? colors.surfaceActionSubtle : 'transparent',
|
|
302
|
+
},
|
|
303
|
+
content: {
|
|
304
|
+
color: colors.contentPrimary,
|
|
305
|
+
fontWeight: '600' as const,
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
active: ({ isStartOfRange, isEndOfRange }) => {
|
|
310
|
+
const isEndpoint = isStartOfRange || isEndOfRange;
|
|
311
|
+
const isSingleDay = isStartOfRange && isEndOfRange;
|
|
312
|
+
return {
|
|
313
|
+
container: {
|
|
314
|
+
padding: 0,
|
|
315
|
+
borderRadius: isSingleDay ? DAY_RADIUS : 0,
|
|
316
|
+
borderTopLeftRadius: isStartOfRange ? DAY_RADIUS : 0,
|
|
317
|
+
borderBottomLeftRadius: isStartOfRange ? DAY_RADIUS : 0,
|
|
318
|
+
borderTopRightRadius: isEndOfRange ? DAY_RADIUS : 0,
|
|
319
|
+
borderBottomRightRadius: isEndOfRange ? DAY_RADIUS : 0,
|
|
320
|
+
backgroundColor: isEndpoint
|
|
321
|
+
? colors.surfaceActionStrong
|
|
322
|
+
: colors.surfaceActionSubtleHover,
|
|
323
|
+
},
|
|
324
|
+
content: {
|
|
325
|
+
color: isEndpoint ? colors.contentActionOnStrong : colors.contentAction,
|
|
326
|
+
fontWeight: isEndpoint ? ('600' as const) : ('500' as const),
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
},
|
|
330
|
+
disabled: () => ({
|
|
331
|
+
container: {
|
|
332
|
+
borderRadius: DAY_RADIUS,
|
|
333
|
+
backgroundColor: 'transparent',
|
|
334
|
+
},
|
|
335
|
+
content: {
|
|
336
|
+
color: colors.contentPrimary,
|
|
337
|
+
opacity: lengths.disabledOpacity,
|
|
338
|
+
},
|
|
339
|
+
}),
|
|
340
|
+
},
|
|
341
|
+
}),
|
|
342
|
+
[colors, lengths, range?.endId, range?.startId, selected],
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** Keep range days flush so the band reads as one shape (filler covers residual sub-pixel gaps). */
|
|
347
|
+
export const FLASH_CALENDAR_RANGE_DAY_SPACING = 0;
|
|
348
|
+
|
|
349
|
+
export const FLASH_CALENDAR_DAY_HEIGHT = DAY_SIZE;
|
|
350
|
+
|
|
351
|
+
export function formatFlashCalendarWeekdayShort(date: Date, locale: string): string {
|
|
352
|
+
return new Intl.DateTimeFormat(locale, { weekday: 'short' }).format(date);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Brands a raw Flash Calendar day id as a wire {@link DateId}. Flash Calendar
|
|
357
|
+
* always hands back `YYYY-MM-DD`, so this is the trusted third-party boundary
|
|
358
|
+
* where the wire brand is applied.
|
|
359
|
+
*/
|
|
360
|
+
export function toCalendarDateId(id: string): DateId {
|
|
361
|
+
return id as DateId;
|
|
362
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
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
|
+
CalendarGrid as CalendarGridPrimitive,
|
|
6
|
+
CalendarRoot as CalendarRootPrimitive,
|
|
7
|
+
dataAttributes,
|
|
8
|
+
mergeDataAttributes,
|
|
9
|
+
useCalendarContext,
|
|
10
|
+
type ICalendarProps,
|
|
11
|
+
} from '@cdx-ui/primitives';
|
|
12
|
+
import { cn } from '@cdx-ui/utils';
|
|
13
|
+
import { CalendarMonthHeader } from './CalendarMonthHeader';
|
|
14
|
+
import { CalendarYearPickerGrid } from './CalendarYearPickerGrid';
|
|
15
|
+
import {
|
|
16
|
+
FLASH_CALENDAR_DAY_HEIGHT,
|
|
17
|
+
formatFlashCalendarWeekdayShort,
|
|
18
|
+
toCalendarDateId,
|
|
19
|
+
useFlashCalendarTheme,
|
|
20
|
+
} from './flashCalendarAdapter';
|
|
21
|
+
import { calendarRootVariants } from './styles';
|
|
22
|
+
|
|
23
|
+
export interface CalendarProps extends ICalendarProps, Omit<ViewProps, 'children'> {
|
|
24
|
+
className?: string;
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function CalendarDefaultBody() {
|
|
29
|
+
const { view } = useCalendarContext();
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<CalendarHeader />
|
|
33
|
+
{view === 'year' ? <CalendarYearPicker /> : <CalendarGrid />}
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const CalendarRoot = forwardRef<View, CalendarProps>(
|
|
39
|
+
({ className, children, style, ...props }, ref) => (
|
|
40
|
+
<CalendarRootPrimitive
|
|
41
|
+
ref={ref}
|
|
42
|
+
className={cn(calendarRootVariants(), className)}
|
|
43
|
+
style={style}
|
|
44
|
+
{...props}
|
|
45
|
+
{...mergeDataAttributes(props, dataAttributes({ slot: 'calendar' }))}
|
|
46
|
+
>
|
|
47
|
+
{children ?? <CalendarDefaultBody />}
|
|
48
|
+
</CalendarRootPrimitive>
|
|
49
|
+
),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
CalendarRoot.displayName = 'Calendar';
|
|
53
|
+
|
|
54
|
+
const CalendarHeader = forwardRef<View, ViewProps>(({ className, style, ...props }, ref) => {
|
|
55
|
+
const { monthId, onMonthIdChange, disabled, view, setView, locale, labels } =
|
|
56
|
+
useCalendarContext();
|
|
57
|
+
return (
|
|
58
|
+
<CalendarMonthHeader
|
|
59
|
+
ref={ref}
|
|
60
|
+
monthId={monthId}
|
|
61
|
+
onMonthIdChange={onMonthIdChange}
|
|
62
|
+
disabled={disabled}
|
|
63
|
+
view={view}
|
|
64
|
+
onViewChange={setView}
|
|
65
|
+
locale={locale}
|
|
66
|
+
labels={labels}
|
|
67
|
+
className={className}
|
|
68
|
+
style={style}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
CalendarHeader.displayName = 'Calendar.Header';
|
|
75
|
+
|
|
76
|
+
const CalendarGrid = forwardRef<View, ViewProps>(({ className, style, ...props }, ref) => {
|
|
77
|
+
const { value, onValueChange, monthId, minValue, maxValue, disabled, locale } =
|
|
78
|
+
useCalendarContext();
|
|
79
|
+
const instanceId = useId();
|
|
80
|
+
const theme = useFlashCalendarTheme(value);
|
|
81
|
+
|
|
82
|
+
const formatWeekday = useCallback(
|
|
83
|
+
(date: Date) => formatFlashCalendarWeekdayShort(date, locale),
|
|
84
|
+
[locale],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const activeRanges = useMemo(() => (value ? [{ startId: value, endId: value }] : []), [value]);
|
|
88
|
+
|
|
89
|
+
const handleDayPress = useCallback(
|
|
90
|
+
(dateId: string) => {
|
|
91
|
+
if (!disabled) {
|
|
92
|
+
onValueChange?.(toCalendarDateId(dateId));
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
[disabled, onValueChange],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<CalendarGridPrimitive ref={ref} className={className} style={style} {...props}>
|
|
100
|
+
<FlashCalendar
|
|
101
|
+
calendarInstanceId={instanceId}
|
|
102
|
+
calendarMonthId={monthId}
|
|
103
|
+
calendarActiveDateRanges={activeRanges}
|
|
104
|
+
calendarMinDateId={minValue}
|
|
105
|
+
calendarMaxDateId={maxValue}
|
|
106
|
+
calendarMonthHeaderHeight={0}
|
|
107
|
+
calendarDayHeight={FLASH_CALENDAR_DAY_HEIGHT}
|
|
108
|
+
calendarWeekHeaderHeight={28}
|
|
109
|
+
calendarRowVerticalSpacing={4}
|
|
110
|
+
calendarRowHorizontalSpacing={4}
|
|
111
|
+
getCalendarMonthFormat={() => ''}
|
|
112
|
+
getCalendarWeekDayFormat={formatWeekday}
|
|
113
|
+
onCalendarDayPress={handleDayPress}
|
|
114
|
+
theme={theme}
|
|
115
|
+
/>
|
|
116
|
+
</CalendarGridPrimitive>
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
CalendarGrid.displayName = 'Calendar.Grid';
|
|
121
|
+
|
|
122
|
+
const CalendarYearPicker = forwardRef<View, ViewProps>(({ className, style, ...props }, ref) => {
|
|
123
|
+
const { monthId, onMonthIdChange, disabled, setView, labels } = useCalendarContext();
|
|
124
|
+
return (
|
|
125
|
+
<CalendarYearPickerGrid
|
|
126
|
+
ref={ref}
|
|
127
|
+
monthId={monthId}
|
|
128
|
+
onMonthIdChange={onMonthIdChange}
|
|
129
|
+
disabled={disabled}
|
|
130
|
+
onViewChange={setView}
|
|
131
|
+
labels={labels}
|
|
132
|
+
className={className}
|
|
133
|
+
style={style}
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
CalendarYearPicker.displayName = 'Calendar.YearPicker';
|
|
140
|
+
|
|
141
|
+
type CalendarCompound = typeof CalendarRoot & {
|
|
142
|
+
Header: typeof CalendarHeader;
|
|
143
|
+
Grid: typeof CalendarGrid;
|
|
144
|
+
YearPicker: typeof CalendarYearPicker;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const Calendar = Object.assign(CalendarRoot, {
|
|
148
|
+
Header: CalendarHeader,
|
|
149
|
+
Grid: CalendarGrid,
|
|
150
|
+
YearPicker: CalendarYearPicker,
|
|
151
|
+
}) as CalendarCompound;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { cva, type VariantProps } from '@cdx-ui/utils';
|
|
2
|
+
import { DISABLED_CURSOR, DISABLED_OPACITY } from '../../styles/constants';
|
|
3
|
+
|
|
4
|
+
export const calendarRootVariants = cva([
|
|
5
|
+
'gap-3 rounded-[var(--border-radius-default)] bg-surface-primary p-3',
|
|
6
|
+
DISABLED_OPACITY,
|
|
7
|
+
DISABLED_CURSOR,
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
export const calendarMonthHeaderVariants = cva('flex-row items-center justify-between gap-2 px-1');
|
|
11
|
+
|
|
12
|
+
export const calendarMonthHeaderToggleVariants = cva('min-w-0 flex-1 flex-row items-center gap-1');
|
|
13
|
+
|
|
14
|
+
export const calendarMonthHeaderLabelVariants = cva('body-lg font-semibold shrink', {
|
|
15
|
+
variants: {
|
|
16
|
+
isYearView: {
|
|
17
|
+
true: 'text-content-action',
|
|
18
|
+
false: 'text-content-primary',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: { isYearView: false },
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const calendarMonthHeaderIconVariants = cva('size-5 shrink-0', {
|
|
25
|
+
variants: {
|
|
26
|
+
isYearView: {
|
|
27
|
+
true: 'text-content-action',
|
|
28
|
+
false: 'text-content-secondary',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
defaultVariants: { isYearView: false },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export const calendarMonthHeaderNavVariants = cva('flex-row items-center gap-1');
|
|
35
|
+
|
|
36
|
+
export const calendarYearPickerGridVariants = cva(
|
|
37
|
+
'flex-row flex-wrap justify-between gap-y-2 px-1 py-1',
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const calendarPillCellVariants = cva(
|
|
41
|
+
'my-0.5 w-[31%] items-center justify-center rounded-[var(--border-radius-round)] py-2.5',
|
|
42
|
+
{
|
|
43
|
+
variants: {
|
|
44
|
+
selected: {
|
|
45
|
+
true: 'bg-surface-action-strong',
|
|
46
|
+
false: 'bg-transparent',
|
|
47
|
+
},
|
|
48
|
+
// Dims out-of-range / disabled cells and gates the compound variant
|
|
49
|
+
// below so hover is suppressed on disabled cells.
|
|
50
|
+
disabled: {
|
|
51
|
+
true: DISABLED_OPACITY,
|
|
52
|
+
false: '',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
compoundVariants: [
|
|
56
|
+
{
|
|
57
|
+
selected: false,
|
|
58
|
+
disabled: false,
|
|
59
|
+
className: 'web:hover:bg-surface-action-subtle',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
defaultVariants: { selected: false, disabled: false },
|
|
63
|
+
},
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
export const calendarPillCellTextVariants = cva('body-md', {
|
|
67
|
+
variants: {
|
|
68
|
+
selected: {
|
|
69
|
+
true: 'font-semibold text-content-action-on-strong',
|
|
70
|
+
false: 'text-content-primary',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
defaultVariants: { selected: false },
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export type CalendarRootVariantProps = VariantProps<typeof calendarRootVariants>;
|
|
77
|
+
export type CalendarMonthHeaderLabelVariantProps = VariantProps<
|
|
78
|
+
typeof calendarMonthHeaderLabelVariants
|
|
79
|
+
>;
|
|
80
|
+
export type CalendarPillCellVariantProps = VariantProps<typeof calendarPillCellVariants>;
|