Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ExpandableCalendar - Added height calculation on layout instead of hard coded header height #2599

Merged
merged 18 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3d849de
Added height calculation on layout instead of hardcoded header height
nitzanyiz Feb 9, 2025
9904034
Removed horizontal handling
nitzanyiz Feb 9, 2025
5f1bbbf
Fix missing newline at end of file in useHeaderHeight.ts
nitzanyiz Feb 9, 2025
987f7b8
Remove unused lodash import in useHeaderHeight.ts
nitzanyiz Feb 9, 2025
e7b4b46
Refactor useHeaderHeight to use inferred type for headerHeight state
nitzanyiz Feb 9, 2025
35e2bb7
Changed the way the header height is effecting the styles
nitzanyiz Feb 10, 2025
6320d8f
Refactor ExpandableCalendar to manage header height state locally and…
nitzanyiz Feb 12, 2025
6694f80
Merge remote-tracking branch 'origin' into fix/ExpandabelHeaderHeader…
nitzanyiz Feb 16, 2025
e11ebfe
Added onHeaderLayout flag and default header heights
nitzanyiz Feb 17, 2025
f4d34b7
Removed memo from _headerStyles object so its updated onClose
nitzanyiz Feb 17, 2025
b56158d
Revert default header height
nitzanyiz Feb 17, 2025
063c0f8
Removed header height from week container
nitzanyiz Feb 19, 2025
fa41fbb
Remove constant for header height in style file
nitzanyiz Feb 20, 2025
6fb7beb
Pass conditional onHeaderLayout to CalendarHeader and reset shouldMea…
nitzanyiz Feb 20, 2025
02e4d9d
Added open height change on header change
nitzanyiz Feb 20, 2025
3f1481d
Rename closedHeightWithKnob to closeHeight
nitzanyiz Feb 20, 2025
0b741f0
Added comments to explain the ref flag and pan responder dependencies
nitzanyiz Feb 23, 2025
3812986
Fix open height calculation by adjusting knob container height condition
nitzanyiz Feb 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/calendar-list/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,14 @@ const CalendarList = (props: CalendarListProps & ContextProp, ref: any) => {
/** FlatList props */
contentContainerStyle,
onEndReachedThreshold,
onEndReached
onEndReached,
onHeaderLayout
} = props;

const calendarProps = extractCalendarProps(props);
const headerProps = extractHeaderProps(props);
const calendarSize = horizontal ? calendarWidth : calendarHeight;
const shouldUseStaticHeader = staticHeader && horizontal;

const [currentMonth, setCurrentMonth] = useState(parseDate(current));

Expand Down Expand Up @@ -250,12 +252,13 @@ const CalendarList = (props: CalendarListProps & ContextProp, ref: any) => {
calendarHeight={calendarHeight}
scrollToMonth={scrollToMonth}
visible={isDateInRange(item)}
onHeaderLayout={!shouldUseStaticHeader ? onHeaderLayout : undefined}
/>
);
}, [horizontal, calendarStyle, calendarWidth, testID, getMarkedDatesForItem, isDateInRange, calendarProps]);

