mattermost-mobile/app/components/navigation_header/index.tsx
Rahim Rahman 00e05d581e
fix: MM-58164 Search text is behind the textbox while searching (#8026)
* fix: MM-58164 search text hides behind textbox
* clean up unit test for useCollapsibleHeader
* simplify math
* unlock doesn't need to use useCallback as it doesn't have any deps
* revert changes on unlock
* using lockValue & headerOffset directly into children props
* disable scrolling to prevent scrollValue from being updated
* properly snapping flatlist to the proper spot in a few scenarios

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2024-09-05 11:28:00 -06:00

130 lines
4.2 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {forwardRef} from 'react';
import Animated, {useAnimatedStyle, useDerivedValue} from 'react-native-reanimated';
import {SEARCH_INPUT_HEIGHT, SEARCH_INPUT_MARGIN} from '@constants/view';
import {useTheme} from '@context/theme';
import useHeaderHeight, {MAX_OVERSCROLL} from '@hooks/header';
import {clamp} from '@utils/gallery';
import {makeStyleSheetFromTheme} from '@utils/theme';
import Header, {type HeaderRightButton} from './header';
import NavigationHeaderLargeTitle from './large';
import NavigationSearch from './search';
import type {SearchProps, SearchRef} from '@components/search';
type Props = SearchProps & {
hasSearch?: boolean;
isLargeTitle?: boolean;
leftComponent?: React.ReactElement;
onBackPress?: () => void;
onTitlePress?: () => void;
rightButtons?: HeaderRightButton[];
scrollValue?: Animated.SharedValue<number>;
lockValue?: number;
hideHeader?: () => void;
showBackButton?: boolean;
subtitle?: string;
subtitleCompanion?: React.ReactElement;
title?: string;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
container: {
backgroundColor: theme.sidebarBg,
position: 'absolute',
width: '100%',
zIndex: 10,
},
}));
const NavigationHeader = forwardRef<SearchRef, Props>(({
hasSearch = false,
isLargeTitle = false,
leftComponent,
onBackPress,
onTitlePress,
rightButtons,
scrollValue,
lockValue,
showBackButton,
subtitle,
subtitleCompanion,
title = '',
hideHeader,
...searchProps
}: Props, ref) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const {largeHeight, defaultHeight, headerOffset} = useHeaderHeight();
const minScrollValue = useDerivedValue(() => scrollValue?.value || 0, [scrollValue]);
const containerHeight = useAnimatedStyle(() => {
const calculatedHeight = (isLargeTitle ? largeHeight : defaultHeight) - minScrollValue.value;
const height = lockValue || calculatedHeight;
return {
height: Math.max(height, defaultHeight),
minHeight: defaultHeight,
maxHeight: largeHeight + MAX_OVERSCROLL,
};
}, [defaultHeight, largeHeight, lockValue, isLargeTitle]);
const translateY = useDerivedValue(() => (
lockValue ? -lockValue : Math.min(-minScrollValue.value, headerOffset)
), [lockValue, headerOffset]);
const searchTopStyle = useAnimatedStyle(() => {
const margin = clamp(-minScrollValue.value, -headerOffset, headerOffset);
const marginTop = (lockValue ? -lockValue : margin) - SEARCH_INPUT_HEIGHT - SEARCH_INPUT_MARGIN;
return {marginTop};
}, [lockValue, headerOffset]);
return (
<Animated.View style={[styles.container, containerHeight]}>
<Header
defaultHeight={defaultHeight}
hasSearch={hasSearch}
isLargeTitle={isLargeTitle}
heightOffset={lockValue || headerOffset}
leftComponent={leftComponent}
onBackPress={onBackPress}
onTitlePress={onTitlePress}
rightButtons={rightButtons}
scrollValue={scrollValue}
showBackButton={showBackButton}
subtitle={subtitle}
subtitleCompanion={subtitleCompanion}
theme={theme}
title={title}
/>
{isLargeTitle &&
<NavigationHeaderLargeTitle
heightOffset={lockValue || headerOffset}
hasSearch={hasSearch}
subtitle={subtitle}
theme={theme}
title={title}
translateY={translateY}
/>
}
{hasSearch &&
<NavigationSearch
{...searchProps}
hideHeader={hideHeader}
theme={theme}
topStyle={searchTopStyle}
ref={ref}
/>
}
</Animated.View>
);
});
NavigationHeader.displayName = 'NavHeader';
export default NavigationHeader;