@attio/react-native-bottom-sheet-toolbox-text-input
Version:
102 lines (88 loc) • 3.42 kB
text/typescript
import * as React from "react"
import {
type NativeSyntheticEvent,
type StyleProp,
StyleSheet,
type TextInputContentSizeChangeEventData,
type TextStyle,
} from "react-native"
import {type SharedValue, useAnimatedReaction, useSharedValue} from "react-native-reanimated"
import {
useBottomSheetToolboxInternalContext,
useKeyboardHeightSharedValue,
} from "@attio/react-native-bottom-sheet-toolbox"
import {getVerticalBorderWidth} from "../../utils/get-vertical-border-width"
import {getVerticalPadding} from "../../utils/get-vertical-padding"
export interface UseMinOverlayHeightSharedValueArgs {
textInputStyle?: StyleProp<TextStyle>
bottomInsetSharedValue: SharedValue<number>
topInsetSharedValue: SharedValue<number | null>
minTextInputHeightSharedValue: SharedValue<number | null>
fallbackLines: number
}
export function useMinOverlayHeightSharedValue({
textInputStyle,
topInsetSharedValue,
bottomInsetSharedValue,
minTextInputHeightSharedValue,
fallbackLines,
}: UseMinOverlayHeightSharedValueArgs) {
const {wrapperHeightSharedValue} = useBottomSheetToolboxInternalContext()
const flatTextInputStyle = React.useMemo(
() => StyleSheet.flatten(textInputStyle),
[textInputStyle]
)
const borderWidth = getVerticalBorderWidth(flatTextInputStyle)
const verticalPadding = getVerticalPadding(flatTextInputStyle)
/**
* There is a bug inside React Native which prevents this from being called
* when it should be: https://github.com/facebook/react-native/issues/47186
*
* Hence we try to calculate some meaningful default.
*/
const lineHeight = Number(flatTextInputStyle.lineHeight ?? 16) * fallbackLines
const initialContentHeight =
typeof flatTextInputStyle.minHeight === "number"
? Math.max(flatTextInputStyle.minHeight, lineHeight)
: lineHeight
const contentHeightSharedValue = useSharedValue(initialContentHeight)
const onContentSizeChange = React.useCallback(
(evt: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) => {
contentHeightSharedValue.set(evt.nativeEvent.contentSize.height)
},
[]
)
const keyboardHeightSharedValue = useKeyboardHeightSharedValue()
useAnimatedReaction(
() => {
const contentHeight = contentHeightSharedValue.get()
const wrapperHeight = wrapperHeightSharedValue.get()
const topInset = topInsetSharedValue.get()
if (contentHeight === 0 || wrapperHeight === null || topInset === null) {
return null
}
const maxOverlayHeight =
wrapperHeight -
keyboardHeightSharedValue.get() -
topInset -
bottomInsetSharedValue.get()
const layoutMargins = verticalPadding + borderWidth
return Math.min(
contentHeight === 0 ? 0 : contentHeight + layoutMargins || 0,
maxOverlayHeight
)
},
(result) => {
minTextInputHeightSharedValue.set(result)
},
[
wrapperHeightSharedValue,
keyboardHeightSharedValue,
topInsetSharedValue,
bottomInsetSharedValue,
verticalPadding,
borderWidth,
]
)
return onContentSizeChange
}