UNPKG

react-native-popover-view

Version:

A <Popover /> component for react-native iOS, Android, and Web

102 lines (91 loc) 3.25 kB
import { RefObject } from 'react'; import { MeasureInWindowOnSuccessCallback, StyleProp, ViewStyle, StyleSheet } from 'react-native'; import { Placement, Point, Rect, Size } from './Types'; import { DEFAULT_ARROW_SIZE, DEFAULT_BORDER_RADIUS } from './Constants'; type RefType = RefObject<{ measureInWindow: (callback: MeasureInWindowOnSuccessCallback) => void }>; export function getRectForRef(ref: RefType): Promise<Rect> { return new Promise((resolve, reject) => { if (ref.current) { ref.current.measureInWindow( (x: number, y: number, width: number, height: number) => resolve(new Rect(x, y, width, height)) ); } else { reject(new Error('getRectForRef - current is not set')); } }); } export async function waitForChange( getFirst: () => Promise<Rect>, getSecond: () => Promise<Rect> ): Promise<void> { // Failsafe so that the interval doesn't run forever let count = 0; let first, second; do { first = await getFirst(); second = await getSecond(); await new Promise(resolve => { setTimeout(resolve, 100); }); count++; if (count++ > 20) { throw new Error('waitForChange - Timed out waiting for change (waited 2 seconds)'); } } while (first.equals(second)); } export async function waitForNewRect(ref: RefType, initialRect: Rect): Promise<Rect> { await waitForChange(() => getRectForRef(ref), () => Promise.resolve(initialRect)); const rect = await getRectForRef(ref); return rect; } export function sizeChanged(a: Size | null, b: Size | null): boolean { if (!a || !b) return false; return Math.round(a.width) !== Math.round(b.width) || Math.round(a.height) !== Math.round(b.height); } export function rectChanged(a: Rect | null, b: Rect | null): boolean { if (!a || !b) return false; return Math.round(a.x) !== Math.round(b.x) || Math.round(a.y) !== Math.round(b.y) || Math.round(a.width) !== Math.round(b.width) || Math.round(a.height) !== Math.round(b.height); } export function pointChanged(a: Point, b: Point): boolean { return (Math.round(a.x) !== Math.round(b.x) || Math.round(a.y) !== Math.round(b.y)); } export function getArrowSize( placement: Placement, arrowStyle: StyleProp<ViewStyle> ): Size { let { width, height } = StyleSheet.flatten(arrowStyle); if (typeof width !== 'number') ({ width } = DEFAULT_ARROW_SIZE); if (typeof height !== 'number') ({ height } = DEFAULT_ARROW_SIZE); switch (placement) { case Placement.LEFT: case Placement.RIGHT: return new Size(height, width); default: return new Size(width, height); } } export function getBorderRadius(popoverStyle: StyleProp<ViewStyle>): number { if (StyleSheet.flatten(popoverStyle).borderRadius === 0) return 0; return StyleSheet.flatten(popoverStyle).borderRadius || DEFAULT_BORDER_RADIUS; } export function getChangedProps( props: Record<string, unknown>, prevProps: Record<string, unknown>, importantProps: string[] ): string[] { return importantProps.filter(key => { const curVal = props[key]; const prevVal = prevProps[key]; if (curVal instanceof Rect && prevVal instanceof Rect) { return !curVal.equals(prevVal); } return curVal !== prevVal; }); }