cpk-ui 0.5.5 → 0.8.0-beta.0
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/README.md +80 -147
- package/components/modals/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/components/modals/AlertDialog/AlertDialog.js +14 -7
- package/components/modals/Snackbar/Snackbar.d.ts.map +1 -1
- package/components/modals/Snackbar/Snackbar.js +20 -17
- package/components/uis/Accordion/AccordionItem.d.ts.map +1 -1
- package/components/uis/Accordion/AccordionItem.js +41 -17
- package/components/uis/Button/Button.d.ts.map +1 -1
- package/components/uis/Button/Button.js +10 -10
- package/components/uis/Calendar/Calendar.d.ts +6 -0
- package/components/uis/Calendar/Calendar.d.ts.map +1 -0
- package/components/uis/Calendar/Calendar.js +482 -0
- package/components/uis/Calendar/CalendarDay.d.ts +21 -0
- package/components/uis/Calendar/CalendarDay.d.ts.map +1 -0
- package/components/uis/Calendar/CalendarDay.js +172 -0
- package/components/uis/Calendar/CalendarHeader.d.ts +22 -0
- package/components/uis/Calendar/CalendarHeader.d.ts.map +1 -0
- package/components/uis/Calendar/CalendarHeader.js +40 -0
- package/components/uis/Calendar/CalendarPage.d.ts +46 -0
- package/components/uis/Calendar/CalendarPage.d.ts.map +1 -0
- package/components/uis/Calendar/CalendarPage.js +34 -0
- package/components/uis/Calendar/CalendarPager.d.ts +11 -0
- package/components/uis/Calendar/CalendarPager.d.ts.map +1 -0
- package/components/uis/Calendar/CalendarPager.js +115 -0
- package/components/uis/Calendar/CalendarPager.web.d.ts +11 -0
- package/components/uis/Calendar/CalendarPager.web.d.ts.map +1 -0
- package/components/uis/Calendar/CalendarPager.web.js +72 -0
- package/components/uis/Calendar/CalendarWeekRow.d.ts +20 -0
- package/components/uis/Calendar/CalendarWeekRow.d.ts.map +1 -0
- package/components/uis/Calendar/CalendarWeekRow.js +64 -0
- package/components/uis/Calendar/CalendarWeekdayRow.d.ts +24 -0
- package/components/uis/Calendar/CalendarWeekdayRow.d.ts.map +1 -0
- package/components/uis/Calendar/CalendarWeekdayRow.js +45 -0
- package/components/uis/Calendar/intl.d.ts +18 -0
- package/components/uis/Calendar/intl.d.ts.map +1 -0
- package/components/uis/Calendar/intl.js +146 -0
- package/components/uis/Calendar/palette.d.ts +10 -0
- package/components/uis/Calendar/palette.d.ts.map +1 -0
- package/components/uis/Calendar/palette.js +93 -0
- package/components/uis/Calendar/types.d.ts +286 -0
- package/components/uis/Calendar/types.d.ts.map +1 -0
- package/components/uis/Calendar/types.js +1 -0
- package/components/uis/Calendar/useCalendarState.d.ts +66 -0
- package/components/uis/Calendar/useCalendarState.d.ts.map +1 -0
- package/components/uis/Calendar/useCalendarState.js +211 -0
- package/components/uis/Calendar/utils.d.ts +114 -0
- package/components/uis/Calendar/utils.d.ts.map +1 -0
- package/components/uis/Calendar/utils.js +429 -0
- package/components/uis/Card/Card.d.ts +2 -1
- package/components/uis/Card/Card.d.ts.map +1 -1
- package/components/uis/Card/Card.js +12 -3
- package/components/uis/Checkbox/Checkbox.d.ts.map +1 -1
- package/components/uis/Checkbox/Checkbox.js +1 -1
- package/components/uis/CustomPressable/CustomPressable.d.ts.map +1 -1
- package/components/uis/CustomPressable/CustomPressable.js +6 -6
- package/components/uis/EditText/EditText.d.ts.map +1 -1
- package/components/uis/EditText/EditText.js +17 -12
- package/components/uis/Fab/Fab.d.ts +2 -2
- package/components/uis/Fab/Fab.d.ts.map +1 -1
- package/components/uis/Fab/Fab.js +22 -4
- package/components/uis/Icon/Icon.d.ts.map +1 -1
- package/components/uis/Icon/Icon.js +6 -3
- package/components/uis/IconButton/IconButton.d.ts.map +1 -1
- package/components/uis/IconButton/IconButton.js +43 -41
- package/components/uis/LoadingIndicator/LoadingIndicator.d.ts +2 -1
- package/components/uis/LoadingIndicator/LoadingIndicator.d.ts.map +1 -1
- package/components/uis/LoadingIndicator/LoadingIndicator.js +4 -4
- package/components/uis/ParallaxHeader/ParallaxHeader.d.ts +44 -0
- package/components/uis/ParallaxHeader/ParallaxHeader.d.ts.map +1 -0
- package/components/uis/ParallaxHeader/ParallaxHeader.js +142 -0
- package/components/uis/RadioGroup/RadioButton.d.ts.map +1 -1
- package/components/uis/RadioGroup/RadioButton.js +1 -1
- package/components/uis/RadioGroup/RadioGroup.d.ts.map +1 -1
- package/components/uis/RadioGroup/RadioGroup.js +16 -3
- package/components/uis/Rating/Rating.d.ts.map +1 -1
- package/components/uis/Rating/Rating.js +23 -19
- package/components/uis/SegmentedControl/SegmentedControl.d.ts.map +1 -1
- package/components/uis/SegmentedControl/SegmentedControl.js +1 -1
- package/components/uis/StatusbarBrightness/StatusBarBrightness.d.ts +2 -2
- package/components/uis/StatusbarBrightness/StatusBarBrightness.d.ts.map +1 -1
- package/components/uis/StatusbarBrightness/StatusBarBrightness.js +1 -1
- package/components/uis/SwitchToggle/SwitchToggle.d.ts.map +1 -1
- package/components/uis/SwitchToggle/SwitchToggle.js +1 -1
- package/hooks/useHoverState.d.ts +8 -0
- package/hooks/useHoverState.d.ts.map +1 -0
- package/hooks/useHoverState.js +16 -0
- package/index.d.ts +2 -0
- package/index.d.ts.map +1 -1
- package/index.js +2 -0
- package/package.json +19 -6
- package/providers/ThemeProvider.d.ts.map +1 -1
- package/providers/ThemeProvider.js +0 -1
- package/providers/index.d.ts.map +1 -1
- package/providers/index.js +3 -2
- package/components/uis/Icon/selection.json +0 -1
- package/src/components/common/ErrorBoundary/ErrorBoundary.tsx +0 -75
- package/src/components/modals/AlertDialog/AlertDialog.stories.tsx +0 -216
- package/src/components/modals/AlertDialog/AlertDialog.test.tsx +0 -251
- package/src/components/modals/AlertDialog/AlertDialog.tsx +0 -356
- package/src/components/modals/Snackbar/Snackbar.stories.tsx +0 -182
- package/src/components/modals/Snackbar/Snackbar.test.tsx +0 -319
- package/src/components/modals/Snackbar/Snackbar.tsx +0 -260
- package/src/components/modals/Snackbar/const.ts +0 -4
- package/src/components/uis/Accordion/Accordion.stories.tsx +0 -167
- package/src/components/uis/Accordion/Accordion.test.tsx +0 -272
- package/src/components/uis/Accordion/Accordion.tsx +0 -109
- package/src/components/uis/Accordion/AccordionItem.tsx +0 -306
- package/src/components/uis/Button/Button.stories.tsx +0 -218
- package/src/components/uis/Button/Button.test.tsx +0 -694
- package/src/components/uis/Button/Button.tsx +0 -440
- package/src/components/uis/Card/Card.test.tsx +0 -146
- package/src/components/uis/Card/Card.tsx +0 -102
- package/src/components/uis/Checkbox/Checkbox.stories.tsx +0 -216
- package/src/components/uis/Checkbox/Checkbox.test.tsx +0 -267
- package/src/components/uis/Checkbox/Checkbox.tsx +0 -225
- package/src/components/uis/CustomPressable/CustomPressable.tsx +0 -48
- package/src/components/uis/EditText/EditText.stories.tsx +0 -124
- package/src/components/uis/EditText/EditText.test.tsx +0 -477
- package/src/components/uis/EditText/EditText.tsx +0 -593
- package/src/components/uis/Fab/Fab.stories.tsx +0 -133
- package/src/components/uis/Fab/Fab.test.tsx +0 -110
- package/src/components/uis/Fab/Fab.tsx +0 -168
- package/src/components/uis/Hr/Hr.test.tsx +0 -26
- package/src/components/uis/Hr/Hr.tsx +0 -19
- package/src/components/uis/Icon/Icon.stories.tsx +0 -98
- package/src/components/uis/Icon/Icon.test.tsx +0 -49
- package/src/components/uis/Icon/Icon.tsx +0 -3819
- package/src/components/uis/IconButton/IconButton.test.tsx +0 -204
- package/src/components/uis/IconButton/IconButton.tsx +0 -279
- package/src/components/uis/LoadingIndicator/LoadingIndicator.tsx +0 -126
- package/src/components/uis/LoadingIndicator/LodingIndicator.stories.tsx +0 -81
- package/src/components/uis/PinchZoom/PinchZoom.stories.tsx +0 -266
- package/src/components/uis/PinchZoom/PinchZoom.test.tsx +0 -113
- package/src/components/uis/PinchZoom/PinchZoom.tsx +0 -424
- package/src/components/uis/RadioGroup/RadioButton.tsx +0 -193
- package/src/components/uis/RadioGroup/RadioGroup.stories.tsx +0 -188
- package/src/components/uis/RadioGroup/RadioGroup.test.tsx +0 -354
- package/src/components/uis/RadioGroup/RadioGroup.tsx +0 -109
- package/src/components/uis/Rating/Rating.test.tsx +0 -98
- package/src/components/uis/Rating/Rating.tsx +0 -200
- package/src/components/uis/SegmentedControl/SegmentedControl.stories.tsx +0 -546
- package/src/components/uis/SegmentedControl/SegmentedControl.test.tsx +0 -274
- package/src/components/uis/SegmentedControl/SegmentedControl.tsx +0 -232
- package/src/components/uis/StatusbarBrightness/StatusBarBrightness.test.tsx +0 -41
- package/src/components/uis/StatusbarBrightness/StatusBarBrightness.tsx +0 -26
- package/src/components/uis/Styled/StyledComponents.tsx +0 -217
- package/src/components/uis/SwitchToggle/SwitchToggle.stories.tsx +0 -75
- package/src/components/uis/SwitchToggle/SwitchToggle.test.tsx +0 -149
- package/src/components/uis/SwitchToggle/SwitchToggle.tsx +0 -309
- package/src/components/uis/Typography/Typography.stories.tsx +0 -149
- package/src/components/uis/Typography/Typography.test.tsx +0 -58
- package/src/components/uis/Typography/Typography.tsx +0 -386
- package/src/hooks/index.ts +0 -2
- package/src/hooks/useAnimations.ts +0 -71
- package/src/hooks/useDebouncedColorScheme.tsx +0 -30
- package/src/hooks/useOptimizedStyles.ts +0 -22
- package/src/hooks/usePerformance.ts +0 -93
- package/src/index.tsx +0 -38
- package/src/providers/ThemeProvider.tsx +0 -177
- package/src/providers/index.tsx +0 -130
- package/src/test/testUtils.tsx +0 -101
- package/src/types/common.ts +0 -77
- package/src/types/index.ts +0 -1
- package/src/types/kstyled.d.ts +0 -6
- package/src/utils/accessibility.ts +0 -110
- package/src/utils/colors.ts +0 -151
- package/src/utils/createCtx.tsx +0 -26
- package/src/utils/guards.ts +0 -93
- package/src/utils/index.ts +0 -2
- package/src/utils/performanceTest.ts +0 -174
- package/src/utils/platform.ts +0 -69
- package/src/utils/theme.ts +0 -38
- package/test/testUtils.d.ts +0 -113
- package/test/testUtils.d.ts.map +0 -1
- package/test/testUtils.js +0 -65
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { AccessibilityInfo, I18nManager, useWindowDimensions, View, } from 'react-native';
|
|
4
|
+
import { css } from 'kstyled';
|
|
5
|
+
import { useAnimatedReaction, useSharedValue, withTiming, runOnJS, } from 'react-native-reanimated';
|
|
6
|
+
import { useTheme } from '../../../providers/ThemeProvider';
|
|
7
|
+
import { CalendarHeader } from './CalendarHeader';
|
|
8
|
+
import { CalendarPage } from './CalendarPage';
|
|
9
|
+
import { CalendarPager } from './CalendarPager';
|
|
10
|
+
import { CalendarWeekdayRow } from './CalendarWeekdayRow';
|
|
11
|
+
import { DEFAULT_FORMATTERS, getFormattersId, resolveLocale } from './intl';
|
|
12
|
+
import { isSamePalette, resolveCalendarPalette } from './palette';
|
|
13
|
+
import { resolveKeyboardAction, useControllable, useDisabledState, useReduceMotion, useShallowStable, useToday, } from './useCalendarState';
|
|
14
|
+
import { addDays, addPages, getCalendarGrid, localNoon, normalizePageKey, pageFirstDate, pageLastDate, resolveMetrics, resolveWeekStart, startOfWeek, toDateKeyInput, toDayKey, } from './utils';
|
|
15
|
+
const CONTAINER = css `
|
|
16
|
+
align-self: center;
|
|
17
|
+
width: 100%;
|
|
18
|
+
`;
|
|
19
|
+
const TWO_UP_ROW = css `
|
|
20
|
+
flex-direction: row;
|
|
21
|
+
align-items: flex-start;
|
|
22
|
+
gap: 24px;
|
|
23
|
+
`;
|
|
24
|
+
const COLUMN = css `
|
|
25
|
+
flex: 1;
|
|
26
|
+
`;
|
|
27
|
+
const EMPTY_MARKERS = Object.freeze({});
|
|
28
|
+
const EMPTY_STYLES = Object.freeze({});
|
|
29
|
+
const DEFAULT_LABELS = Object.freeze({
|
|
30
|
+
previousMonth: 'Previous month',
|
|
31
|
+
nextMonth: 'Next month',
|
|
32
|
+
previousWeek: 'Previous week',
|
|
33
|
+
nextWeek: 'Next week',
|
|
34
|
+
today: 'Today',
|
|
35
|
+
selected: 'Selected',
|
|
36
|
+
weekNumber: 'Week',
|
|
37
|
+
pageAnnouncement: (pageTitle) => pageTitle,
|
|
38
|
+
});
|
|
39
|
+
/** A Sunday, used only to derive weekday header labels. Never rendered. */
|
|
40
|
+
const WEEKDAY_LABEL_ANCHOR = localNoon(2024, 0, 7);
|
|
41
|
+
const COLLAPSE_DURATION = 220;
|
|
42
|
+
const BADGE_FONT_SCALE_LIMIT = 1.3;
|
|
43
|
+
const SINGLE_DOT_FONT_SCALE_LIMIT = 1.5;
|
|
44
|
+
/**
|
|
45
|
+
* `onKeyDown` is forwarded by react-native-web and simply never fires on native,
|
|
46
|
+
* so one handler covers both platforms without a `.web.tsx` split.
|
|
47
|
+
*/
|
|
48
|
+
const KeyboardView = View;
|
|
49
|
+
function buildGeometry(metrics) {
|
|
50
|
+
return Object.freeze({
|
|
51
|
+
row: { height: metrics.rowHeight },
|
|
52
|
+
cell: { height: metrics.rowHeight, paddingTop: metrics.pillOffset },
|
|
53
|
+
pill: {
|
|
54
|
+
width: metrics.numberSize,
|
|
55
|
+
height: metrics.numberSize,
|
|
56
|
+
borderRadius: metrics.numberSize / 2,
|
|
57
|
+
},
|
|
58
|
+
dayText: { fontSize: metrics.dayFontSize },
|
|
59
|
+
badgeText: { fontSize: metrics.badgeFontSize },
|
|
60
|
+
dot: {
|
|
61
|
+
width: metrics.dotSize,
|
|
62
|
+
height: metrics.dotSize,
|
|
63
|
+
borderRadius: metrics.dotSize / 2,
|
|
64
|
+
},
|
|
65
|
+
dotRow: { height: metrics.dotRowHeight },
|
|
66
|
+
weekNumber: { width: metrics.weekNumberWidth },
|
|
67
|
+
weekNumberText: { fontSize: metrics.weekdayFontSize },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function CalendarContainer(props, ref) {
|
|
71
|
+
const { value, defaultValue, onChange, month, defaultMonth, onMonthChange, mode: modeProp, defaultMode, onModeChange, collapseProgress, locale: localeProp, weekStart: weekStartProp, today: todayProp, fixedWeeks = true, markers = EMPTY_MARKERS, showDots = true, maxDotsPerDay = 3, showWeekNumbers = false, showOutsideDays = true, minDate, maxDate, disabledDates, isDateDisabled, showHeader = true, showNavigationButtons = true, showWeekdayRow = true, paging = 'swipe', monthsToShow = 1, size, maxFontSizeMultiplier = 1.6, colors, styles: stylesProp = EMPTY_STYLES, style, formatters: formattersProp, renderHeader, renderWeekdayLabel, renderDay, onDayLongPress, onDisabledDayPress, labels: labelsPropInput, accessibilityLabel, testID = 'calendar', } = props;
|
|
72
|
+
const { theme, media } = useTheme();
|
|
73
|
+
const { fontScale } = useWindowDimensions();
|
|
74
|
+
const reduceMotion = useReduceMotion();
|
|
75
|
+
const locale = useMemo(() => localeProp ?? resolveLocale(), [localeProp]);
|
|
76
|
+
const weekStart = useMemo(() => weekStartProp ?? resolveWeekStart(locale), [locale, weekStartProp]);
|
|
77
|
+
const todayNoon = useToday(todayProp);
|
|
78
|
+
const todayKey = useMemo(() => toDayKey(todayNoon), [todayNoon]);
|
|
79
|
+
// Value comparison, not identity, for the three props callers almost always
|
|
80
|
+
// write as inline object literals. Without it `chrome` is rebuilt on every
|
|
81
|
+
// parent render and all 42 day cells re-render — the exact regression the
|
|
82
|
+
// memo test suite pins for `colors` but never checked for these.
|
|
83
|
+
const labelsProp = useShallowStable(labelsPropInput);
|
|
84
|
+
const styles = useShallowStable(stylesProp);
|
|
85
|
+
const formattersInput = useShallowStable(formattersProp);
|
|
86
|
+
const formatters = useMemo(() => formattersInput
|
|
87
|
+
? Object.freeze({ ...DEFAULT_FORMATTERS, ...formattersInput })
|
|
88
|
+
: DEFAULT_FORMATTERS, [formattersInput]);
|
|
89
|
+
const formattersId = getFormattersId(formatters);
|
|
90
|
+
const [mode, setModeState] = useControllable(modeProp, defaultMode ?? 'month');
|
|
91
|
+
// ── selection ───────────────────────────────────────────────────────────────
|
|
92
|
+
const isValueControlled = value !== undefined;
|
|
93
|
+
const [selectedKeyState, setSelectedKeyState] = useState(() => defaultValue == null ? null : toDateKeyInput(defaultValue));
|
|
94
|
+
const selectedKey = isValueControlled
|
|
95
|
+
? value === null
|
|
96
|
+
? null
|
|
97
|
+
: toDateKeyInput(value)
|
|
98
|
+
: selectedKeyState;
|
|
99
|
+
// ── paging ──────────────────────────────────────────────────────────────────
|
|
100
|
+
const isMonthControlled = month !== undefined;
|
|
101
|
+
const [pageKeyState, setPageKeyState] = useState(() => normalizePageKey(defaultMonth ?? selectedKeyState ?? todayNoon, defaultMode ?? 'month', weekStartProp ?? resolveWeekStart(localeProp ?? resolveLocale()), monthsToShow));
|
|
102
|
+
const pageInput = month === undefined
|
|
103
|
+
? pageKeyState
|
|
104
|
+
: typeof month === 'string'
|
|
105
|
+
? month
|
|
106
|
+
: toDayKey(month);
|
|
107
|
+
const pageKey = useMemo(() => normalizePageKey(pageInput, mode, weekStart, monthsToShow), [mode, monthsToShow, pageInput, weekStart]);
|
|
108
|
+
const [focusedKey, setFocusedKey] = useState(null);
|
|
109
|
+
// ── grids ───────────────────────────────────────────────────────────────────
|
|
110
|
+
const gridFor = useCallback((key) => getCalendarGrid({
|
|
111
|
+
pageKey: key,
|
|
112
|
+
mode,
|
|
113
|
+
weekStart,
|
|
114
|
+
fixedWeeks,
|
|
115
|
+
showWeekNumbers,
|
|
116
|
+
locale,
|
|
117
|
+
formatters,
|
|
118
|
+
formattersId,
|
|
119
|
+
}), [
|
|
120
|
+
fixedWeeks,
|
|
121
|
+
formatters,
|
|
122
|
+
formattersId,
|
|
123
|
+
locale,
|
|
124
|
+
mode,
|
|
125
|
+
showWeekNumbers,
|
|
126
|
+
weekStart,
|
|
127
|
+
]);
|
|
128
|
+
const previousPageKey = useMemo(() => addPages(pageKey, -1, mode, weekStart, monthsToShow), [mode, monthsToShow, pageKey, weekStart]);
|
|
129
|
+
const nextPageKey = useMemo(() => addPages(pageKey, 1, mode, weekStart, monthsToShow), [mode, monthsToShow, pageKey, weekStart]);
|
|
130
|
+
const currentGrid = useMemo(() => gridFor(pageKey), [gridFor, pageKey]);
|
|
131
|
+
// ── theming and geometry ────────────────────────────────────────────────────
|
|
132
|
+
// Value comparison, not identity: `theme` and an inline `colors` object are both
|
|
133
|
+
// rebuilt on every parent render, and a new palette would re-render every cell.
|
|
134
|
+
const paletteRef = useRef(null);
|
|
135
|
+
const palette = useMemo(() => {
|
|
136
|
+
const next = resolveCalendarPalette(theme, colors);
|
|
137
|
+
const previous = paletteRef.current;
|
|
138
|
+
if (previous && isSamePalette(previous, next)) {
|
|
139
|
+
return previous;
|
|
140
|
+
}
|
|
141
|
+
paletteRef.current = next;
|
|
142
|
+
return next;
|
|
143
|
+
}, [colors, theme]);
|
|
144
|
+
const resolvedSize = size ?? (media.isDesktop || media.isTablet ? 'expanded' : 'regular');
|
|
145
|
+
const metrics = useMemo(() => resolveMetrics(resolvedSize, fontScale, maxFontSizeMultiplier), [fontScale, maxFontSizeMultiplier, resolvedSize]);
|
|
146
|
+
const geometry = useMemo(() => buildGeometry(metrics), [metrics]);
|
|
147
|
+
const labels = useMemo(() => labelsProp
|
|
148
|
+
? Object.freeze({ ...DEFAULT_LABELS, ...labelsProp })
|
|
149
|
+
: DEFAULT_LABELS, [labelsProp]);
|
|
150
|
+
// ── constraints ─────────────────────────────────────────────────────────────
|
|
151
|
+
const { resolver: isDisabled, version: disabledVersion, minKey, maxKey, } = useDisabledState({ minDate, maxDate, disabledDates, isDateDisabled });
|
|
152
|
+
// ── latest-value refs, so no handler identity depends on a changing callback ─
|
|
153
|
+
const callbacksRef = useRef({
|
|
154
|
+
onChange,
|
|
155
|
+
onMonthChange,
|
|
156
|
+
onModeChange,
|
|
157
|
+
onDayLongPress,
|
|
158
|
+
onDisabledDayPress,
|
|
159
|
+
});
|
|
160
|
+
const stateRef = useRef({
|
|
161
|
+
pageKey,
|
|
162
|
+
mode,
|
|
163
|
+
weekStart,
|
|
164
|
+
monthsToShow,
|
|
165
|
+
isMonthControlled,
|
|
166
|
+
isValueControlled,
|
|
167
|
+
isModeControlled: modeProp !== undefined,
|
|
168
|
+
});
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
callbacksRef.current = {
|
|
171
|
+
onChange,
|
|
172
|
+
onMonthChange,
|
|
173
|
+
onModeChange,
|
|
174
|
+
onDayLongPress,
|
|
175
|
+
onDisabledDayPress,
|
|
176
|
+
};
|
|
177
|
+
}, [
|
|
178
|
+
onChange,
|
|
179
|
+
onDayLongPress,
|
|
180
|
+
onDisabledDayPress,
|
|
181
|
+
onModeChange,
|
|
182
|
+
onMonthChange,
|
|
183
|
+
]);
|
|
184
|
+
useEffect(() => {
|
|
185
|
+
stateRef.current = {
|
|
186
|
+
pageKey,
|
|
187
|
+
mode,
|
|
188
|
+
weekStart,
|
|
189
|
+
monthsToShow,
|
|
190
|
+
isMonthControlled,
|
|
191
|
+
isValueControlled,
|
|
192
|
+
isModeControlled: modeProp !== undefined,
|
|
193
|
+
};
|
|
194
|
+
}, [
|
|
195
|
+
isMonthControlled,
|
|
196
|
+
isValueControlled,
|
|
197
|
+
mode,
|
|
198
|
+
modeProp,
|
|
199
|
+
monthsToShow,
|
|
200
|
+
pageKey,
|
|
201
|
+
weekStart,
|
|
202
|
+
]);
|
|
203
|
+
const goToPage = useCallback((nextKey, source) => {
|
|
204
|
+
const current = stateRef.current;
|
|
205
|
+
if (!current.isMonthControlled) {
|
|
206
|
+
setPageKeyState(nextKey);
|
|
207
|
+
}
|
|
208
|
+
const first = pageFirstDate(nextKey, current.mode, current.weekStart);
|
|
209
|
+
callbacksRef.current.onMonthChange?.(new Date(first.getFullYear(), first.getMonth(), first.getDate()), nextKey, source);
|
|
210
|
+
}, []);
|
|
211
|
+
const stepPage = useCallback((delta, source) => {
|
|
212
|
+
const current = stateRef.current;
|
|
213
|
+
goToPage(addPages(current.pageKey, delta, current.mode, current.weekStart, current.monthsToShow), source);
|
|
214
|
+
}, [goToPage]);
|
|
215
|
+
const handleSwipeCommit = useCallback((delta) => stepPage(delta, 'swipe'), [stepPage]);
|
|
216
|
+
const focusKey = useCallback((key, source) => {
|
|
217
|
+
setFocusedKey(key);
|
|
218
|
+
const current = stateRef.current;
|
|
219
|
+
const targetPage = normalizePageKey(key, current.mode, current.weekStart, current.monthsToShow);
|
|
220
|
+
if (targetPage !== current.pageKey) {
|
|
221
|
+
goToPage(targetPage, source);
|
|
222
|
+
}
|
|
223
|
+
}, [goToPage]);
|
|
224
|
+
const selectKey = useCallback((key, source) => {
|
|
225
|
+
const current = stateRef.current;
|
|
226
|
+
if (!current.isValueControlled) {
|
|
227
|
+
setSelectedKeyState(key);
|
|
228
|
+
}
|
|
229
|
+
setFocusedKey(key);
|
|
230
|
+
const date = new Date(+key.slice(0, 4), +key.slice(5, 7) - 1, +key.slice(8, 10));
|
|
231
|
+
callbacksRef.current.onChange?.(date, key, source);
|
|
232
|
+
}, []);
|
|
233
|
+
// ── stable cell callbacks. No inline arrow ever reaches a day cell ───────────
|
|
234
|
+
const handleDayPress = useCallback((key) => selectKey(key, 'press'), [selectKey]);
|
|
235
|
+
const handleDayLongPress = useCallback((key) => {
|
|
236
|
+
callbacksRef.current.onDayLongPress?.(new Date(+key.slice(0, 4), +key.slice(5, 7) - 1, +key.slice(8, 10)), key);
|
|
237
|
+
}, []);
|
|
238
|
+
const handleDisabledDayPress = useCallback((key) => {
|
|
239
|
+
callbacksRef.current.onDisabledDayPress?.(new Date(+key.slice(0, 4), +key.slice(5, 7) - 1, +key.slice(8, 10)), key);
|
|
240
|
+
}, []);
|
|
241
|
+
const chrome = useMemo(() => Object.freeze({
|
|
242
|
+
palette,
|
|
243
|
+
metrics,
|
|
244
|
+
geometry,
|
|
245
|
+
showDots,
|
|
246
|
+
maxDots: fontScale > SINGLE_DOT_FONT_SCALE_LIMIT
|
|
247
|
+
? 1
|
|
248
|
+
: Math.max(maxDotsPerDay, 0),
|
|
249
|
+
showBadges: fontScale <= BADGE_FONT_SCALE_LIMIT,
|
|
250
|
+
maxFontSizeMultiplier,
|
|
251
|
+
fontScale,
|
|
252
|
+
labels,
|
|
253
|
+
styles,
|
|
254
|
+
renderDay,
|
|
255
|
+
onPress: handleDayPress,
|
|
256
|
+
onLongPress: onDayLongPress ? handleDayLongPress : undefined,
|
|
257
|
+
onDisabledPress: onDisabledDayPress
|
|
258
|
+
? handleDisabledDayPress
|
|
259
|
+
: undefined,
|
|
260
|
+
}), [
|
|
261
|
+
fontScale,
|
|
262
|
+
geometry,
|
|
263
|
+
handleDayLongPress,
|
|
264
|
+
handleDayPress,
|
|
265
|
+
handleDisabledDayPress,
|
|
266
|
+
labels,
|
|
267
|
+
maxDotsPerDay,
|
|
268
|
+
maxFontSizeMultiplier,
|
|
269
|
+
metrics,
|
|
270
|
+
onDayLongPress,
|
|
271
|
+
onDisabledDayPress,
|
|
272
|
+
palette,
|
|
273
|
+
renderDay,
|
|
274
|
+
showDots,
|
|
275
|
+
styles,
|
|
276
|
+
]);
|
|
277
|
+
// ── collapse ────────────────────────────────────────────────────────────────
|
|
278
|
+
const internalCollapse = useSharedValue(mode === 'week' ? 1 : 0);
|
|
279
|
+
const collapse = collapseProgress ?? internalCollapse;
|
|
280
|
+
const activeRow = useSharedValue(0);
|
|
281
|
+
const anchorKey = selectedKey &&
|
|
282
|
+
selectedKey >= currentGrid.firstKey &&
|
|
283
|
+
selectedKey <= currentGrid.lastKey
|
|
284
|
+
? selectedKey
|
|
285
|
+
: todayKey >= currentGrid.firstKey && todayKey <= currentGrid.lastKey
|
|
286
|
+
? todayKey
|
|
287
|
+
: currentGrid.firstKey;
|
|
288
|
+
const activeRowIndex = useMemo(() => {
|
|
289
|
+
for (let i = 0; i < currentGrid.days.length; i++) {
|
|
290
|
+
if (currentGrid.days[i].key === anchorKey) {
|
|
291
|
+
return (i / 7) | 0;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return 0;
|
|
295
|
+
}, [anchorKey, currentGrid]);
|
|
296
|
+
useEffect(() => {
|
|
297
|
+
activeRow.value = mode === 'week' ? 0 : activeRowIndex;
|
|
298
|
+
}, [activeRow, activeRowIndex, mode]);
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
if (collapseProgress) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
internalCollapse.value = withTiming(mode === 'week' ? 1 : 0, {
|
|
304
|
+
duration: reduceMotion ? 0 : COLLAPSE_DURATION,
|
|
305
|
+
});
|
|
306
|
+
}, [collapseProgress, internalCollapse, mode, reduceMotion]);
|
|
307
|
+
const commitMode = useCallback((nextMode) => {
|
|
308
|
+
if (stateRef.current.mode === nextMode) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
setModeState(nextMode);
|
|
312
|
+
callbacksRef.current.onModeChange?.(nextMode);
|
|
313
|
+
}, [setModeState]);
|
|
314
|
+
useAnimatedReaction(() => collapse.value, (progress, previous) => {
|
|
315
|
+
if (previous === null) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (progress >= 0.999 && previous < 0.999) {
|
|
319
|
+
runOnJS(commitMode)('week');
|
|
320
|
+
}
|
|
321
|
+
else if (progress <= 0.001 && previous > 0.001) {
|
|
322
|
+
runOnJS(commitMode)('month');
|
|
323
|
+
}
|
|
324
|
+
}, [collapse, commitMode]);
|
|
325
|
+
// ── derived chrome values ───────────────────────────────────────────────────
|
|
326
|
+
const weekdayLabels = useMemo(() => {
|
|
327
|
+
const anchor = startOfWeek(WEEKDAY_LABEL_ANCHOR, weekStart);
|
|
328
|
+
return Object.freeze(Array.from({ length: 7 }, (_, index) => formatters.weekdayLabel(addDays(anchor, index), locale)));
|
|
329
|
+
}, [formatters, locale, weekStart]);
|
|
330
|
+
const headerDate = useMemo(() => pageFirstDate(pageKey, mode, weekStart), [mode, pageKey, weekStart]);
|
|
331
|
+
const title = useMemo(() => {
|
|
332
|
+
if (mode === 'week') {
|
|
333
|
+
return formatters.weekTitle(headerDate, addDays(headerDate, 6), locale);
|
|
334
|
+
}
|
|
335
|
+
if (monthsToShow > 1) {
|
|
336
|
+
const secondMonth = pageFirstDate(nextPageKey, mode, weekStart);
|
|
337
|
+
return `${formatters.monthTitle(headerDate, locale)} – ${formatters.monthTitle(addDays(secondMonth, -1), locale)}`;
|
|
338
|
+
}
|
|
339
|
+
return formatters.monthTitle(headerDate, locale);
|
|
340
|
+
}, [
|
|
341
|
+
formatters,
|
|
342
|
+
headerDate,
|
|
343
|
+
locale,
|
|
344
|
+
mode,
|
|
345
|
+
monthsToShow,
|
|
346
|
+
nextPageKey,
|
|
347
|
+
weekStart,
|
|
348
|
+
]);
|
|
349
|
+
// Compared against each neighbouring page's own month/week range, not against the
|
|
350
|
+
// padded grid, so a min date on the 1st really does disable the previous button.
|
|
351
|
+
const canGoPrevious = minKey === null ||
|
|
352
|
+
toDayKey(pageLastDate(previousPageKey, mode, weekStart, monthsToShow)) >=
|
|
353
|
+
minKey;
|
|
354
|
+
const canGoNext = maxKey === null ||
|
|
355
|
+
toDayKey(pageFirstDate(nextPageKey, mode, weekStart)) <= maxKey;
|
|
356
|
+
const goPrevious = useCallback(() => stepPage(-1, 'button'), [stepPage]);
|
|
357
|
+
const goNext = useCallback(() => stepPage(1, 'button'), [stepPage]);
|
|
358
|
+
const headerContext = useMemo(() => ({
|
|
359
|
+
title,
|
|
360
|
+
date: headerDate,
|
|
361
|
+
mode,
|
|
362
|
+
canGoPrevious,
|
|
363
|
+
canGoNext,
|
|
364
|
+
goPrevious,
|
|
365
|
+
goNext,
|
|
366
|
+
}), [canGoNext, canGoPrevious, goNext, goPrevious, headerDate, mode, title]);
|
|
367
|
+
const announcedTitle = useRef(null);
|
|
368
|
+
useEffect(() => {
|
|
369
|
+
if (announcedTitle.current === null) {
|
|
370
|
+
announcedTitle.current = title;
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (announcedTitle.current === title) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
announcedTitle.current = title;
|
|
377
|
+
AccessibilityInfo.announceForAccessibility(labels.pageAnnouncement(title));
|
|
378
|
+
}, [labels, title]);
|
|
379
|
+
// ── roving focus target ─────────────────────────────────────────────────────
|
|
380
|
+
const tabbableKey = useMemo(() => {
|
|
381
|
+
const inPage = (key) => key >= currentGrid.firstKey && key <= currentGrid.lastKey;
|
|
382
|
+
if (focusedKey !== null && inPage(focusedKey)) {
|
|
383
|
+
return focusedKey;
|
|
384
|
+
}
|
|
385
|
+
if (selectedKey !== null && inPage(selectedKey)) {
|
|
386
|
+
return selectedKey;
|
|
387
|
+
}
|
|
388
|
+
if (inPage(todayKey)) {
|
|
389
|
+
return todayKey;
|
|
390
|
+
}
|
|
391
|
+
for (const day of currentGrid.days) {
|
|
392
|
+
if (!day.isOutside) {
|
|
393
|
+
return day.key;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return currentGrid.firstKey;
|
|
397
|
+
}, [currentGrid, focusedKey, selectedKey, todayKey]);
|
|
398
|
+
const handleKeyDown = useCallback((event) => {
|
|
399
|
+
const action = resolveKeyboardAction({
|
|
400
|
+
key: event.key,
|
|
401
|
+
shiftKey: event.shiftKey === true,
|
|
402
|
+
focusedKey: tabbableKey,
|
|
403
|
+
weekStart: stateRef.current.weekStart,
|
|
404
|
+
isRTL: I18nManager.isRTL,
|
|
405
|
+
mode: stateRef.current.mode,
|
|
406
|
+
});
|
|
407
|
+
if (!action) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
event.preventDefault?.();
|
|
411
|
+
if (action.type === 'select') {
|
|
412
|
+
selectKey(tabbableKey, 'keyboard');
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
focusKey(action.key, 'keyboard');
|
|
416
|
+
}, [focusKey, selectKey, tabbableKey]);
|
|
417
|
+
// ── imperative API ──────────────────────────────────────────────────────────
|
|
418
|
+
useImperativeHandle(ref, () => ({
|
|
419
|
+
goToMonth: (target) => {
|
|
420
|
+
const current = stateRef.current;
|
|
421
|
+
goToPage(normalizePageKey(target, current.mode, current.weekStart, current.monthsToShow), 'imperative');
|
|
422
|
+
},
|
|
423
|
+
goToToday: () => {
|
|
424
|
+
const current = stateRef.current;
|
|
425
|
+
goToPage(normalizePageKey(todayKey, current.mode, current.weekStart, current.monthsToShow), 'imperative');
|
|
426
|
+
},
|
|
427
|
+
next: () => stepPage(1, 'imperative'),
|
|
428
|
+
previous: () => stepPage(-1, 'imperative'),
|
|
429
|
+
setMode: (nextMode) => commitMode(nextMode),
|
|
430
|
+
focusDay: (key) => focusKey(key, 'imperative'),
|
|
431
|
+
}), [commitMode, focusKey, goToPage, stepPage, todayKey]);
|
|
432
|
+
// ── page rendering ──────────────────────────────────────────────────────────
|
|
433
|
+
const rowCount = mode === 'week' ? 1 : currentGrid.rowCount;
|
|
434
|
+
const collapsible = mode === 'week' || (monthsToShow === 1 && rowCount > 1);
|
|
435
|
+
const renderGrid = useCallback((gridPageKey, gridTestID) => {
|
|
436
|
+
const grid = gridFor(gridPageKey);
|
|
437
|
+
return (_jsx(CalendarPage, { activeRow: activeRow, chrome: chrome, collapse: collapse, collapsible: collapsible, disabledVersion: disabledVersion, grid: grid, isDisabled: isDisabled, markers: markers, selectedKey: selectedKey, showOutsideDays: showOutsideDays, showWeekNumbers: showWeekNumbers, tabbableKey: tabbableKey, testID: gridTestID, todayKey: todayKey }));
|
|
438
|
+
}, [
|
|
439
|
+
activeRow,
|
|
440
|
+
chrome,
|
|
441
|
+
collapse,
|
|
442
|
+
collapsible,
|
|
443
|
+
disabledVersion,
|
|
444
|
+
gridFor,
|
|
445
|
+
isDisabled,
|
|
446
|
+
markers,
|
|
447
|
+
selectedKey,
|
|
448
|
+
showOutsideDays,
|
|
449
|
+
showWeekNumbers,
|
|
450
|
+
tabbableKey,
|
|
451
|
+
todayKey,
|
|
452
|
+
]);
|
|
453
|
+
const renderSlot = useCallback((slotPageKey) => {
|
|
454
|
+
if (monthsToShow === 1) {
|
|
455
|
+
return renderGrid(slotPageKey, `calendar-page-${slotPageKey}`);
|
|
456
|
+
}
|
|
457
|
+
const monthKeys = [slotPageKey];
|
|
458
|
+
for (let index = 1; index < monthsToShow; index++) {
|
|
459
|
+
monthKeys.push(addPages(slotPageKey, index, mode, weekStart, 1));
|
|
460
|
+
}
|
|
461
|
+
return (_jsx(View, { style: TWO_UP_ROW, children: monthKeys.map((monthKey) => (_jsx(View, { style: COLUMN, children: renderGrid(monthKey, `calendar-page-${monthKey}`) }, monthKey))) }));
|
|
462
|
+
}, [mode, monthsToShow, renderGrid, weekStart]);
|
|
463
|
+
const slots = useMemo(() => {
|
|
464
|
+
if (paging === 'none') {
|
|
465
|
+
return [{ key: pageKey, offset: 0, node: renderSlot(pageKey) }];
|
|
466
|
+
}
|
|
467
|
+
return [
|
|
468
|
+
{ key: previousPageKey, offset: -1, node: renderSlot(previousPageKey) },
|
|
469
|
+
{ key: pageKey, offset: 0, node: renderSlot(pageKey) },
|
|
470
|
+
{ key: nextPageKey, offset: 1, node: renderSlot(nextPageKey) },
|
|
471
|
+
];
|
|
472
|
+
}, [nextPageKey, pageKey, paging, previousPageKey, renderSlot]);
|
|
473
|
+
const containerStyle = useMemo(() => ({
|
|
474
|
+
backgroundColor: palette.background,
|
|
475
|
+
maxWidth: metrics.cellSize * 7 * 1.6 * monthsToShow,
|
|
476
|
+
}), [metrics.cellSize, monthsToShow, palette.background]);
|
|
477
|
+
const weekdayRow = showWeekdayRow ? (_jsx(View, { style: monthsToShow > 1 ? TWO_UP_ROW : undefined, children: Array.from({ length: monthsToShow }, (_, index) => (_jsx(View, { style: COLUMN, children: _jsx(CalendarWeekdayRow, { maxFontSizeMultiplier: maxFontSizeMultiplier, metrics: metrics, palette: palette, renderWeekdayLabel: renderWeekdayLabel, showWeekNumbers: showWeekNumbers, styles: styles, weekStart: weekStart, weekdayLabels: weekdayLabels }) }, `weekday-row-${index}`))) })) : null;
|
|
478
|
+
return (_jsxs(KeyboardView, { accessibilityLabel: accessibilityLabel ?? title, onKeyDown: handleKeyDown, style: [CONTAINER, containerStyle, styles.container, style], testID: testID, children: [showHeader ? (_jsx(CalendarHeader, { canGoNext: canGoNext, canGoPrevious: canGoPrevious, headerContext: headerContext, labels: labels, maxFontSizeMultiplier: maxFontSizeMultiplier, metrics: metrics, mode: mode, onNext: goNext, onPrevious: goPrevious, palette: palette, renderHeader: renderHeader, showNavigationButtons: showNavigationButtons, styles: styles, title: title })) : null, weekdayRow, _jsx(CalendarPager, { activeRow: activeRow, collapse: collapse, onCommit: handleSwipeCommit, reduceMotion: reduceMotion, rowCount: rowCount, rowHeight: metrics.rowHeight, slots: slots, swipeEnabled: paging === 'swipe', testID: "calendar-pager" })] }));
|
|
479
|
+
}
|
|
480
|
+
export const Calendar = React.memo(forwardRef(CalendarContainer));
|
|
481
|
+
export default Calendar;
|
|
482
|
+
export * from './types';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { type ReactElement } from 'react';
|
|
2
|
+
import type { CalendarChrome, CalendarDayModel, CalendarMarker } from './types';
|
|
3
|
+
export type CalendarDayProps = {
|
|
4
|
+
day: CalendarDayModel;
|
|
5
|
+
/** Precomputed by the grid builder. A cell never formats a date itself. */
|
|
6
|
+
dayLabel: string;
|
|
7
|
+
accessibilityLabel: string;
|
|
8
|
+
isSelected: boolean;
|
|
9
|
+
isToday: boolean;
|
|
10
|
+
isDisabled: boolean;
|
|
11
|
+
/** Roving tab index: exactly one cell per page is tabbable. */
|
|
12
|
+
isTabbable: boolean;
|
|
13
|
+
/** Inert spacer for an out of month day when `showOutsideDays` is false. */
|
|
14
|
+
isHidden: boolean;
|
|
15
|
+
marker?: CalendarMarker;
|
|
16
|
+
chrome: CalendarChrome;
|
|
17
|
+
};
|
|
18
|
+
declare function CalendarDayContainer(props: CalendarDayProps): ReactElement;
|
|
19
|
+
export declare const CalendarDay: React.MemoExoticComponent<typeof CalendarDayContainer>;
|
|
20
|
+
export default CalendarDay;
|
|
21
|
+
//# sourceMappingURL=CalendarDay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CalendarDay.d.ts","sourceRoot":"","sources":["../../../../src/components/uis/Calendar/CalendarDay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuB,KAAK,YAAY,EAAC,MAAM,OAAO,CAAC;AAKrE,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EAEf,MAAM,SAAS,CAAC;AAyCjB,MAAM,MAAM,gBAAgB,GAAG;IAC7B,GAAG,EAAE,gBAAgB,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,+DAA+D;IAC/D,UAAU,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,cAAc,CAAC;CACxB,CAAC;AAoDF,iBAAS,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,CAsMnE;AAcD,eAAO,MAAM,WAAW,wDAAiD,CAAC;AAE1E,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback, useMemo } from 'react';
|
|
3
|
+
import { Platform, Pressable, Text, View } from 'react-native';
|
|
4
|
+
import { css } from 'kstyled';
|
|
5
|
+
import { isSameMarker } from './utils';
|
|
6
|
+
const CELL = css `
|
|
7
|
+
flex: 1;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: flex-start;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
border-radius: 10px;
|
|
12
|
+
`;
|
|
13
|
+
const PILL = css `
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
`;
|
|
17
|
+
const DAY_TEXT = css `
|
|
18
|
+
text-align: center;
|
|
19
|
+
font-weight: 500;
|
|
20
|
+
`;
|
|
21
|
+
const DECORATIONS = css `
|
|
22
|
+
position: absolute;
|
|
23
|
+
bottom: 0;
|
|
24
|
+
start: 0;
|
|
25
|
+
end: 0;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: flex-end;
|
|
28
|
+
`;
|
|
29
|
+
const DOT_ROW = css `
|
|
30
|
+
flex-direction: row;
|
|
31
|
+
align-items: center;
|
|
32
|
+
justify-content: center;
|
|
33
|
+
gap: 2px;
|
|
34
|
+
`;
|
|
35
|
+
const BADGE_TEXT = css `
|
|
36
|
+
text-align: center;
|
|
37
|
+
`;
|
|
38
|
+
function resolveTextColor(args) {
|
|
39
|
+
const { palette, isSelected, isDisabled, isToday, isOutside, weekday, markerTextColor, } = args;
|
|
40
|
+
if (isSelected) {
|
|
41
|
+
return palette.selectedText;
|
|
42
|
+
}
|
|
43
|
+
if (isDisabled) {
|
|
44
|
+
return palette.disabledText;
|
|
45
|
+
}
|
|
46
|
+
if (isOutside) {
|
|
47
|
+
return palette.outsideText;
|
|
48
|
+
}
|
|
49
|
+
if (markerTextColor) {
|
|
50
|
+
return markerTextColor;
|
|
51
|
+
}
|
|
52
|
+
if (isToday) {
|
|
53
|
+
return palette.todayText;
|
|
54
|
+
}
|
|
55
|
+
if (weekday === 0) {
|
|
56
|
+
return palette.sundayText;
|
|
57
|
+
}
|
|
58
|
+
if (weekday === 6) {
|
|
59
|
+
return palette.saturdayText;
|
|
60
|
+
}
|
|
61
|
+
return palette.dayText;
|
|
62
|
+
}
|
|
63
|
+
function CalendarDayContainer(props) {
|
|
64
|
+
const { day, dayLabel, accessibilityLabel, isSelected, isToday, isDisabled, isTabbable, isHidden, marker, chrome, } = props;
|
|
65
|
+
const { palette, metrics, geometry, styles } = chrome;
|
|
66
|
+
const pillStyle = useMemo(() => {
|
|
67
|
+
if (isSelected) {
|
|
68
|
+
return {
|
|
69
|
+
backgroundColor: marker?.selectedColor ?? palette.selectedBackground,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (isToday) {
|
|
73
|
+
return { borderWidth: 1.5, borderColor: palette.todayRing };
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}, [isSelected, isToday, marker?.selectedColor, palette]);
|
|
77
|
+
const textStyle = useMemo(() => ({
|
|
78
|
+
color: resolveTextColor({
|
|
79
|
+
palette,
|
|
80
|
+
isSelected,
|
|
81
|
+
isDisabled,
|
|
82
|
+
isToday,
|
|
83
|
+
isOutside: day.isOutside,
|
|
84
|
+
weekday: day.weekday,
|
|
85
|
+
markerTextColor: marker?.textColor,
|
|
86
|
+
}),
|
|
87
|
+
opacity: isDisabled ? 0.55 : 1,
|
|
88
|
+
}), [
|
|
89
|
+
day.isOutside,
|
|
90
|
+
day.weekday,
|
|
91
|
+
isDisabled,
|
|
92
|
+
isSelected,
|
|
93
|
+
isToday,
|
|
94
|
+
marker?.textColor,
|
|
95
|
+
palette,
|
|
96
|
+
]);
|
|
97
|
+
const handlePress = useCallback(() => {
|
|
98
|
+
if (isDisabled) {
|
|
99
|
+
chrome.onDisabledPress?.(day.key);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
chrome.onPress(day.key);
|
|
103
|
+
}, [chrome, day.key, isDisabled]);
|
|
104
|
+
const handleLongPress = useCallback(() => {
|
|
105
|
+
if (isDisabled) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
chrome.onLongPress?.(day.key);
|
|
109
|
+
}, [chrome, day.key, isDisabled]);
|
|
110
|
+
const cellStyle = useCallback(({ pressed }) => pressed && !isDisabled
|
|
111
|
+
? [
|
|
112
|
+
CELL,
|
|
113
|
+
geometry.cell,
|
|
114
|
+
{ backgroundColor: palette.underlay },
|
|
115
|
+
styles.dayCell,
|
|
116
|
+
isSelected ? styles.selectedDayCell : null,
|
|
117
|
+
isToday ? styles.todayDayCell : null,
|
|
118
|
+
]
|
|
119
|
+
: [
|
|
120
|
+
CELL,
|
|
121
|
+
geometry.cell,
|
|
122
|
+
styles.dayCell,
|
|
123
|
+
isSelected ? styles.selectedDayCell : null,
|
|
124
|
+
isToday ? styles.todayDayCell : null,
|
|
125
|
+
], [geometry.cell, isDisabled, isSelected, isToday, palette.underlay, styles]);
|
|
126
|
+
if (isHidden) {
|
|
127
|
+
return (_jsx(View, { accessibilityElementsHidden: true, importantForAccessibility: "no-hide-descendants", style: [CELL, geometry.cell], testID: `calendar-day-${day.key}-spacer` }));
|
|
128
|
+
}
|
|
129
|
+
const dots = chrome.showDots && marker?.dots?.length
|
|
130
|
+
? marker.dots.slice(0, chrome.maxDots)
|
|
131
|
+
: null;
|
|
132
|
+
const badgeText = chrome.showBadges && marker?.badgeText ? marker.badgeText : null;
|
|
133
|
+
return (_jsx(Pressable, { accessibilityLabel: accessibilityLabel, accessibilityRole: "button", accessibilityState: { disabled: isDisabled, selected: isSelected }, "aria-disabled": isDisabled, "aria-selected": isSelected, onLongPress: chrome.onLongPress ? handleLongPress : undefined, onPress: handlePress, style: cellStyle, tabIndex: isTabbable ? 0 : -1, testID: `calendar-day-${day.key}`, children: chrome.renderDay ? (chrome.renderDay({
|
|
134
|
+
day,
|
|
135
|
+
isSelected,
|
|
136
|
+
isToday,
|
|
137
|
+
isDisabled,
|
|
138
|
+
marker,
|
|
139
|
+
palette,
|
|
140
|
+
cellSize: metrics.cellSize,
|
|
141
|
+
})) : (_jsxs(_Fragment, { children: [_jsx(View, { style: [PILL, geometry.pill, pillStyle], children: _jsx(Text, { adjustsFontSizeToFit: Platform.OS === 'ios', maxFontSizeMultiplier: chrome.maxFontSizeMultiplier, minimumFontScale: 0.8, numberOfLines: 1, style: [
|
|
142
|
+
DAY_TEXT,
|
|
143
|
+
geometry.dayText,
|
|
144
|
+
textStyle,
|
|
145
|
+
styles.dayText,
|
|
146
|
+
day.isOutside ? styles.outsideDayText : null,
|
|
147
|
+
isDisabled ? styles.disabledDayText : null,
|
|
148
|
+
isSelected ? styles.selectedDayText : null,
|
|
149
|
+
isToday ? styles.todayDayText : null,
|
|
150
|
+
], children: dayLabel }) }), dots || badgeText ? (_jsxs(View, { importantForAccessibility: "no", pointerEvents: "none", style: DECORATIONS, children: [dots ? (_jsx(View, { style: [DOT_ROW, geometry.dotRow, styles.dotRow], children: dots.map((dot) => (_jsx(View, { style: [
|
|
151
|
+
geometry.dot,
|
|
152
|
+
{ backgroundColor: dot.color ?? palette.dot },
|
|
153
|
+
styles.dot,
|
|
154
|
+
] }, dot.key))) })) : null, badgeText ? (_jsx(Text, { maxFontSizeMultiplier: chrome.maxFontSizeMultiplier, numberOfLines: 1, style: [
|
|
155
|
+
BADGE_TEXT,
|
|
156
|
+
geometry.badgeText,
|
|
157
|
+
{ color: marker?.badgeColor ?? palette.badgeText },
|
|
158
|
+
styles.badgeText,
|
|
159
|
+
], children: badgeText })) : null] })) : null] })) }));
|
|
160
|
+
}
|
|
161
|
+
const areDaysEqual = (a, b) => a.day === b.day &&
|
|
162
|
+
a.dayLabel === b.dayLabel &&
|
|
163
|
+
a.isSelected === b.isSelected &&
|
|
164
|
+
a.isToday === b.isToday &&
|
|
165
|
+
a.isDisabled === b.isDisabled &&
|
|
166
|
+
a.isTabbable === b.isTabbable &&
|
|
167
|
+
a.isHidden === b.isHidden &&
|
|
168
|
+
a.chrome === b.chrome &&
|
|
169
|
+
a.accessibilityLabel === b.accessibilityLabel &&
|
|
170
|
+
isSameMarker(a.marker, b.marker);
|
|
171
|
+
export const CalendarDay = React.memo(CalendarDayContainer, areDaysEqual);
|
|
172
|
+
export default CalendarDay;
|