const renderStaticHeader = () => {
if (staticHeader && horizontal) {
if (shouldUseStaticHeader) {
return (
<CalendarHeader
{...headerProps}
Expand All @@ -265,6 +268,7 @@ const CalendarList = (props: CalendarListProps & ContextProp, ref: any) => {
addMonth={addMonth}
accessibilityElementsHidden={true} // iOS
importantForAccessibility={'no-hide-descendants'} // Android
onHeaderLayout={onHeaderLayout}
/>
);
}
Expand Down
9 changes: 7 additions & 2 deletions src/calendar/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
ViewStyle,
AccessibilityActionEvent,
ColorValue,
Insets
Insets,
ViewProps
} from 'react-native';
import {formatNumbers, weekDayNames} from '../../dateutils';
import styleConstructor from './style';
Expand Down Expand Up @@ -73,6 +74,8 @@ export interface CalendarHeaderProps {
numberOfDays?: number;
/** Left inset for the timeline calendar header. Default = 72 */
timelineLeftInset?: number;
/** Callback for header onLayout */
onHeaderLayout?: ViewProps['onLayout'];
}

const accessibilityActions = [
Expand Down Expand Up @@ -107,7 +110,8 @@ const CalendarHeader = forwardRef((props: CalendarHeaderProps, ref) => {
importantForAccessibility,
numberOfDays,
current = '',
timelineLeftInset
timelineLeftInset,
onHeaderLayout
} = props;

const numberOfDaysCondition = useMemo(() => {
Expand Down Expand Up @@ -289,6 +293,7 @@ const CalendarHeader = forwardRef((props: CalendarHeaderProps, ref) => {
onAccessibilityAction={onAccessibilityAction}
accessibilityElementsHidden={accessibilityElementsHidden} // iOS
importantForAccessibility={importantForAccessibility} // Android
onLayout={onHeaderLayout}
>
<View style={headerStyle}>
{_renderArrow('left')}
Expand Down
6 changes: 4 additions & 2 deletions src/componentUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ export function extractHeaderProps(props: CalendarProps) {
numberOfDays,
current,
timelineLeftInset,
testID
testID,
onHeaderLayout
} = props;

const headerProps = {
Expand All @@ -139,7 +140,8 @@ export function extractHeaderProps(props: CalendarProps) {
numberOfDays,
current,
timelineLeftInset,
testID
testID,
onHeaderLayout
};

return headerProps;
Expand Down
49 changes: 26 additions & 23 deletions src/expandableCalendar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import {page} from '../dateutils';
import {parseDate, toMarkingFormat} from '../interface';
import {DateData, Direction} from '../types';
import styleConstructor, {HEADER_HEIGHT, KNOB_CONTAINER_HEIGHT} from './style';
import styleConstructor, {KNOB_CONTAINER_HEIGHT} from './style';
import WeekDaysNames from '../commons/WeekDaysNames';
import Calendar from '../calendar';
import CalendarList, {CalendarListProps} from '../calendar-list';
Expand All @@ -31,6 +31,7 @@ import WeekCalendar from './WeekCalendar';
import Context from './Context';
import constants from '../commons/constants';
import {UpdateSources} from './commons';
import useHeaderHeight from './useHeaderHeight';

export enum Positions {
CLOSED = 'closed',
Expand Down Expand Up @@ -126,6 +127,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
} = props;

const [screenReaderEnabled, setScreenReaderEnabled] = useState(false);
const {headerHeight, onHeaderLayout} = useHeaderHeight();

/** Date */

Expand Down Expand Up @@ -182,7 +184,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
const startHeight = useMemo(() => isOpen ? openHeight.current : closedHeight, [closedHeight, isOpen]);
const _height = useRef(startHeight);
const deltaY = useMemo(() => new Animated.Value(startHeight), [startHeight]);
const headerDeltaY = useRef(new Animated.Value(isOpen ? -HEADER_HEIGHT : 0));
const headerDeltaY = useRef(new Animated.Value(isOpen ? -headerHeight : 0));

useEffect(() => {
_height.current = startHeight;
Expand All @@ -205,17 +207,17 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {

/** Styles */

const style = useRef(styleConstructor(theme));
const style = useMemo(() => styleConstructor(theme, {headerHeight}), [theme, headerHeight]);
const themeObject = Object.assign(headerStyleOverride, theme);

const _wrapperStyles = useRef({style: {height: startHeight}});
const _headerStyles = {style: {top: isOpen ? -HEADER_HEIGHT : 0}};
const _wrapperStyles = useMemo(() => ({style: {height: startHeight}}), [headerHeight]);
const _headerStyles = {style: {top: isOpen ? -headerHeight : 0}};
const _weekCalendarStyles = {style: {opacity: isOpen ? 0 : 1}};

const shouldHideArrows = !horizontal ? true : hideArrows || false;

const updateNativeStyles = () => {
wrapper?.current?.setNativeProps(_wrapperStyles.current);
wrapper?.current?.setNativeProps(_wrapperStyles);

if (!horizontal) {
header?.current?.setNativeProps(_headerStyles);
Expand All @@ -229,25 +231,25 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
const rightPaddings = calendarStyle?.paddingRight;

return [
style.current.weekDayNames,
style.weekDayNames,
{
paddingLeft: isNumber(leftPaddings) ? leftPaddings + 6 : DAY_NAMES_PADDING,
paddingRight: isNumber(rightPaddings) ? rightPaddings + 6 : DAY_NAMES_PADDING
}
];
}, [calendarStyle]);
}, [calendarStyle, style]);

const animatedHeaderStyle = useMemo(() => {
return [style.current.header, {height: HEADER_HEIGHT + 10, top: headerDeltaY.current}];
}, [headerDeltaY.current]);
return [style.header, {height: headerHeight + 10, top: headerDeltaY.current}];
}, [headerDeltaY.current, style]);

const weekCalendarStyle = useMemo(() => {
return [style.current.weekContainer, isOpen ? style.current.hidden : style.current.visible];
}, [isOpen]);
return [style.weekContainer, isOpen ? style.hidden : style.visible];
}, [isOpen, style]);

const containerStyle = useMemo(() => {
return [allowShadow && style.current.containerShadow, propsStyle];
}, [allowShadow, propsStyle]);
return [allowShadow && style.containerShadow, propsStyle, headerHeight === 0 && {opacity: 0}];
}, [allowShadow, propsStyle, style]);

const wrapperStyle = useMemo(() => {
return {height: deltaY};
Expand Down Expand Up @@ -339,11 +341,11 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {

const handlePanResponderMove = (_: GestureResponderEvent, gestureState: PanResponderGestureState) => {
// limit min height to closed height and max to open height
_wrapperStyles.current.style.height = Math.min(Math.max(closedHeight, _height.current + gestureState.dy), openHeight.current);
_wrapperStyles.style.height = Math.min(Math.max(closedHeight, _height.current + gestureState.dy), openHeight.current);

if (!horizontal) {
// vertical CalenderList header
_headerStyles.style.top = Math.min(Math.max(-gestureState.dy, -HEADER_HEIGHT), 0);
_headerStyles.style.top = Math.min(Math.max(-gestureState.dy, -headerHeight), 0);
} else {
// horizontal Week view
if (!isOpen) {
Expand All @@ -357,7 +359,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
};

const handlePanResponderEnd = () => {
_height.current = Number(_wrapperStyles.current.style.height);
_height.current = Number(_wrapperStyles.style.height);
bounceToPosition();
};

Expand Down Expand Up @@ -497,7 +499,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
return (
<Image
source={direction === 'right' ? rightArrowImageSource : leftArrowImageSource}
style={style.current.arrowImage}
style={style.arrowImage}
testID={`${testID}.${direction}Arrow`}
/>
);
Expand All @@ -508,7 +510,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
<View style={weekDaysStyle}>
<WeekDaysNames
firstDay={firstDay}
style={style.current.dayHeader}
style={style.dayHeader}
/>
</View>
);
Expand All @@ -523,7 +525,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
style={animatedHeaderStyle}
pointerEvents={'none'}
>
<Text allowFontScaling={false} style={style.current.headerTitle}>
<Text allowFontScaling={false} style={style.headerTitle}>
{monthYear}
</Text>
{renderWeekDaysNames()}
Expand All @@ -533,8 +535,8 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {

const renderKnob = () => {
return (
<View style={style.current.knobContainer} pointerEvents={'box-none'}>
<TouchableOpacity style={style.current.knob} testID={`${testID}.knob`} onPress={toggleCalendarPosition} hitSlop={knobHitSlop} /* activeOpacity={isOpen ? undefined : 1} *//>
<View style={style.knobContainer} pointerEvents={'box-none'}>
<TouchableOpacity style={style.knob} testID={`${testID}.knob`} onPress={toggleCalendarPosition} hitSlop={knobHitSlop} /* activeOpacity={isOpen ? undefined : 1} *//>
</View>
);
};
Expand Down Expand Up @@ -572,6 +574,7 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
horizontal={horizontal}
firstDay={firstDay}
calendarStyle={calendarStyle}
onHeaderLayout={onHeaderLayout}
{...others}
current={date}
theme={themeObject}
Expand Down Expand Up @@ -608,9 +611,9 @@ const ExpandableCalendar = (props: ExpandableCalendarProps) => {
) : (
<Animated.View testID={`${testID}.expandableContainer`} ref={wrapper} style={wrapperStyle} {...panResponder.panHandlers}>
{renderCalendarList()}
{renderWeekCalendar()}
{!hideKnob && renderKnob()}
{!horizontal && renderAnimatedHeader()}
{renderWeekCalendar()}
</Animated.View>
)}
</View>
Expand Down
8 changes: 5 additions & 3 deletions src/expandableCalendar/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import constants from '../commons/constants';
export const HEADER_HEIGHT = 68;
export const KNOB_CONTAINER_HEIGHT = 24;

type StyleConstructorOptions = Partial<{
headerHeight: number
}>;

export default function styleConstructor(theme: Theme = {}) {
export default function styleConstructor(theme: Theme = {}, {headerHeight = HEADER_HEIGHT}: StyleConstructorOptions = {}) {
const appStyle = {...defaultStyle, ...theme};

return StyleSheet.create({
containerShadow: {
backgroundColor: appStyle.calendarBackground,
Expand Down Expand Up @@ -96,7 +98,7 @@ export default function styleConstructor(theme: Theme = {}) {
position: 'absolute',
left: 0,
right: 0,
top: HEADER_HEIGHT + (constants.isAndroid ? 8 : 9), // align row on top of calendar's first row
top: headerHeight
},
hidden: {
opacity: 0
Expand Down
16 changes: 16 additions & 0 deletions src/expandableCalendar/useHeaderHeight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {useCallback, useState} from 'react';
import type {LayoutChangeEvent} from 'react-native';

const useHeaderHeight = () => {
const [headerHeight, setHeaderHeight] = useState(0);
const onHeaderLayout = useCallback(({nativeEvent: {layout: {height}}}: LayoutChangeEvent) => {
setHeaderHeight(height);
}, []);

return {
headerHeight,
onHeaderLayout,
};
};

export default useHeaderHeight;