UNPKG

react-native-paper

Version:
130 lines (109 loc) 3.58 kB
import { Dimensions, LayoutRectangle, ViewStyle } from 'react-native'; type ChildrenMeasurement = { width: number; height: number; pageX: number; pageY: number; }; type TooltipLayout = LayoutRectangle; export type Measurement = { children: ChildrenMeasurement; tooltip: TooltipLayout; measured: boolean; }; /** * Return true when the tooltip center x-coordinate relative to the wrapped element is negative. * The tooltip will be placed at the starting x-coordinate from the wrapped element. */ const overflowLeft = (center: number): boolean => { return center < 0; }; /** * Return true when the tooltip center x-coordinate + tooltip width is greater than the layout width * The tooltip width will grow from right to left relative to the wrapped element. */ const overflowRight = (center: number, tooltipWidth: number): boolean => { const { width: layoutWidth } = Dimensions.get('window'); return center + tooltipWidth > layoutWidth; }; /** * Return true when the children y-coordinate + its height + tooltip height is greater than the layout height. * The tooltip will be placed at the top of the wrapped element. */ const overflowBottom = ( childrenY: number, childrenHeight: number, tooltipHeight: number ): boolean => { const { height: layoutHeight } = Dimensions.get('window'); return childrenY + childrenHeight + tooltipHeight > layoutHeight; }; const getTooltipXPosition = ( { pageX: childrenX, width: childrenWidth }: ChildrenMeasurement, { width: tooltipWidth }: TooltipLayout ): number => { // when the children use position absolute the childrenWidth is measured as 0, // so it's best to anchor the tooltip at the start of the children const center = childrenWidth > 0 ? childrenX + (childrenWidth - tooltipWidth) / 2 : childrenX; if (overflowLeft(center)) return childrenX; if (overflowRight(center, tooltipWidth)) return childrenX + childrenWidth - tooltipWidth; return center; }; const getTooltipYPosition = ( { pageY: childrenY, height: childrenHeight }: ChildrenMeasurement, { height: tooltipHeight }: TooltipLayout ): number => { if (overflowBottom(childrenY, childrenHeight, tooltipHeight)) return childrenY - tooltipHeight; return childrenY + childrenHeight; }; const getChildrenMeasures = ( style: ViewStyle | Array<ViewStyle>, measures: ChildrenMeasurement ): ChildrenMeasurement => { const { position, top, bottom, left, right } = Array.isArray(style) ? style.reduce((acc, current) => ({ ...acc, ...current })) : style; if (position === 'absolute') { let pageX = 0; let pageY = measures.pageY; let height = 0; let width = 0; if (typeof left === 'number') { pageX = left; width = 0; } if (typeof right === 'number') { pageX = measures.width - right; width = 0; } if (typeof top === 'number') { pageY = pageY + top; } if (typeof bottom === 'number') { pageY = pageY - bottom; } return { pageX, pageY, width, height }; } return measures; }; export const getTooltipPosition = ( { children, tooltip, measured }: Measurement, component: React.ReactElement<{ style: ViewStyle | Array<ViewStyle> | undefined | null; }> ): {} | { left: number; top: number } => { if (!measured) return {}; let measures = children; if (component.props.style) { measures = getChildrenMeasures(component.props.style, children); } return { left: getTooltipXPosition(measures, tooltip), top: getTooltipYPosition(measures, tooltip), }; };