@react-spectrum/calendar 3.0.0-alpha.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.
@@ -0,0 +1,89 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {AriaCalendarCellProps, useCalendarCell} from '@react-aria/calendar';
14
+ import {CalendarDate, getDayOfWeek, isEqualDay, isSameDay, isSameMonth, isToday} from '@internationalized/date';
15
+ import {CalendarState, RangeCalendarState} from '@react-stately/calendar';
16
+ import {classNames} from '@react-spectrum/utils';
17
+ import {mergeProps} from '@react-aria/utils';
18
+ import React, {useMemo, useRef} from 'react';
19
+ import styles from '@adobe/spectrum-css-temp/components/calendar/vars.css';
20
+ import {useDateFormatter, useLocale} from '@react-aria/i18n';
21
+ import {useFocusRing} from '@react-aria/focus';
22
+ import {useHover} from '@react-aria/interactions';
23
+
24
+ interface CalendarCellProps extends AriaCalendarCellProps {
25
+ state: CalendarState | RangeCalendarState,
26
+ currentMonth: CalendarDate
27
+ }
28
+
29
+ export function CalendarCell({state, currentMonth, ...props}: CalendarCellProps) {
30
+ let ref = useRef<HTMLElement>();
31
+ let {cellProps, buttonProps, isPressed} = useCalendarCell({
32
+ ...props,
33
+ isDisabled: !isSameMonth(props.date, currentMonth)
34
+ }, state, ref);
35
+ let {hoverProps, isHovered} = useHover({});
36
+ let dateFormatter = useDateFormatter({
37
+ day: 'numeric',
38
+ timeZone: state.timeZone,
39
+ calendar: currentMonth.calendar.identifier
40
+ });
41
+ let isSelected = state.isSelected(props.date);
42
+ let highlightedRange = 'highlightedRange' in state && state.highlightedRange;
43
+ let isSelectionStart = highlightedRange && isSameDay(props.date, highlightedRange.start);
44
+ let isSelectionEnd = highlightedRange && isSameDay(props.date, highlightedRange.end);
45
+ let {locale} = useLocale();
46
+ let dayOfWeek = getDayOfWeek(props.date, locale);
47
+ let isRangeStart = isSelected && (dayOfWeek === 0 || props.date.day === 1);
48
+ let isRangeEnd = isSelected && (dayOfWeek === 6 || props.date.day === currentMonth.calendar.getDaysInMonth(currentMonth));
49
+ let {focusProps, isFocusVisible} = useFocusRing();
50
+
51
+ // For performance, reuse the same date object as before if the new date prop is the same.
52
+ // This allows subsequent useMemo results to be reused.
53
+ let date = props.date;
54
+ let lastDate = useRef(null);
55
+ if (lastDate.current && isEqualDay(date, lastDate.current)) {
56
+ date = lastDate.current;
57
+ }
58
+
59
+ lastDate.current = date;
60
+
61
+ let nativeDate = useMemo(() => date.toDate(state.timeZone), [date, state.timeZone]);
62
+ let formatted = useMemo(() => dateFormatter.format(nativeDate), [dateFormatter, nativeDate]);
63
+
64
+ return (
65
+ <td
66
+ {...cellProps}
67
+ className={classNames(styles, 'spectrum-Calendar-tableCell')}>
68
+ <span
69
+ {...mergeProps(buttonProps, hoverProps, focusProps)}
70
+ ref={ref}
71
+ className={classNames(styles, 'spectrum-Calendar-date', {
72
+ 'is-today': isToday(props.date, state.timeZone),
73
+ 'is-selected': isSelected,
74
+ 'is-focused': state.isCellFocused(props.date) && isFocusVisible,
75
+ 'is-disabled': state.isCellDisabled(props.date),
76
+ 'is-outsideMonth': !isSameMonth(props.date, currentMonth),
77
+ 'is-range-start': isRangeStart,
78
+ 'is-range-end': isRangeEnd,
79
+ 'is-range-selection': isSelected && 'highlightedRange' in state,
80
+ 'is-selection-start': isSelectionStart,
81
+ 'is-selection-end': isSelectionEnd,
82
+ 'is-hovered': isHovered,
83
+ 'is-pressed': isPressed
84
+ })}>
85
+ <span className={classNames(styles, 'spectrum-Calendar-dateText')}>{formatted}</span>
86
+ </span>
87
+ </td>
88
+ );
89
+ }
@@ -0,0 +1,110 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {CalendarCell} from './CalendarCell';
14
+ import {CalendarDate, endOfMonth, getWeeksInMonth, startOfWeek} from '@internationalized/date';
15
+ import {CalendarPropsBase} from '@react-types/calendar';
16
+ import {CalendarState, RangeCalendarState} from '@react-stately/calendar';
17
+ import {classNames} from '@react-spectrum/utils';
18
+ import {DOMProps, StyleProps} from '@react-types/shared';
19
+ import React, {useEffect, useState} from 'react';
20
+ import styles from '@adobe/spectrum-css-temp/components/calendar/vars.css';
21
+ import {useCalendarGrid} from '@react-aria/calendar';
22
+ import {useDateFormatter, useLocale} from '@react-aria/i18n';
23
+ import {useProviderProps} from '@react-spectrum/provider';
24
+ import {VisuallyHidden} from '@react-aria/visually-hidden';
25
+
26
+ interface CalendarMonthProps extends CalendarPropsBase, DOMProps, StyleProps {
27
+ state: CalendarState | RangeCalendarState,
28
+ startDate: CalendarDate
29
+ }
30
+
31
+ export function CalendarMonth(props: CalendarMonthProps) {
32
+ props = useProviderProps(props);
33
+ let {
34
+ state,
35
+ startDate
36
+ } = props;
37
+ let {
38
+ gridProps
39
+ } = useCalendarGrid({
40
+ ...props,
41
+ endDate: endOfMonth(startDate)
42
+ }, state);
43
+
44
+ let dayFormatter = useDateFormatter({weekday: 'narrow'});
45
+ let dayFormatterLong = useDateFormatter({weekday: 'long'});
46
+
47
+ let {locale} = useLocale();
48
+ let monthStart = startOfWeek(startDate, locale);
49
+ let weeksInMonth = getWeeksInMonth(startDate, locale);
50
+
51
+ let [isRangeSelecting, setRangeSelecting] = useState(false);
52
+ let hasAnchorDate = 'anchorDate' in state && state.anchorDate != null;
53
+
54
+ // Update isRangeSelecting immediately when it becomes true.
55
+ // This feels weird but is actually fine...
56
+ // https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
57
+ if (hasAnchorDate && !isRangeSelecting) {
58
+ setRangeSelecting(true);
59
+ }
60
+
61
+ // Delay removing the is-range-selecting class for a frame after selection ends.
62
+ // This avoids an undesired animation on touch devices.
63
+ useEffect(() => {
64
+ if (!hasAnchorDate && isRangeSelecting) {
65
+ let raf = requestAnimationFrame(() => setRangeSelecting(false));
66
+ return () => cancelAnimationFrame(raf);
67
+ }
68
+ }, [hasAnchorDate, isRangeSelecting]);
69
+
70
+ return (
71
+ <table
72
+ {...gridProps}
73
+ className={classNames(styles, 'spectrum-Calendar-body', 'spectrum-Calendar-table', {'is-range-selecting': isRangeSelecting})}>
74
+ <thead>
75
+ <tr>
76
+ {[...new Array(7).keys()].map((index) => {
77
+ let date = monthStart.add({days: index});
78
+ let dateDay = date.toDate(state.timeZone);
79
+ let day = dayFormatter.format(dateDay);
80
+ let dayLong = dayFormatterLong.format(dateDay);
81
+ return (
82
+ <th
83
+ key={index}
84
+ className={classNames(styles, 'spectrum-Calendar-tableCell')}>
85
+ {/* Make sure screen readers read the full day name, but we show an abbreviation visually. */}
86
+ <VisuallyHidden>{dayLong}</VisuallyHidden>
87
+ <span aria-hidden="true" className={classNames(styles, 'spectrum-Calendar-dayOfWeek')}>
88
+ {day}
89
+ </span>
90
+ </th>
91
+ );
92
+ })}
93
+ </tr>
94
+ </thead>
95
+ <tbody>
96
+ {[...new Array(weeksInMonth).keys()].map(weekIndex => (
97
+ <tr key={weekIndex}>
98
+ {[...new Array(7).keys()].map(dayIndex => (
99
+ <CalendarCell
100
+ key={dayIndex}
101
+ state={state}
102
+ date={monthStart.add({weeks: weekIndex, days: dayIndex})}
103
+ currentMonth={startDate} />
104
+ ))}
105
+ </tr>
106
+ ))}
107
+ </tbody>
108
+ </table>
109
+ );
110
+ }
@@ -0,0 +1,38 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {CalendarBase} from './CalendarBase';
14
+ import {createCalendar} from '@internationalized/date';
15
+ import {DateValue, SpectrumRangeCalendarProps} from '@react-types/calendar';
16
+ import React, {useMemo} from 'react';
17
+ import {useLocale} from '@react-aria/i18n';
18
+ import {useRangeCalendar} from '@react-aria/calendar';
19
+ import {useRangeCalendarState} from '@react-stately/calendar';
20
+
21
+ export function RangeCalendar<T extends DateValue>(props: SpectrumRangeCalendarProps<T>) {
22
+ let {visibleMonths = 1} = props;
23
+ let visibleDuration = useMemo(() => ({months: visibleMonths}), [visibleMonths]);
24
+ let {locale} = useLocale();
25
+ let state = useRangeCalendarState({
26
+ ...props,
27
+ locale,
28
+ visibleDuration,
29
+ createCalendar
30
+ });
31
+
32
+ return (
33
+ <CalendarBase
34
+ {...props}
35
+ state={state}
36
+ useCalendar={useRangeCalendar} />
37
+ );
38
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright 2020 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ /// <reference types="css-module-types" />
14
+
15
+ export * from './Calendar';
16
+ export * from './RangeCalendar';