@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.
- package/LICENSE +201 -0
- package/README.md +3 -0
- package/dist/main.css +2 -0
- package/dist/main.css.map +1 -0
- package/dist/main.js +365 -0
- package/dist/main.js.map +1 -0
- package/dist/module.js +300 -0
- package/dist/module.js.map +1 -0
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +63 -0
- package/src/Calendar.tsx +38 -0
- package/src/CalendarBase.tsx +106 -0
- package/src/CalendarCell.tsx +89 -0
- package/src/CalendarMonth.tsx +110 -0
- package/src/RangeCalendar.tsx +38 -0
- package/src/index.ts +16 -0
|
@@ -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';
|