diff --git a/packages/@react-spectrum/s2/src/Calendar.tsx b/packages/@react-spectrum/s2/src/Calendar.tsx index 8a197556e25..5976cb3507f 100644 --- a/packages/@react-spectrum/s2/src/Calendar.tsx +++ b/packages/@react-spectrum/s2/src/Calendar.tsx @@ -72,19 +72,44 @@ export interface CalendarProps export const CalendarContext = createContext>, HTMLDivElement>>(null); -const calendarStyles = style({ +const calendarStyles = style<{isMultiMonth?: boolean}>({ display: 'flex', + containerType: { + default: 'inline-size', + isMultiMonth: 'unset' + }, flexDirection: 'column', gap: 24, - width: 'fit', - disableTapHighlight: true + disableTapHighlight: true, + '--cell-gap': { + type: 'paddingStart', + value: 4 + }, + '--cell-max-width': { + type: 'width', + value: 32 + }, + '--cell-responsive-size': { + type: 'width', + value: { + default: '[min(var(--cell-max-width), (100cqw - (var(--cell-gap) * 12)) / 7)]', + isMultiMonth: '--cell-max-width' + } + }, + width: { + default: 'calc(7 * var(--cell-max-width) + var(--cell-gap) * 12)', + isMultiMonth: 'fit' + }, + maxWidth: { + default: 'full', + isMultiMonth: 'unset' + } }, getAllowedOverrides()); const headerStyles = style({ display: 'flex', alignItems: 'center', - justifyContent: 'space-between', - width: 'full' + justifyContent: 'space-between' }); const headingStyles = style({ @@ -92,16 +117,14 @@ const headingStyles = style({ alignItems: 'center', justifyContent: 'space-between', margin: 0, - width: 'full' + flexGrow: 1 }); const titleStyles = style({ font: 'title-lg', textAlign: 'center', flexGrow: 1, - flexShrink: 0, - flexBasis: '0%', - minWidth: 0 + flexShrink: 0 }); const headerCellStyles = style({ @@ -121,10 +144,7 @@ const headerCellStyles = style({ const cellStyles = style({ outlineStyle: 'none', - '--cell-gap': { - type: 'paddingStart', - value: 4 - }, + boxSizing: 'content-box', paddingStart: { default: 4, isFirstChild: 0 @@ -142,15 +162,16 @@ const cellStyles = style({ isLastWeek: 0 }, position: 'relative', - width: 32, - height: 32, display: { default: 'flex', isOutsideMonth: 'none' }, alignItems: 'center', justifyContent: 'center', - disableTapHighlight: true + disableTapHighlight: true, + width: '--cell-responsive-size', + aspectRatio: 'square', + height: 'auto' }); const cellInnerStyles = style({ @@ -174,7 +195,7 @@ const cellInnerStyles = style 1; return ( + className={(UNSAFE_className || '') + calendarStyles({isMultiMonth}, styles)}> {({isInvalid, isDisabled}) => { return ( <> @@ -435,11 +457,7 @@ export const Calendar = /*#__PURE__*/ (forwardRef as forwardRefType)(function Ca [HeaderContext, null], [HeadingContext, null] ]}> -
- - - -
+
{Array.from({length: visibleMonths}).map((_, i) => ( - + ))}
{isInvalid && ( @@ -465,6 +483,16 @@ export const Calendar = /*#__PURE__*/ (forwardRef as forwardRefType)(function Ca ); }); +export const CalendarHeader = (): ReactElement => { + return ( +
+ + + +
+ ); +}; + export const CalendarGrid = (props: Omit & PropsWithChildren & {months: number}): ReactElement => { let rangeCalendarProps = useSlottedContext(RangeCalendarContext); let calendarProps = useSlottedContext(AriaCalendarContext); @@ -497,7 +525,7 @@ export const CalendarGrid = (props: Omit & Pr // Ordinarily the heading is a formatted date range, ie January 2025 - February 2025. // However, we want to show each month individually. -export const CalendarHeading = (): ReactElement => { +const CalendarHeading = (): ReactElement => { let calendarStateContext = useContext(CalendarStateContext); let rangeCalendarStateContext = useContext(RangeCalendarStateContext); let {visibleRange, timeZone} = calendarStateContext ?? rangeCalendarStateContext ?? {}; @@ -648,11 +676,8 @@ const CalendarCellInner = (props: Omit & {isRange
extends Omit, 'visibleDuration' | 'style' | 'className' | 'render' | 'children' | 'styles' | keyof GlobalDOMAttributes>, StyleProps { @@ -48,21 +45,40 @@ export interface RangeCalendarProps export const RangeCalendarContext = createContext>, HTMLDivElement>>(null); - -const calendarStyles = style({ +const calendarStyles = style<{isMultiMonth?: boolean}>({ display: 'flex', + containerType: { + default: 'inline-size', + isMultiMonth: 'unset' + }, flexDirection: 'column', gap: 24, - width: 'fit' + disableTapHighlight: true, + '--cell-gap': { + type: 'paddingStart', + value: 4 + }, + '--cell-max-width': { + type: 'width', + value: 32 + }, + '--cell-responsive-size': { + type: 'width', + value: { + default: '[min(var(--cell-max-width), (100cqw - (var(--cell-gap) * 12)) / 7)]', + isMultiMonth: '--cell-max-width' + } + }, + width: { + default: 'calc(7 * var(--cell-max-width) + var(--cell-gap) * 12)', + isMultiMonth: 'fit' + }, + maxWidth: { + default: 'full', + isMultiMonth: 'unset' + } }, getAllowedOverrides()); -const headerStyles = style({ - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - width: 'full' -}); - /** * RangeCalendars display a grid of days in one or more months and allow users to select a contiguous range of dates. */ @@ -78,13 +94,14 @@ export const RangeCalendar = /*#__PURE__*/ (forwardRef as forwardRefType)(functi } = props; let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/s2'); + let isMultiMonth = visibleMonths > 1; return ( + style={{...UNSAFE_style, '--num-calendars': visibleMonths} as CSSProperties} + className={(UNSAFE_className || '') + calendarStyles({isMultiMonth}, styles)}> {({isInvalid, isDisabled}) => { return ( <> @@ -93,22 +110,17 @@ export const RangeCalendar = /*#__PURE__*/ (forwardRef as forwardRefType)(functi [HeaderContext, null], [HeadingContext, null] ]}> -
- - - -
+
{Array.from({length: visibleMonths}).map((_, i) => ( - + ))}
{isInvalid && (