Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-native-popover-view

Package Overview
Dependencies
Maintainers
1
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-native-popover-view - npm Package Compare versions

Comparing version 4.1.0 to 5.0.0

dist/AdaptivePopover.d.ts

2

.eslintrc.json

@@ -162,3 +162,3 @@ {

"no-unsafe-optional-chaining": "error",
"no-unused-expressions": "error",
"no-unused-expressions": "warn",
"no-use-before-define": "off",

@@ -165,0 +165,0 @@ "no-useless-backreference": "error",

@@ -0,7 +1,34 @@

import { Size } from './Types';
export declare const MULTIPLE_POPOVER_WARNING = "Popover Warning - Can't Show - Attempted to show a Popover while another one was already showing. You can only show one Popover at a time, and must wait for one to close completely before showing a different one. You can use the onCloseComplete prop to detect when a Popover has finished closing. To show multiple Popovers simultaneously, all but one should have mode={Popover.MODE.JS_MODAL}. Once you change the mode, you can show as many Popovers as you want, but you are responsible for keeping them above other views.";
export declare const DEFAULT_ARROW_SIZE: {
width: number;
height: number;
};
export declare const DEFAULT_ARROW_SIZE: Size;
export declare const DEFAULT_BORDER_RADIUS = 3;
export declare const POPOVER_MARGIN = 10;
export declare const DEBUG = false;
export declare const isIOS: boolean;
export declare const isWeb: boolean;
export declare const FIX_SHIFT: number;
export declare const styles: {
container: {
top: number;
bottom: number;
left: number;
right: number;
position: "absolute";
backgroundColor: string;
};
background: {
top: number;
bottom: number;
left: number;
right: number;
position: "absolute";
backgroundColor: string;
};
popoverContent: {
overflow: "hidden";
position: "absolute";
backgroundColor: string;
borderBottomColor: string;
borderRadius: number;
};
};

@@ -0,5 +1,44 @@

import { Dimensions, Platform, StyleSheet } from 'react-native';
import { Size } from './Types';
export var MULTIPLE_POPOVER_WARNING = "Popover Warning - Can't Show - Attempted to show a Popover while another one was already showing. You can only show one Popover at a time, and must wait for one to close completely before showing a different one. You can use the onCloseComplete prop to detect when a Popover has finished closing. To show multiple Popovers simultaneously, all but one should have mode={Popover.MODE.JS_MODAL}. Once you change the mode, you can show as many Popovers as you want, but you are responsible for keeping them above other views.";
export var DEFAULT_ARROW_SIZE = { width: 16, height: 8 };
export var DEFAULT_ARROW_SIZE = new Size(16, 8);
export var DEFAULT_BORDER_RADIUS = 3;
export var POPOVER_MARGIN = 10;
export var DEBUG = false;
export var isIOS = Platform.OS === 'ios';
export var isWeb = Platform.OS === 'web';
/*
* FIX_SHIFT resolves an issue with useNativeDriver, where it would flash the
* popover on and off really quickly, and then animate in normally. Now, because
* of the shift, the flash happens off screen, and then it is shifted on screen
* just before beginning the actual animation.
*/
export var FIX_SHIFT = isWeb
? 0
: Dimensions.get('window').height * 2;
export var styles = StyleSheet.create({
container: {
top: 0,
bottom: 0,
left: 0,
right: 0,
position: 'absolute',
backgroundColor: 'transparent'
},
background: {
top: 0,
bottom: FIX_SHIFT,
left: 0,
right: 0,
position: 'absolute',
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
popoverContent: {
overflow: 'hidden',
position: 'absolute',
backgroundColor: 'white',
borderBottomColor: '#333438',
borderRadius: DEFAULT_BORDER_RADIUS
}
});
//# sourceMappingURL=Constants.js.map
import { StyleProp, ViewStyle } from 'react-native';
import { Placement } from './Types';
import { Rect, Size, Point } from './Utility';
import { Rect, Size, Point, Placement, PopoverProps } from './Types';
declare type ComputeGeometryBaseProps = {

@@ -8,2 +7,3 @@ requestedContentSize: Size;

debug: (line: string, obj?: unknown) => void;
offset?: number;
};

@@ -14,5 +14,6 @@ declare type ComputeGeometryProps = ComputeGeometryBaseProps & {

fromRect: Rect | null;
arrowStyle: StyleProp<ViewStyle>;
arrowSize: Size;
popoverStyle: StyleProp<ViewStyle>;
arrowShift?: number;
popoverShift?: PopoverProps['popoverShift'];
};

@@ -23,3 +24,3 @@ export declare class Geometry {

placement: Placement;
forcedContentSize: Size | null;
forcedContentSize: Size;
viewLargerThanDisplayArea: {

@@ -33,3 +34,3 @@ width: boolean;

placement: Placement;
forcedContentSize: Size | null;
forcedContentSize: Size;
viewLargerThanDisplayArea: {

@@ -36,0 +37,0 @@ width: boolean;

@@ -12,4 +12,4 @@ var __assign = (this && this.__assign) || function () {

};
import { Placement } from './Types';
import { Rect, Point, getArrowSize, getBorderRadius } from './Utility';
import { Rect, Size, Point, Placement } from './Types';
import { getBorderRadius } from './Utility';
import { POPOVER_MARGIN } from './Constants';

@@ -26,10 +26,9 @@ var Geometry = /** @class */ (function () {

Geometry.equals = function (a, b) {
var _a, _b, _c, _d, _e, _f, _g, _h;
return Point.equals(a.popoverOrigin, b.popoverOrigin) &&
Point.equals(a.anchorPoint, b.anchorPoint) &&
var _a, _b, _c, _d;
return a.popoverOrigin.equals(b.popoverOrigin) &&
a.anchorPoint.equals(b.anchorPoint) &&
a.placement === b.placement &&
((_a = a.forcedContentSize) === null || _a === void 0 ? void 0 : _a.width) === ((_b = b.forcedContentSize) === null || _b === void 0 ? void 0 : _b.width) &&
((_c = a.forcedContentSize) === null || _c === void 0 ? void 0 : _c.height) === ((_d = b.forcedContentSize) === null || _d === void 0 ? void 0 : _d.height) &&
((_e = a.viewLargerThanDisplayArea) === null || _e === void 0 ? void 0 : _e.width) === ((_f = b.viewLargerThanDisplayArea) === null || _f === void 0 ? void 0 : _f.width) &&
((_g = a.viewLargerThanDisplayArea) === null || _g === void 0 ? void 0 : _g.height) === ((_h = b.viewLargerThanDisplayArea) === null || _h === void 0 ? void 0 : _h.height);
a.forcedContentSize.equals(b.forcedContentSize) &&
((_a = a.viewLargerThanDisplayArea) === null || _a === void 0 ? void 0 : _a.width) === ((_b = b.viewLargerThanDisplayArea) === null || _b === void 0 ? void 0 : _b.width) &&
((_c = a.viewLargerThanDisplayArea) === null || _c === void 0 ? void 0 : _c.height) === ((_d = b.viewLargerThanDisplayArea) === null || _d === void 0 ? void 0 : _d.height);
};

@@ -40,3 +39,3 @@ return Geometry;

export function computeGeometry(options) {
var requestedContentSize = options.requestedContentSize, placement = options.placement, displayArea = options.displayArea, debug = options.debug, popoverStyle = options.popoverStyle, arrowShift = options.arrowShift;
var requestedContentSize = options.requestedContentSize, placement = options.placement, displayArea = options.displayArea, debug = options.debug, popoverStyle = options.popoverStyle, arrowShift = options.arrowShift, popoverShift = options.popoverShift;
var newGeom = null;

@@ -48,11 +47,2 @@ // Make copy so doesn't modify original

if (fromRect && options.fromRect instanceof Rect) {
// Check to see if fromRect is outside of displayArea, and adjust if it is
if (fromRect.x > displayArea.x + displayArea.width)
fromRect.x = displayArea.x + displayArea.width;
if (fromRect.y > displayArea.y + displayArea.height)
fromRect.y = displayArea.y + displayArea.height;
if (fromRect.x + fromRect.width < 0)
fromRect.x = -1 * fromRect.width;
if (fromRect.y + fromRect.height < 0)
fromRect.y = -1 * fromRect.height;
var borderRadius = getBorderRadius(popoverStyle);

@@ -72,3 +62,3 @@ switch (placement) {

break;
case Placement.CENTER:
case Placement.FLOATING:
newGeom = null;

@@ -81,3 +71,3 @@ break;

/*
* If the popover will be restricted and the view that the popover isshowing
* If the popover will be restricted and the view that the popover is showing
* from is sufficiently large, try to show the popover inside the view

@@ -100,6 +90,3 @@ */

constrainedY = displayArea.y + displayArea.height - requestedContentSize.height;
var forcedContentSize = {
width: Math.min(fromRect.width - 20, displayArea.width),
height: Math.min(fromRect.height - 20, displayArea.height)
};
var forcedContentSize = new Size(Math.min(fromRect.width - 20, displayArea.width), Math.min(fromRect.height - 20, displayArea.height));
debug('computeGeometry - showing inside anchor');

@@ -109,3 +96,3 @@ newGeom = new Geometry({

anchorPoint: new Point(fromRect.x + (fromRect.width / 2), fromRect.y + (fromRect.height / 2)),
placement: Placement.CENTER,
placement: Placement.FLOATING,
forcedContentSize: forcedContentSize,

@@ -120,3 +107,3 @@ viewLargerThanDisplayArea: {

/*
* If we can't fit inside or outside the fromRect, show the popover centered on the screen,
* If we can't fit inside or outside the fromRect, show the popover floating on the screen,
* but only do this if they haven't asked for a specifc placement type

@@ -139,11 +126,8 @@ * and if it will actually help show more content

var preferedX = ((displayArea.width - requestedContentSize.width) / 2) + displayArea.x;
debug('computeGeometry - showing centered on screen');
debug('computeGeometry - showing floating');
newGeom = new Geometry({
popoverOrigin: new Point(Math.max(minX, preferedX), Math.max(minY, preferedY)),
anchorPoint: new Point((displayArea.width / 2) + displayArea.x, (displayArea.height / 2) + displayArea.y),
placement: Placement.CENTER,
forcedContentSize: {
width: displayArea.width,
height: displayArea.height
},
placement: Placement.FLOATING,
forcedContentSize: new Size(displayArea.width, displayArea.height),
viewLargerThanDisplayArea: {

@@ -154,2 +138,15 @@ width: preferedX < minX - 1,

});
// Apply popover shift
if (!newGeom.viewLargerThanDisplayArea.width && (popoverShift === null || popoverShift === void 0 ? void 0 : popoverShift.x)) {
debug('computeGeometry - applying popoverShift.x', popoverShift.x);
var horizontalMargin = (displayArea.width - requestedContentSize.width) / 2;
newGeom.popoverOrigin.x += popoverShift.x * horizontalMargin;
newGeom.anchorPoint.x = newGeom.popoverOrigin.x + (requestedContentSize.width / 2);
}
if (!newGeom.viewLargerThanDisplayArea.height && (popoverShift === null || popoverShift === void 0 ? void 0 : popoverShift.y)) {
debug('computeGeometry - applying popoverShift.y', popoverShift.y);
var verticalMargin = (displayArea.height - requestedContentSize.height) / 2;
newGeom.popoverOrigin.y += popoverShift.y * verticalMargin;
newGeom.anchorPoint.y = newGeom.popoverOrigin.y + (requestedContentSize.height / 2);
}
}

@@ -166,12 +163,11 @@ if (arrowShift && fromRect) {

function computeTopGeometry(_a) {
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, arrowStyle = _a.arrowStyle, borderRadius = _a.borderRadius;
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, arrowSize = _a.arrowSize, borderRadius = _a.borderRadius, offset = _a.offset;
// Apply a margin on non-arrow sides
displayArea = new Rect(displayArea.x + POPOVER_MARGIN, displayArea.y + POPOVER_MARGIN, displayArea.width - (POPOVER_MARGIN * 2), displayArea.height);
var arrowSize = getArrowSize(Placement.TOP, arrowStyle);
if (offset)
fromRect.y -= offset;
var minY = displayArea.y;
var maxY = displayArea.y + displayArea.height;
var preferredY = fromRect.y - requestedContentSize.height - arrowSize.height;
var forcedContentSize = {
height: (fromRect.y - arrowSize.height - displayArea.y),
width: displayArea.width
};
var forcedContentSize = new Size(displayArea.width, (fromRect.y - arrowSize.height - displayArea.y));
var viewLargerThanDisplayArea = {

@@ -187,6 +183,6 @@ height: preferredY <= minY - 1,

var preferredX = fromRect.x + ((fromRect.width - viewWidth) / 2);
var popoverOrigin = new Point(Math.min(maxX, Math.max(minX, preferredX)), Math.max(minY, preferredY));
var popoverOrigin = new Point(Math.min(maxX, Math.max(minX, preferredX)), Math.min(maxY, Math.max(minY, preferredY)));
var anchorPoint = new Point(fromRect.x + (fromRect.width / 2), fromRect.y);
// Make sure the arrow isn't cut off
anchorPoint.x = Math.max(anchorPoint.x, (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.max(anchorPoint.x, popoverOrigin.x + (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.min(anchorPoint.x, displayArea.x + displayArea.width - (arrowSize.width / 2) - borderRadius);

@@ -202,11 +198,11 @@ return new Geometry({

function computeBottomGeometry(_a) {
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, arrowStyle = _a.arrowStyle, borderRadius = _a.borderRadius;
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, arrowSize = _a.arrowSize, borderRadius = _a.borderRadius, offset = _a.offset;
// Apply a margin on non-arrow sides
displayArea = new Rect(displayArea.x + POPOVER_MARGIN, displayArea.y, displayArea.width - (POPOVER_MARGIN * 2), displayArea.height - POPOVER_MARGIN);
var arrowSize = getArrowSize(Placement.BOTTOM, arrowStyle);
var preferedY = fromRect.y + fromRect.height + arrowSize.height;
var forcedContentSize = {
height: displayArea.y + displayArea.height - preferedY,
width: displayArea.width
};
if (offset)
fromRect.y += offset;
var minY = displayArea.y;
var maxY = displayArea.y + displayArea.height;
var preferedY = fromRect.y + fromRect.height;
var forcedContentSize = new Size(displayArea.width, displayArea.y + displayArea.height - preferedY);
var viewLargerThanDisplayArea = {

@@ -222,6 +218,6 @@ height: preferedY + requestedContentSize.height >= displayArea.y + displayArea.height + 1,

var preferedX = fromRect.x + ((fromRect.width - viewWidth) / 2);
var popoverOrigin = new Point(Math.min(maxX, Math.max(minX, preferedX)), preferedY);
var popoverOrigin = new Point(Math.min(maxX, Math.max(minX, preferedX)), Math.min(maxY, Math.max(minY, preferedY)));
var anchorPoint = new Point(fromRect.x + (fromRect.width / 2), fromRect.y + fromRect.height);
// Make sure the arrow isn't cut off
anchorPoint.x = Math.max(anchorPoint.x, (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.max(anchorPoint.x, popoverOrigin.x + (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.min(anchorPoint.x, displayArea.x + displayArea.width - (arrowSize.width / 2) - borderRadius);

@@ -237,10 +233,8 @@ return new Geometry({

function computeLeftGeometry(_a) {
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, borderRadius = _a.borderRadius, arrowStyle = _a.arrowStyle;
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, borderRadius = _a.borderRadius, arrowSize = _a.arrowSize, offset = _a.offset;
// Apply a margin on non-arrow sides
displayArea = new Rect(displayArea.x + POPOVER_MARGIN, displayArea.y + POPOVER_MARGIN, displayArea.width, displayArea.height - (POPOVER_MARGIN * 2));
var arrowSize = getArrowSize(Placement.LEFT, arrowStyle);
var forcedContentSize = {
height: displayArea.height,
width: fromRect.x - displayArea.x - arrowSize.width
};
if (offset)
fromRect.x -= offset;
var forcedContentSize = new Size(fromRect.x - displayArea.x - arrowSize.width, displayArea.height);
var viewLargerThanDisplayArea = {

@@ -256,10 +250,12 @@ height: requestedContentSize.height >= displayArea.height + 1,

: requestedContentSize.height;
var preferedX = fromRect.x - viewWidth - arrowSize.width;
var preferedX = fromRect.x - viewWidth - arrowSize.height;
var minX = displayArea.x;
var maxX = displayArea.x + displayArea.width;
var preferedY = fromRect.y + ((fromRect.height - viewHeight) / 2);
var minY = displayArea.y;
var maxY = (displayArea.height - viewHeight) + displayArea.y;
var popoverOrigin = new Point(preferedX, Math.min(Math.max(minY, preferedY), maxY));
var popoverOrigin = new Point(Math.min(Math.max(minX, preferedX), maxX), Math.min(Math.max(minY, preferedY), maxY));
var anchorPoint = new Point(fromRect.x, fromRect.y + (fromRect.height / 2));
// Make sure the arrow isn't cut off
anchorPoint.y = Math.max(anchorPoint.y, (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.max(anchorPoint.y, popoverOrigin.y + (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.min(anchorPoint.y, displayArea.y + displayArea.height - (arrowSize.height / 2) - borderRadius);

@@ -275,11 +271,9 @@ return new Geometry({

function computeRightGeometry(_a) {
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, arrowStyle = _a.arrowStyle, borderRadius = _a.borderRadius;
var displayArea = _a.displayArea, fromRect = _a.fromRect, requestedContentSize = _a.requestedContentSize, arrowSize = _a.arrowSize, borderRadius = _a.borderRadius, offset = _a.offset;
// Apply a margin on non-arrow sides
displayArea = new Rect(displayArea.x, displayArea.y + POPOVER_MARGIN, displayArea.width - POPOVER_MARGIN, displayArea.height - (POPOVER_MARGIN * 2));
var arrowSize = getArrowSize(Placement.RIGHT, arrowStyle);
if (offset)
fromRect.x += offset;
var horizontalSpace = displayArea.x + displayArea.width - (fromRect.x + fromRect.width) - arrowSize.width;
var forcedContentSize = {
height: displayArea.height,
width: horizontalSpace
};
var forcedContentSize = new Size(horizontalSpace, displayArea.height);
var viewLargerThanDisplayArea = {

@@ -292,10 +286,12 @@ height: requestedContentSize.height >= displayArea.height + 1,

: requestedContentSize.height;
var preferedX = fromRect.x + fromRect.width + arrowSize.width;
var preferedX = fromRect.x + fromRect.width;
var minX = displayArea.x;
var maxX = displayArea.x + displayArea.width;
var preferedY = fromRect.y + ((fromRect.height - viewHeight) / 2);
var minY = displayArea.y + POPOVER_MARGIN;
var maxY = (displayArea.height - viewHeight) + displayArea.y - POPOVER_MARGIN;
var popoverOrigin = new Point(preferedX, Math.min(Math.max(minY, preferedY), maxY));
var minY = displayArea.y;
var maxY = (displayArea.height - viewHeight) + displayArea.y;
var popoverOrigin = new Point(Math.min(Math.max(minX, preferedX), maxX), Math.min(Math.max(minY, preferedY), maxY));
var anchorPoint = new Point(fromRect.x + fromRect.width, fromRect.y + (fromRect.height / 2.0));
// Make sure the arrow isn't cut off
anchorPoint.y = Math.max(anchorPoint.y, (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.max(anchorPoint.y, popoverOrigin.y + (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.min(anchorPoint.y, displayArea.y + displayArea.height - (arrowSize.height / 2) - borderRadius);

@@ -312,3 +308,3 @@ return new Geometry({

var _a;
var displayArea = options.displayArea, requestedContentSize = options.requestedContentSize, fromRect = options.fromRect, previousPlacement = options.previousPlacement, debug = options.debug, arrowStyle = options.arrowStyle;
var displayArea = options.displayArea, requestedContentSize = options.requestedContentSize, fromRect = options.fromRect, previousPlacement = options.previousPlacement, debug = options.debug, arrowSize = options.arrowSize;
// Keep same placement if possible (left/right)

@@ -336,3 +332,2 @@ if (previousPlacement === Placement.LEFT || previousPlacement === Placement.RIGHT) {

*/
var arrowSize = getArrowSize(Placement.LEFT, arrowStyle);
// generating list of all possible sides with validity

@@ -339,0 +334,0 @@ debug('computeAutoGeometry - displayArea', displayArea);

export { default } from './Popover';
export { Placement as PopoverPlacement, Mode as PopoverMode } from './Types';
export { Rect, Size } from './Utility';
export { Rect, Size } from './Types';
export { default } from './Popover';
export { Placement as PopoverPlacement, Mode as PopoverMode } from './Types';
export { Rect, Size } from './Utility';
export { Rect, Size } from './Types';
//# sourceMappingURL=index.js.map

@@ -1,26 +0,8 @@

import { Component, RefObject, ReactNode, ReactElement } from 'react';
import { Component, RefObject, ReactNode } from 'react';
import PropTypes from 'prop-types';
import { Animated, View, StyleProp, ViewStyle } from 'react-native';
import { Rect } from './Utility';
import { Insets, Placement, Mode } from './Types';
interface PopoverProps {
isVisible?: boolean;
placement?: Placement;
animationConfig?: Partial<Animated.TimingAnimationConfig>;
verticalOffset?: number;
displayArea?: Rect;
displayAreaInsets?: Insets;
popoverStyle?: StyleProp<ViewStyle>;
arrowStyle?: StyleProp<ViewStyle>;
backgroundStyle?: StyleProp<ViewStyle>;
arrowShift?: number;
onOpenStart?: () => void;
onOpenComplete?: () => void;
onRequestClose?: () => void;
onCloseStart?: () => void;
onCloseComplete?: () => void;
onPositionChange?: () => void;
debug?: boolean;
}
interface PublicPopoverProps extends PopoverProps {
import { View } from 'react-native';
import { Rect, PopoverProps, Placement, Mode, Size } from './Types';
interface PublicPopoverProps extends Omit<PopoverProps, 'displayArea' | 'arrowSize'> {
displayArea?: Pick<Rect, 'x' | 'y' | 'width' | 'height'>;
arrowSize?: Pick<Size, 'width' | 'height'>;
mode?: Mode;

@@ -52,4 +34,11 @@ from?: Rect | RefObject<View> | ((sourceRef: RefObject<View>, openPopover: () => void) => ReactNode) | ReactNode;

popoverStyle: any;
arrowStyle: any;
popoverShift: PropTypes.Requireable<PropTypes.InferProps<{
x: PropTypes.Requireable<number>;
y: PropTypes.Requireable<number>;
}>>;
backgroundStyle: any;
arrowSize: PropTypes.Requireable<PropTypes.InferProps<{
width: PropTypes.Requireable<number>;
height: PropTypes.Requireable<number>;
}>>;
arrowShift: PropTypes.Requireable<number>;

@@ -69,4 +58,4 @@ onOpenStart: PropTypes.Requireable<(...args: any[]) => any>;

private sourceRef;
render(): ReactElement;
render(): ReactNode;
}
export {};

@@ -27,38 +27,2 @@ var __extends = (this && this.__extends) || (function () {

};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __rest = (this && this.__rest) || function (s, e) {

@@ -77,19 +41,6 @@ var t = {};

import PropTypes from 'prop-types';
import { Platform, Dimensions, Animated, TouchableWithoutFeedback, View, Modal, Keyboard, Easing, StyleSheet, I18nManager } from 'react-native';
import { Rect, Point, getRectForRef, getArrowSize, getBorderRadius } from './Utility';
import { computeGeometry, Geometry } from './Geometry';
import { Placement, Mode } from './Types';
import { MULTIPLE_POPOVER_WARNING, DEFAULT_BORDER_RADIUS } from './Constants';
var isIOS = Platform.OS === 'ios';
var isWeb = Platform.OS === 'web';
var DEBUG = false;
/*
* FIX_SHIFT resolves an issue with useNativeDriver, where it would flash the
* popover on and off really quickly, and then animate in normally. Now, because
* of the shift, the flash happens off screen, and then it is shifted on screen
* just before beginning the actual animation.
*/
var FIX_SHIFT = isWeb
? 0
: Dimensions.get('window').height * 2;
import { Rect, Placement, Mode, Size } from './Types';
import { DEFAULT_ARROW_SIZE, isWeb } from './Constants';
import JSModalPopover from './JSModalPopover';
import RNModalPopover from './RNModalPopover';
// React Native Web does not export ViewPropTypes, so this is a workaround

@@ -112,3 +63,3 @@ var stylePropType = isWeb

var _this = this;
var _a = this.props, mode = _a.mode, from = _a.from, isVisible = _a.isVisible, onRequestClose = _a.onRequestClose, otherProps = __rest(_a, ["mode", "from", "isVisible", "onRequestClose"]);
var _a = this.props, mode = _a.mode, from = _a.from, isVisible = _a.isVisible, onRequestClose = _a.onRequestClose, placement = _a.placement, arrowSize = _a.arrowSize, displayArea = _a.displayArea, otherProps = __rest(_a, ["mode", "from", "isVisible", "onRequestClose", "placement", "arrowSize", "displayArea"]);
var actualIsVisible = isVisible === undefined

@@ -121,4 +72,10 @@ ? this.state.isVisible

if (from) {
if (from instanceof Rect) {
fromRect = from;
if (typeof from === 'object' && from.x && from.y) {
if (from.width && from.height) {
var fromAsRect = from;
fromRect = new Rect(fromAsRect.x, fromAsRect.y, fromAsRect.width, fromAsRect.height);
}
else {
fromRect = new Rect(from.x, from.y, 0, 0);
}
}

@@ -152,7 +109,11 @@ else if ({}.hasOwnProperty.call(from, 'current')) {

var modalProps = __assign(__assign({}, otherProps), { fromRect: fromRect,
fromRef: fromRef, isVisible: actualIsVisible, onRequestClose: function () {
fromRef: fromRef, isVisible: actualIsVisible, arrowSize: arrowSize ? new Size(arrowSize === null || arrowSize === void 0 ? void 0 : arrowSize.width, arrowSize === null || arrowSize === void 0 ? void 0 : arrowSize.height) : undefined, displayArea: displayArea
? new Rect(displayArea.x, displayArea.y, displayArea.width, displayArea.height)
: undefined, onRequestClose: function () {
if (onRequestClose)
onRequestClose();
_this.setState({ isVisible: false });
} });
},
// Handle changing CENTER -> FLOATING until CENTER is removed
placement: placement === Placement.CENTER ? Placement.FLOATING : placement });
if (mode === Mode.RN_MODAL) {

@@ -179,3 +140,2 @@ return (React.createElement(React.Fragment, null,

displayArea: PropTypes.oneOfType([
PropTypes.instanceOf(Rect),
PropTypes.exact({

@@ -200,2 +160,3 @@ x: PropTypes.number,

Placement.AUTO,
Placement.FLOATING,
Placement.CENTER

@@ -207,4 +168,11 @@ ]),

popoverStyle: stylePropType,
arrowStyle: stylePropType,
popoverShift: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number
}),
backgroundStyle: stylePropType,
arrowSize: PropTypes.shape({
width: PropTypes.number,
height: PropTypes.number
}),
arrowShift: PropTypes.number,

@@ -225,3 +193,3 @@ // lifecycle

popoverStyle: {},
arrowStyle: {},
arrowSize: DEFAULT_ARROW_SIZE,
backgroundStyle: {},

@@ -233,826 +201,2 @@ debug: false

export default Popover;
var RNModalPopover = /** @class */ (function (_super) {
__extends(RNModalPopover, _super);
function RNModalPopover() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = {
visible: false
};
return _this;
}
RNModalPopover.prototype.componentDidMount = function () {
if (this.props.isVisible) {
if (RNModalPopover.isShowingInModal)
console.warn(MULTIPLE_POPOVER_WARNING);
else
this.setState({ visible: true });
}
};
RNModalPopover.prototype.componentDidUpdate = function (prevProps, prevState) {
if (this.props.isVisible && !prevProps.isVisible) {
if (RNModalPopover.isShowingInModal)
console.warn(MULTIPLE_POPOVER_WARNING);
else
this.setState({ visible: true });
}
if (!this.state.visible && prevState.visible && this.props.onCloseComplete) {
/*
* Don't run this callback until after update, so that <Modal> is no longer active
* Need to wait 50ms to make sure <Modal> is completely gone, in case
* we want to show another popover immediately after
*/
setTimeout(this.props.onCloseComplete, 50);
}
};
RNModalPopover.prototype.render = function () {
var _this = this;
var _a = this.props, statusBarTranslucent = _a.statusBarTranslucent, onCloseStart = _a.onCloseStart, onRequestClose = _a.onRequestClose;
var visible = this.state.visible;
return (React.createElement(Modal, { transparent: true, supportedOrientations: ['portrait', 'portrait-upside-down', 'landscape'], hardwareAccelerated: true, visible: visible, statusBarTranslucent: statusBarTranslucent, onShow: function () {
RNModalPopover.isShowingInModal = true;
},
// Handles android back button
onRequestClose: onRequestClose },
React.createElement(AdaptivePopover, __assign({}, this.props, { onCloseStart: function () {
RNModalPopover.isShowingInModal = false;
if (onCloseStart)
onCloseStart();
}, onCloseComplete: function () { return _this.setState({ visible: false }); }, getDisplayAreaOffset: function () { return Promise.resolve(new Point(0, 0)); } }))));
};
RNModalPopover.isShowingInModal = false;
return RNModalPopover;
}(Component));
var JSModalPopover = /** @class */ (function (_super) {
__extends(JSModalPopover, _super);
function JSModalPopover() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = {
visible: false
};
_this.containerRef = React.createRef();
return _this;
}
JSModalPopover.prototype.componentDidMount = function () {
if (this.props.isVisible)
this.setState({ visible: true });
};
JSModalPopover.prototype.componentDidUpdate = function (prevProps) {
if (this.props.isVisible && !prevProps.isVisible)
this.setState({ visible: true });
};
JSModalPopover.prototype.render = function () {
var _this = this;
var onCloseComplete = this.props.onCloseComplete;
var visible = this.state.visible;
if (visible) {
return (React.createElement(View, { pointerEvents: "box-none", style: styles.container, ref: this.containerRef },
React.createElement(AdaptivePopover, __assign({}, this.props, { onCloseComplete: function () {
if (onCloseComplete)
onCloseComplete();
_this.setState({ visible: false });
}, getDisplayAreaOffset: function () { return __awaiter(_this, void 0, void 0, function () {
var rect;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, getRectForRef(this.containerRef)];
case 1:
rect = _a.sent();
return [2 /*return*/, new Point(rect.x, rect.y)];
}
});
}); } }))));
}
return null;
};
return JSModalPopover;
}(Component));
var AdaptivePopover = /** @class */ (function (_super) {
__extends(AdaptivePopover, _super);
function AdaptivePopover(props) {
var _this = _super.call(this, props) || this;
_this.state = {
fromRect: null,
shiftedDisplayArea: null,
defaultDisplayArea: null,
displayAreaOffset: null
};
/*
* This is used so that when the device is rotating
* or the viewport is expanding for any other reason,
* we can suspend updates due to content changes until
* we are finished calculating the new display
* area and rect for the new viewport size.
* This makes the recalc on rotation much faster.
*/
_this.waitForResizeToFinish = false;
_this.skipNextDefaultDisplayArea = false;
_this._isMounted = false;
_this.keyboardDidHideSubscription = null;
_this.keyboardDidShowSubscription = null;
_this.handleResizeEventSubscription = null;
_this.handleResizeEvent = _this.handleResizeEvent.bind(_this);
_this.keyboardDidHide = _this.keyboardDidHide.bind(_this);
_this.keyboardDidShow = _this.keyboardDidShow.bind(_this);
return _this;
}
AdaptivePopover.prototype.getUnshiftedDisplayArea = function () {
return this.props.displayArea ||
this.state.defaultDisplayArea ||
new Rect(0, 0, Dimensions.get('window').width, Dimensions.get('window').height);
};
// Apply insets and shifts if needed
AdaptivePopover.prototype.getDisplayArea = function () {
var _a, _b, _c, _d, _e, _f;
var displayAreaInsets = this.props.displayAreaInsets;
var displayArea = this.state.shiftedDisplayArea || this.getUnshiftedDisplayArea();
if (displayAreaInsets) {
return new Rect(displayArea.x + ((_a = displayAreaInsets.left) !== null && _a !== void 0 ? _a : 0), displayArea.x + ((_b = displayAreaInsets.top) !== null && _b !== void 0 ? _b : 0), displayArea.width - ((_c = displayAreaInsets.left) !== null && _c !== void 0 ? _c : 0) - ((_d = displayAreaInsets.right) !== null && _d !== void 0 ? _d : 0), displayArea.height - ((_e = displayAreaInsets.top) !== null && _e !== void 0 ? _e : 0) - ((_f = displayAreaInsets.bottom) !== null && _f !== void 0 ? _f : 0));
}
return displayArea;
};
AdaptivePopover.prototype.componentDidMount = function () {
this.handleResizeEventSubscription = Dimensions.addEventListener('change', this.handleResizeEvent);
if (this.props.fromRect)
this.setState({ fromRect: this.props.fromRect });
else if (this.props.fromRef)
this.calculateRectFromRef();
this._isMounted = true;
};
AdaptivePopover.prototype.componentWillUnmount = function () {
var _a, _b, _c, _d;
this._isMounted = false;
if (typeof ((_a = this.handleResizeEventSubscription) === null || _a === void 0 ? void 0 : _a.remove) === 'function')
(_b = this.handleResizeEventSubscription) === null || _b === void 0 ? void 0 : _b.remove();
else
// Backward-compatibility with RN <= 0.63
Dimensions.removeEventListener('change', this.handleResizeEvent);
(_c = this.keyboardDidShowSubscription) === null || _c === void 0 ? void 0 : _c.remove();
(_d = this.keyboardDidHideSubscription) === null || _d === void 0 ? void 0 : _d.remove();
};
AdaptivePopover.prototype.componentDidUpdate = function (prevProps) {
var _this = this;
// Make sure a value we care about has actually changed
var importantProps = ['from', 'displayArea'];
if (!importantProps.reduce(function (acc, key) { return acc || _this.props[key] !== prevProps[key]; }, false))
return;
if (this.props.fromRect &&
prevProps.fromRect &&
!Rect.equals(this.props.fromRect, prevProps.fromRect)) {
this.debug('componentDidUpdate - fromRect changed', this.props.fromRect);
this.setState({ fromRect: this.props.fromRect });
}
else if (this.props.fromRef) {
this.debug('componentDidUpdate - fromRef changed');
this.calculateRectFromRef();
}
if (this.props.isVisible && prevProps.isVisible) {
var displayArea = this.props.displayArea;
if ((this.props.displayArea && !prevProps.displayArea) ||
(displayArea &&
prevProps.displayArea &&
!Rect.equals(displayArea, prevProps.displayArea)) ||
(this.displayAreaStore &&
!Rect.equals(this.getDisplayArea(), this.displayAreaStore))) {
this.debug('componentDidUpdate - displayArea changed', this.getDisplayArea());
this.displayAreaStore = this.getDisplayArea();
}
}
};
// First thing called when device rotates
AdaptivePopover.prototype.handleResizeEvent = function (change) {
this.debug('handleResizeEvent - New Dimensions', change);
if (this.props.isVisible) {
this.waitForResizeToFinish = true;
}
};
AdaptivePopover.prototype.debug = function (line, obj) {
if (DEBUG || this.props.debug)
console.log("[" + (new Date()).toISOString() + "] " + line + (obj ? ": " + JSON.stringify(obj) : ''));
};
AdaptivePopover.prototype.setDefaultDisplayArea = function (newDisplayArea) {
return __awaiter(this, void 0, void 0, function () {
var defaultDisplayArea, isValidDisplayArea, displayAreaOffset_1;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this._isMounted)
return [2 /*return*/];
defaultDisplayArea = this.state.defaultDisplayArea;
isValidDisplayArea = newDisplayArea.width > 0 && newDisplayArea.height > 0;
if (!((!defaultDisplayArea || !Rect.equals(defaultDisplayArea, newDisplayArea)) &&
isValidDisplayArea)) return [3 /*break*/, 6];
this.debug('setDefaultDisplayArea - newDisplayArea', newDisplayArea);
if (!!this.skipNextDefaultDisplayArea) return [3 /*break*/, 5];
return [4 /*yield*/, this.props.getDisplayAreaOffset()];
case 1:
displayAreaOffset_1 = _a.sent();
this.debug('setDefaultDisplayArea - displayAreaOffset', displayAreaOffset_1);
return [4 /*yield*/, new Promise(function (resolve) {
_this.setState({ defaultDisplayArea: newDisplayArea, displayAreaOffset: displayAreaOffset_1 }, function () { return resolve(null); });
})];
case 2:
_a.sent();
if (!this.props.fromRef) return [3 /*break*/, 4];
return [4 /*yield*/, this.calculateRectFromRef()];
case 3:
_a.sent();
_a.label = 4;
case 4:
this.waitForResizeToFinish = false;
this.displayAreaStore = this.getDisplayArea();
_a.label = 5;
case 5:
if (this.skipNextDefaultDisplayArea)
this.debug('setDefaultDisplayArea - Skipping first because isLandscape');
this.skipNextDefaultDisplayArea = false;
_a.label = 6;
case 6: return [2 /*return*/];
}
});
});
};
// Custom type here, as KeyboardEvent type does not contain endCoordinates
AdaptivePopover.prototype.keyboardDidShow = function (e) {
this.debug("keyboardDidShow - keyboard height: " + e.endCoordinates.height);
this.shiftForKeyboard(e.endCoordinates.height);
};
AdaptivePopover.prototype.keyboardDidHide = function () {
this.debug('keyboardDidHide');
if (this._isMounted)
this.setState({ shiftedDisplayArea: null });
};
AdaptivePopover.prototype.shiftForKeyboard = function (keyboardHeight) {
var displayArea = this.getUnshiftedDisplayArea();
var absoluteVerticalCutoff = Dimensions.get('window').height - keyboardHeight - (isIOS ? 10 : 40);
var combinedY = Math.min(displayArea.height + displayArea.y, absoluteVerticalCutoff);
this.setState({
shiftedDisplayArea: {
x: displayArea.x,
y: displayArea.y,
width: displayArea.width,
height: combinedY - displayArea.y
}
});
};
AdaptivePopover.prototype.calculateRectFromRef = function () {
var _a, _b;
return __awaiter(this, void 0, void 0, function () {
var fromRef, initialRect, displayAreaOffset, count, verticalOffset, horizontalOffset, rect;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
fromRef = this.props.fromRef;
initialRect = this.state.fromRect || new Rect(0, 0, 0, 0);
displayAreaOffset = (_a = this.state.displayAreaOffset) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
this.debug('calculateRectFromRef - waiting for ref');
count = 0;
_c.label = 1;
case 1:
if (!!(fromRef === null || fromRef === void 0 ? void 0 : fromRef.current)) return [3 /*break*/, 3];
return [4 /*yield*/, new Promise(function (resolve) {
setTimeout(resolve, 100);
})];
case 2:
_c.sent();
// Timeout after 2 seconds
if (count++ > 20)
return [2 /*return*/];
return [3 /*break*/, 1];
case 3:
verticalOffset = ((_b = this.props.verticalOffset) !== null && _b !== void 0 ? _b : 0) - displayAreaOffset.y;
horizontalOffset = -displayAreaOffset.x;
this.debug('calculateRectFromRef - waiting for ref to move');
count = 0;
_c.label = 4;
case 4: return [4 /*yield*/, getRectForRef(fromRef)];
case 5:
rect = _c.sent();
rect = new Rect(rect.x + horizontalOffset, rect.y + verticalOffset, rect.width, rect.height);
// Timeout after 2 seconds
if (count++ > 20)
return [2 /*return*/];
_c.label = 6;
case 6:
if (Rect.equals(rect, initialRect)) return [3 /*break*/, 4];
_c.label = 7;
case 7:
this.debug('calculateRectFromRef - calculated Rect', rect);
if (this._isMounted)
this.setState({ fromRect: rect });
return [2 /*return*/];
}
});
});
};
AdaptivePopover.prototype.render = function () {
var _this = this;
var _a = this.props, onOpenStart = _a.onOpenStart, onCloseStart = _a.onCloseStart, fromRef = _a.fromRef, otherProps = __rest(_a, ["onOpenStart", "onCloseStart", "fromRef"]);
var fromRect = this.state.fromRect;
// Don't render popover until we have an initial fromRect calculated for the view
if (fromRef && !fromRect)
return null;
return (React.createElement(BasePopover, __assign({}, otherProps, { displayArea: this.getDisplayArea(), fromRect: fromRect, onOpenStart: function () {
if (onOpenStart)
onOpenStart();
_this.debug('Setting up keyboard listeners');
_this.keyboardDidShowSubscription = Keyboard.addListener('keyboardDidShow', _this.keyboardDidShow);
_this.keyboardDidHideSubscription = Keyboard.addListener('keyboardDidHide', _this.keyboardDidHide);
_this.displayAreaStore = _this.getDisplayArea();
}, onCloseStart: function () {
if (onCloseStart)
onCloseStart();
_this.debug('Tearing down keyboard listeners');
if (_this.keyboardDidShowSubscription !== null) {
_this.keyboardDidShowSubscription.remove();
_this.keyboardDidShowSubscription = null;
}
if (_this.keyboardDidHideSubscription !== null) {
_this.keyboardDidHideSubscription.remove();
_this.keyboardDidHideSubscription = null;
}
if (_this._isMounted)
_this.setState({ shiftedDisplayArea: null });
}, skipMeasureContent: function () { return _this.waitForResizeToFinish; }, onDisplayAreaChanged: function (rect) { return _this.setDefaultDisplayArea(rect); } })));
};
return AdaptivePopover;
}(Component));
var BasePopover = /** @class */ (function (_super) {
__extends(BasePopover, _super);
function BasePopover() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.state = {
requestedContentSize: null,
activeGeom: undefined,
nextGeom: undefined,
showing: false,
animatedValues: {
scale: new Animated.Value(0),
translate: new Animated.ValueXY(),
fade: new Animated.Value(0),
translateArrow: new Animated.ValueXY()
}
};
_this._isMounted = false;
_this.animating = false;
_this.animateOutAfterShow = false;
_this.popoverRef = React.createRef();
_this.arrowRef = React.createRef();
return _this;
}
BasePopover.prototype.debug = function (line, obj) {
if (DEBUG || this.props.debug)
console.log("[" + (new Date()).toISOString() + "] " + line + (obj ? ": " + JSON.stringify(obj) : ''));
};
BasePopover.prototype.componentDidMount = function () {
this._isMounted = true;
};
BasePopover.prototype.componentDidUpdate = function (prevProps) {
var _this = this;
// Make sure a value we care about has actually changed
var importantProps = ['isVisible', 'fromRect', 'displayArea', 'verticalOffset', 'placement'];
if (!importantProps.reduce(function (acc, key) { return acc || _this.props[key] !== prevProps[key]; }, false))
return;
if (this.props.isVisible !== prevProps.isVisible) {
this.debug("componentDidUpdate - isVisible changed, now " + this.props.isVisible);
if (!this.props.isVisible) {
if (this.state.showing)
this.animateOut();
else
this.animateOutAfterShow = true;
this.debug('componentDidUpdate - Hiding popover');
}
}
else if (this.props.isVisible && prevProps.isVisible) {
this.handleChange();
}
};
BasePopover.prototype.componentWillUnmount = function () {
this._isMounted = false;
if (this.state.showing) {
this.animateOut();
}
};
BasePopover.prototype.measureContent = function (requestedContentSize) {
var _this = this;
if (!requestedContentSize.width)
console.warn("Popover Warning - Can't Show - The Popover content has a width of 0, so there is nothing to present.");
if (!requestedContentSize.height)
console.warn("Popover Warning - Can't Show - The Popover content has a height of 0, so there is nothing to present.");
if (this.props.skipMeasureContent()) {
this.debug("measureContent - Skippting, waiting for resize to finish");
return;
}
if (requestedContentSize.width && requestedContentSize.height) {
this.debug("measureContent - new requestedContentSize: " + JSON.stringify(requestedContentSize) + " (used to be " + JSON.stringify(this.state.requestedContentSize) + ")");
this.setState({ requestedContentSize: requestedContentSize }, function () { return _this.handleChange(); });
}
};
/*
* Many factors may cause the geometry to change.
* This function collects all of them, waiting for 200ms after the last change,
* then takes action, either bringing up the popover or moving it to its new location
*/
BasePopover.prototype.handleChange = function () {
var _this = this;
if (this.handleChangeTimeout)
clearTimeout(this.handleChangeTimeout);
/*
* This function will be called again once we have a requested content size,
* so safe to ignore for now
*/
if (!this.state.requestedContentSize) {
this.debug('handleChange - no requestedContentSize, exiting...');
return;
}
this.debug('handleChange - waiting 100ms to accumulate all changes');
this.handleChangeTimeout = setTimeout(function () {
var _a = _this.state, activeGeom = _a.activeGeom, animatedValues = _a.animatedValues, requestedContentSize = _a.requestedContentSize;
var _b = _this.props, arrowStyle = _b.arrowStyle, popoverStyle = _b.popoverStyle, fromRect = _b.fromRect, displayArea = _b.displayArea, placement = _b.placement, onOpenStart = _b.onOpenStart, arrowShift = _b.arrowShift, onPositionChange = _b.onPositionChange;
if (requestedContentSize) {
_this.debug('handleChange - requestedContentSize', requestedContentSize);
_this.debug('handleChange - displayArea', displayArea);
_this.debug('handleChange - fromRect', fromRect);
if (placement)
_this.debug('handleChange - placement', placement.toString());
var geom_1 = computeGeometry({
requestedContentSize: requestedContentSize,
placement: placement,
fromRect: fromRect,
displayArea: displayArea,
arrowStyle: arrowStyle,
popoverStyle: popoverStyle,
arrowShift: arrowShift,
debug: _this.debug.bind(_this),
previousPlacement: _this.getGeom().placement
});
_this.setState({ nextGeom: geom_1, requestedContentSize: requestedContentSize }, function () {
if (geom_1.viewLargerThanDisplayArea.width || geom_1.viewLargerThanDisplayArea.height) {
/*
* If the view initially overflowed the display area,
* wait one more render cycle to test-render it within
* the display area to get final calculations for popoverOrigin before show
*/
_this.debug('handleChange - delaying showing popover because viewLargerThanDisplayArea');
}
else if (!activeGeom) {
_this.debug('handleChange - animating in');
if (onOpenStart)
setTimeout(onOpenStart);
_this.animateIn();
}
else if (activeGeom && !Geometry.equals(activeGeom, geom_1)) {
var moveTo = new Point(geom_1.popoverOrigin.x, geom_1.popoverOrigin.y);
_this.debug('handleChange - Triggering popover move to', moveTo);
_this.animateTo({
values: animatedValues,
fade: 1,
scale: 1,
translatePoint: moveTo,
easing: Easing.inOut(Easing.quad),
geom: geom_1,
callback: onPositionChange
});
}
else {
_this.debug('handleChange - no change');
}
});
}
}, 100);
};
BasePopover.getPolarity = function () {
return I18nManager.isRTL ? -1 : 1;
};
BasePopover.prototype.getGeom = function () {
var _a = this.state, activeGeom = _a.activeGeom, nextGeom = _a.nextGeom;
if (activeGeom)
return activeGeom;
if (nextGeom)
return nextGeom;
return new Geometry({
popoverOrigin: new Point(0, 0),
anchorPoint: new Point(0, 0),
placement: Placement.AUTO,
forcedContentSize: null,
viewLargerThanDisplayArea: {
width: false,
height: false
}
});
};
BasePopover.prototype.getArrowDynamicStyle = function (geom) {
var placement = (geom || this.getGeom()).placement;
var _a = this.props, arrowStyle = _a.arrowStyle, popoverStyle = _a.popoverStyle;
var _b = this.getCalculatedArrowDims(geom), width = _b.width, height = _b.height;
var backgroundColor = StyleSheet.flatten(arrowStyle).backgroundColor ||
StyleSheet.flatten(popoverStyle).backgroundColor ||
styles.popoverContent.backgroundColor;
var colors = {};
switch (placement) {
case Placement.TOP:
colors = { borderTopColor: backgroundColor };
break;
case Placement.BOTTOM:
colors = { borderBottomColor: backgroundColor };
break;
case Placement.LEFT:
colors = { borderLeftColor: backgroundColor };
break;
case Placement.RIGHT:
colors = { borderRightColor: backgroundColor };
break;
default:
}
/*
* Create the arrow from a rectangle with the appropriate borderXWidth set
* A rotation is then applied dependending on the placement
* Also make it slightly bigger
* to fix a visual artifact when the popover is animated with a scale
*/
return __assign({ width: width,
height: height, borderTopWidth: height / 2, borderRightWidth: width / 2, borderBottomWidth: height / 2, borderLeftWidth: width / 2 }, colors);
};
BasePopover.prototype.getCalculatedArrowDims = function (geom) {
var placement = (geom || this.getGeom()).placement;
var arrowSize = getArrowSize(placement, this.props.arrowStyle);
switch (placement) {
case Placement.LEFT:
case Placement.RIGHT:
arrowSize.height += 2;
arrowSize.width = (arrowSize.width * 2) + 2;
break;
default:
arrowSize.width += 2;
arrowSize.height = (arrowSize.height * 2) + 2;
}
return arrowSize;
};
BasePopover.prototype.getArrowTranslateLocation = function (translatePoint, geom) {
if (translatePoint === void 0) { translatePoint = null; }
var requestedContentSize = this.state.requestedContentSize;
var anchorPoint = geom.anchorPoint, placement = geom.placement, forcedContentSize = geom.forcedContentSize, viewLargerThanDisplayArea = geom.viewLargerThanDisplayArea;
var _a = this.getCalculatedArrowDims(geom), arrowWidth = _a.width, arrowHeight = _a.height;
var viewWidth = 0;
if (viewLargerThanDisplayArea.width && (forcedContentSize === null || forcedContentSize === void 0 ? void 0 : forcedContentSize.width))
viewWidth = forcedContentSize.width;
else if (requestedContentSize === null || requestedContentSize === void 0 ? void 0 : requestedContentSize.width)
viewWidth = requestedContentSize.width;
var viewHeight = 0;
if (viewLargerThanDisplayArea.height && (forcedContentSize === null || forcedContentSize === void 0 ? void 0 : forcedContentSize.height))
viewHeight = forcedContentSize.height;
else if (requestedContentSize === null || requestedContentSize === void 0 ? void 0 : requestedContentSize.height)
viewHeight = requestedContentSize.height;
var arrowX = anchorPoint.x - (arrowWidth / 2);
var arrowY = anchorPoint.y - (arrowHeight / 2);
var borderRadius = getBorderRadius(this.props.popoverStyle);
// Ensuring that the arrow does not go outside the bounds of the content box during a move
if (translatePoint) {
if (placement === Placement.LEFT || placement === Placement.RIGHT) {
if (translatePoint.y > (arrowY - borderRadius))
arrowY = translatePoint.y + borderRadius;
else if (viewHeight && translatePoint.y + viewHeight < arrowY + arrowHeight)
arrowY = translatePoint.y + viewHeight - arrowHeight - borderRadius;
}
else if (placement === Placement.TOP || placement === Placement.BOTTOM) {
if (translatePoint.x > arrowX - borderRadius)
arrowX = translatePoint.x + borderRadius;
else if (viewWidth && translatePoint.x + viewWidth < arrowX + arrowWidth)
arrowX = translatePoint.x + viewWidth - arrowWidth - borderRadius;
}
}
// eslint-disable-next-line
return new Point(arrowX, FIX_SHIFT /* Temp fix for useNativeDriver issue */ + arrowY);
};
BasePopover.prototype.getTranslateOrigin = function () {
var requestedContentSize = this.state.requestedContentSize;
var _a = this.getGeom(), forcedContentSize = _a.forcedContentSize, viewLargerThanDisplayArea = _a.viewLargerThanDisplayArea, popoverOrigin = _a.popoverOrigin, anchorPoint = _a.anchorPoint;
var viewWidth = 0;
if (viewLargerThanDisplayArea.width && (forcedContentSize === null || forcedContentSize === void 0 ? void 0 : forcedContentSize.width))
viewWidth = forcedContentSize.width;
else if (requestedContentSize === null || requestedContentSize === void 0 ? void 0 : requestedContentSize.width)
viewWidth = requestedContentSize.width;
var viewHeight = 0;
if (viewLargerThanDisplayArea.height && (forcedContentSize === null || forcedContentSize === void 0 ? void 0 : forcedContentSize.height))
viewHeight = forcedContentSize.height;
else if (requestedContentSize === null || requestedContentSize === void 0 ? void 0 : requestedContentSize.height)
viewHeight = requestedContentSize.height;
var popoverCenter = new Point(popoverOrigin.x + (viewWidth / 2), popoverOrigin.y + (viewHeight / 2));
var shiftHorizontal = anchorPoint.x - popoverCenter.x;
var shiftVertical = anchorPoint.y - popoverCenter.y;
this.debug('getTranslateOrigin - popoverOrigin', popoverOrigin);
this.debug('getTranslateOrigin - popoverSize', { width: viewWidth, height: viewHeight });
this.debug('getTranslateOrigin - anchorPoint', anchorPoint);
this.debug('getTranslateOrigin - shift', { hoizontal: shiftHorizontal, vertical: shiftVertical });
return new Point(popoverOrigin.x + shiftHorizontal, popoverOrigin.y + shiftVertical);
};
BasePopover.prototype.animateOut = function () {
var _this = this;
if (this.props.onCloseStart)
setTimeout(this.props.onCloseStart);
if (this._isMounted)
this.setState({ showing: false });
this.animateTo({
values: this.state.animatedValues,
fade: 0,
scale: 0,
translatePoint: this.getTranslateOrigin(),
callback: function () { return setTimeout(_this.props.onCloseComplete); },
easing: Easing.inOut(Easing.quad),
geom: this.getGeom()
});
};
BasePopover.prototype.animateIn = function () {
var _this = this;
var nextGeom = this.state.nextGeom;
if (nextGeom !== undefined && nextGeom instanceof Geometry) {
var values = this.state.animatedValues;
// Should grow from anchor point
var translateStart = this.getTranslateOrigin();
// eslint-disable-next-line
translateStart.y += FIX_SHIFT; // Temp fix for useNativeDriver issue
values.translate.setValue(translateStart);
var translatePoint = new Point(nextGeom.popoverOrigin.x, nextGeom.popoverOrigin.y);
values.translateArrow.setValue(this.getArrowTranslateLocation(translatePoint, nextGeom));
this.animateTo({
values: values,
fade: 1,
scale: 1,
translatePoint: translatePoint,
easing: Easing.out(Easing.back(1)),
geom: nextGeom,
callback: function () {
if (_this._isMounted) {
_this.setState({ showing: true });
if (_this.props.debug || DEBUG) {
setTimeout(function () {
return _this.popoverRef.current &&
getRectForRef(_this.popoverRef).then(function (rect) { return _this.debug('animateIn - onOpenComplete - Calculated Popover Rect', rect); });
});
setTimeout(function () {
return _this.arrowRef.current &&
getRectForRef(_this.arrowRef).then(function (rect) { return _this.debug('animateIn - onOpenComplete - Calculated Arrow Rect', rect); });
});
}
}
if (_this.props.onOpenComplete)
setTimeout(_this.props.onOpenComplete);
if (_this.animateOutAfterShow || !_this._isMounted) {
_this.animateOut();
_this.animateOutAfterShow = false;
}
}
});
}
};
BasePopover.prototype.animateTo = function (args) {
var _this = this;
var fade = args.fade, translatePoint = args.translatePoint, scale = args.scale, callback = args.callback, easing = args.easing, values = args.values, geom = args.geom;
var commonConfig = __assign({ duration: 300, easing: easing, useNativeDriver: !isWeb }, this.props.animationConfig);
if (this.animating) {
setTimeout(function () { return _this.animateTo(args); }, 100);
return;
}
var newArrowLocation = this.getArrowTranslateLocation(translatePoint, geom);
// eslint-disable-next-line
translatePoint.y = translatePoint.y + FIX_SHIFT; // Temp fix for useNativeDriver issue
if (!fade && fade !== 0) {
console.log('Popover: Fade value is null');
return;
}
if (!translatePoint) {
console.log('Popover: Translate Point value is null');
return;
}
if (!scale && scale !== 0) {
console.log('Popover: Scale value is null');
return;
}
this.animating = true;
Animated.parallel([
Animated.timing(values.fade, __assign(__assign({}, commonConfig), { toValue: fade })),
Animated.timing(values.translate, __assign(__assign({}, commonConfig), { toValue: translatePoint })),
Animated.timing(values.scale, __assign(__assign({}, commonConfig), { toValue: scale })),
Animated.timing(values.translateArrow, __assign(__assign({}, commonConfig), { toValue: newArrowLocation }))
]).start(function () {
_this.animating = false;
if (_this._isMounted)
_this.setState({ activeGeom: _this.state.nextGeom });
if (callback)
callback();
});
};
BasePopover.prototype.render = function () {
var _this = this;
var geom = this.getGeom();
var _a = this.state, animatedValues = _a.animatedValues, nextGeom = _a.nextGeom;
var popoverStyle = this.props.popoverStyle;
var _b = this.getCalculatedArrowDims(), arrowWidth = _b.width, arrowHeight = _b.height;
var arrowScale = animatedValues.scale.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp'
});
var arrowViewStyle = __assign(__assign({
// eslint-disable-next-line
position: 'absolute', top: 0 }, (I18nManager.isRTL ? { right: 0 } : { left: 0 })), { width: arrowWidth, height: arrowHeight, transform: [
{ translateX: animatedValues.translateArrow.x },
{ translateY: animatedValues.translateArrow.y },
{ scale: arrowScale }
] });
var arrowInnerStyle = [
styles.arrow,
this.getArrowDynamicStyle()
];
// Temp fix for useNativeDriver issue
var backgroundShift = animatedValues.fade.interpolate({
inputRange: [0, 0.0001, 1],
outputRange: [0, FIX_SHIFT, FIX_SHIFT]
});
var backgroundStyle = __assign(__assign(__assign({}, styles.background), { transform: [{ translateY: backgroundShift }] }), StyleSheet.flatten(this.props.backgroundStyle));
var containerStyle = __assign(__assign({}, styles.container), { opacity: animatedValues.fade });
var popoverViewStyle = __assign(__assign(__assign(__assign({}, styles.dropShadow), styles.popoverContent), StyleSheet.flatten(popoverStyle)), { transform: [
{ translateX: animatedValues.translate.x },
{ translateY: animatedValues.translate.y },
{ scale: animatedValues.scale },
{ perspective: 1000 }
] });
/*
* We want to always use next here, because the we need this to re-render
* before we can animate to the correct spot for the active.
*/
if (nextGeom) {
popoverViewStyle.maxWidth =
(nextGeom.forcedContentSize || { width: null }).width || undefined;
popoverViewStyle.maxHeight =
(nextGeom.forcedContentSize || { height: null }).height || undefined;
}
return (React.createElement(View, { pointerEvents: "box-none", style: [styles.container, { top: -1 * FIX_SHIFT }] },
React.createElement(View, { pointerEvents: "box-none", style: [styles.container, { top: FIX_SHIFT, flex: 1 }], onLayout: function (evt) { return _this.props.onDisplayAreaChanged(new Rect(evt.nativeEvent.layout.x, evt.nativeEvent.layout.y - FIX_SHIFT, evt.nativeEvent.layout.width, evt.nativeEvent.layout.height)); } }),
React.createElement(Animated.View, { pointerEvents: "box-none", style: containerStyle },
this.props.showBackground !== false && (React.createElement(TouchableWithoutFeedback, { onPress: this.props.onRequestClose },
React.createElement(Animated.View, { style: backgroundStyle }))),
React.createElement(View, { pointerEvents: "box-none", style: { top: 0, left: 0 } },
React.createElement(Animated.View, { style: popoverViewStyle, ref: this.popoverRef, onLayout: function (evt) {
var layout = __assign({}, evt.nativeEvent.layout);
setTimeout(function () { return _this._isMounted && _this.measureContent(layout); }, 10);
} }, this.props.children),
geom.placement !== Placement.CENTER &&
React.createElement(Animated.View, { style: arrowViewStyle, ref: this.arrowRef },
React.createElement(Animated.View, { style: arrowInnerStyle }))))));
};
return BasePopover;
}(Component));
var styles = StyleSheet.create({
container: {
top: 0,
bottom: 0,
left: 0,
right: 0,
position: 'absolute',
backgroundColor: 'transparent'
},
background: {
top: 0,
bottom: FIX_SHIFT,
left: 0,
right: 0,
position: 'absolute',
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
contentContainer: {
flexDirection: 'column'
},
popoverContainer: {
position: 'absolute',
zIndex: 1000
},
popoverContent: {
position: 'absolute',
backgroundColor: 'white',
borderBottomColor: '#333438',
borderRadius: DEFAULT_BORDER_RADIUS,
overflow: 'hidden'
},
selectContainer: {
backgroundColor: '#f2f2f2',
position: 'absolute'
},
dropShadow: {
shadowColor: 'black',
shadowOffset: {
width: 0,
height: 2
},
shadowRadius: 2,
shadowOpacity: 0.8
},
arrow: {
position: 'absolute',
borderTopColor: 'transparent',
borderRightColor: 'transparent',
borderBottomColor: 'transparent',
borderLeftColor: 'transparent'
}
});
//# sourceMappingURL=Popover.js.map

@@ -0,1 +1,2 @@

import { Animated, StyleProp, ViewStyle } from 'react-native';
export declare enum Placement {

@@ -7,2 +8,3 @@ TOP = "top",

AUTO = "auto",
FLOATING = "floating",
CENTER = "center"

@@ -21,1 +23,49 @@ }

};
export interface ModalPopoverState {
visible: boolean;
}
export declare type PopoverProps = {
isVisible?: boolean;
placement?: Placement;
animationConfig?: Partial<Animated.TimingAnimationConfig>;
offset?: number;
verticalOffset?: number;
displayArea?: Rect;
displayAreaInsets?: Insets;
popoverStyle?: StyleProp<ViewStyle>;
popoverShift?: {
x?: number;
y?: number;
};
backgroundStyle?: StyleProp<ViewStyle>;
arrowShift?: number;
arrowSize?: Size;
onOpenStart?: () => void;
onOpenComplete?: () => void;
onRequestClose?: () => void;
onCloseStart?: () => void;
onCloseComplete?: () => void;
onPositionChange?: () => void;
debug?: boolean;
};
export declare class Point {
x: number;
y: number;
constructor(x: number, y: number);
equals(b: Point): boolean;
}
export declare class Size {
width: number;
height: number;
constructor(width: number, height: number);
equals(b: Size): boolean;
}
export declare class Rect {
x: number;
y: number;
width: number;
height: number;
constructor(x: number, y: number, width: number, height: number);
equals(b: Rect): boolean;
static clone(rect: Rect): Rect;
}

@@ -9,2 +9,4 @@ // eslint-disable-next-line

Placement["AUTO"] = "auto";
Placement["FLOATING"] = "floating";
// deprecated
Placement["CENTER"] = "center";

@@ -19,2 +21,44 @@ })(Placement || (Placement = {}));

})(Mode || (Mode = {}));
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.equals = function (b) {
return Math.round(this.x) === Math.round(b.x) && Math.round(this.y) === Math.round(b.y);
};
return Point;
}());
export { Point };
var Size = /** @class */ (function () {
function Size(width, height) {
this.width = width;
this.height = height;
}
Size.prototype.equals = function (b) {
return Math.round(this.width) === Math.round(b.width) &&
Math.round(this.height) === Math.round(b.height);
};
return Size;
}());
export { Size };
var Rect = /** @class */ (function () {
function Rect(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
Rect.prototype.equals = function (b) {
return (Math.round(this.x) === Math.round(b.x) &&
Math.round(this.y) === Math.round(b.y) &&
Math.round(this.width) === Math.round(b.width) &&
Math.round(this.height) === Math.round(b.height));
};
Rect.clone = function (rect) {
return new Rect(rect.x, rect.y, rect.width, rect.height);
};
return Rect;
}());
export { Rect };
//# sourceMappingURL=Types.js.map
import { RefObject, ComponentClass, Component } from 'react';
import { StyleProp, ViewStyle } from 'react-native';
import { Placement } from './Types';
export declare class Point {
x: number;
y: number;
constructor(x: number, y: number);
static equals(a: Point, b: Point): boolean;
}
export declare class Size {
width: number;
height: number;
constructor(width: number, height: number);
static equals(a: Size, b: Size): boolean;
}
export declare class Rect {
x: number;
y: number;
width: number;
height: number;
constructor(x: number, y: number, width: number, height: number);
static equals(a: Rect, b: Rect): boolean;
static clone(rect: Rect): Rect;
}
import { Placement, Point, Rect, Size } from './Types';
declare type RefType = RefObject<number | Component<any, any, any> | ComponentClass<any, any> | null>;

@@ -34,2 +13,3 @@ export declare function getRectForRef(ref: RefType): Promise<Rect>;

export declare function getBorderRadius(popoverStyle: StyleProp<ViewStyle>): number;
export declare function getChangedProps(props: Record<string, unknown>, prevProps: Record<string, unknown>, importantProps: string[]): string[];
export {};

@@ -38,46 +38,4 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

import { NativeModules, findNodeHandle, StyleSheet } from 'react-native';
import { Placement } from './Types';
import { Placement, Rect, Size } from './Types';
import { DEFAULT_ARROW_SIZE, DEFAULT_BORDER_RADIUS } from './Constants';
var Point = /** @class */ (function () {
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.equals = function (a, b) {
return Math.round(a.x) === Math.round(b.x) && Math.round(a.y) === Math.round(b.y);
};
return Point;
}());
export { Point };
var Size = /** @class */ (function () {
function Size(width, height) {
this.width = width;
this.height = height;
}
Size.equals = function (a, b) {
return Math.round(a.width) === Math.round(b.width) &&
Math.round(a.height) === Math.round(b.height);
};
return Size;
}());
export { Size };
var Rect = /** @class */ (function () {
function Rect(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
Rect.equals = function (a, b) {
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));
};
Rect.clone = function (rect) {
return new Rect(rect.x, rect.y, rect.width, rect.height);
};
return Rect;
}());
export { Rect };
export function getRectForRef(ref) {

@@ -120,3 +78,3 @@ return new Promise(function (resolve, reject) {

case 5:
if (Rect.equals(first, second)) return [3 /*break*/, 1];
if (first.equals(second)) return [3 /*break*/, 1];
_a.label = 6;

@@ -180,2 +138,12 @@ case 6: return [2 /*return*/];

}
export function getChangedProps(props, prevProps, importantProps) {
return importantProps.filter(function (key) {
var curVal = props[key];
var prevVal = prevProps[key];
if (curVal instanceof Rect && prevVal instanceof Rect) {
return !curVal.equals(prevVal);
}
return curVal !== prevVal;
});
}
//# sourceMappingURL=Utility.js.map
{
"name": "react-native-popover-view",
"version": "4.1.0",
"version": "5.0.0",
"description": "A <Popover /> component for react-native iOS, Android, and Web",

@@ -58,2 +58,3 @@ "main": "dist/index.js",

"postinstall-postinstall": "^2.1.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",

@@ -60,0 +61,0 @@ "react-addons-test-utils": "^15.6.2",

@@ -7,12 +7,13 @@ ## react-native-popover-view

A well-tested, adaptable, lightweight `<Popover>` component for react-native. Tested and working on iOS and Android. May work on Web, but not officially supported.
A well-tested, adaptable, lightweight `<Popover>` component for react-native with no dependencies. Tested and working on iOS and Android. May work on Web, but not officially supported.
It is written entirely in TypeScript, but uses [React Native's native driver](https://facebook.github.io/react-native/blog/2017/02/14/using-native-driver-for-animated.html) for responsive animations, even when the JS thread is busy.
It is written entirely in TypeScript and uses [React Native's native driver](https://reactnative.dev/docs/animations#using-the-native-driver) for responsive animations, even when the JS thread is busy.
The `<Popover>` is able to handle dynamic content and adapt to screen size changes while showing, and will move out of the way for on-screen keyboards automatically.
![Demo Video](gifs/react-native-popover-view-smaller.gif)
You can play around with all of the features using [the Expo test app](https://expo.io/@steffeydev/popover-view-test-app).
Source Code: [react-native-popover-view-test-app](https://github.com/SteffeyDev/react-native-popover-view-test-app)
##### Table of Contents
* [Features](#features)
* [Demo](#demo)
* [Installation](#installation)

@@ -28,16 +29,6 @@ * [Usage](#usage)

## <a name="features"/>Popover Features
* Extremely simple but also highly customizable
* Moves to avoid keyboard
* Ability to show from a view, from a rect, or float in center of screen
* Adapts to changing content size
* Automatically detects best placement on screen
* Moves to stay visible on orientation change or when entering split-screen mode
* Great for use in Tablets: you can put entire views that you would normally show in a modal (on a smaller device) into a popover, optionally give it an anchor point, and have it float on top of all of the other views.
* **Simple**: By default the popover will float in the center of the screen, but if you choose to anchor it to a view (like a `Touchable`) or to a point on the screen, it will automatically find the best placement to accommodate the popover content.
* **Customizable**: Tweak everything, including the popover and arrow style, placement heuristics, animation configuration, and more.
* **Adaptable**: Popover adapts to changing content size, screen size, and orientation, and will move to accomidate the on-screen keyboard for text input into the popover.
## <a name="demo"/>Demo App
You can play around with the various features using [the Expo test app](https://expo.io/@steffeydev/popover-view-test-app).
Source Code: [react-native-popover-view-test-app](https://github.com/SteffeyDev/react-native-popover-view-test-app)
## <a name="installation"/>Installation

@@ -181,5 +172,5 @@

### Showing popover centered on the screen
### Showing popover without anchor
If you just want the popover to be centered on the screen, not anchored to anything, you can omit the `from` prop altogether.
If you just want the popover to be floating on the screen, not anchored to anything, you can omit the `from` prop altogether.

@@ -303,23 +294,34 @@ ```jsx

----------------- | -------- | ----------- | -----------
isVisible | bool | false | Show/Hide the popover
from | multiple | null | Popover source. See [From](#from) section below.
isVisible | bool | false | Show/Hide the popover. Required if `from` is *not* a Touchable or function that uses `showPopover` call (see examples). If supplied, takes precedence regardless of `from`.
mode | string | 'rn-modal' | One of: 'rn-modal', 'js-modal', 'tooltip'. See [Mode](#mode) section below for details.
from | element OR | Yes | null | Either a React element, a function that returns a React element, a `ref` created from `React.createRef` or `React.useRef`, or a Rect object created by `new Rect(x, y, width, height)`.
displayArea | rect | | Area where the popover is allowed to be displayed. By default, this will be automatically calculated to be the size of the display, or the size of the parent component if mode is not 'rn-modal'.
placement | string | 'auto' | How to position the popover - top &#124; bottom &#124; left &#124; right &#124; center &#124; auto. When 'auto' is specified, it will determine the ideal placement so that the popover is fully visible within `displayArea`.
animationConfig | object | | An object containing any configuration options that can be passed to Animated.timing (e.g. `{ duration: 600, easing: Easing.inOut(Easing.quad) }`). The configuration options you pass will override the defaults for all animations.
verticalOffset | number | 0 | The amount to vertically shift the popover on the screen. In certain Android configurations, you may need to apply a `verticalOffset` of `-StatusBar.currentHeight` for the popover to originate from the correct place.
statusBarTranslucent | bool | | For 'rn-modal' mode only. Determines whether the background should go under the status bar. Passed through to RN `Modal` component, see [their docs](https://reactnative.dev/docs/modal#statusbartranslucent-1) as well.
displayAreaInsets | object | | Insets to apply to the display area. The Popover will not be allowed to go beyond the display area minus the insets.
placement | string | 'auto' | How to position the popover, one of 'top', 'bottom', 'left', 'right', 'floating', or 'auto'. When 'auto' is specified, it will try to determine the best placement so that the popover is fully visible within `displayArea`.
offset | number | 0 | The amount to shift the popover away from the source. Does not apply if the popover is centered.
popoverStyle | object | | The style of the popover itself. You can override the `borderRadius`, `backgroundColor`, or any other [`style` prop for a `View`](https://facebook.github.io/react-native/docs/view-style-props.html).
backgroundStyle | object | | The style of the background that fades in.
arrowStyle | object | | The style of the arrow that points to the rect. Supported options are `width`, `height`, and `backgroundColor`. You can use `{backgroundColor: 'transparent'}` to hide the arrow completely.
popoverShift | object | | How much to shift the popover in each direction, as a multiplier. Object of shape `{ x: -1 to 1, y: -1 to 1 }`, where both `x` and `y` are optional. `-1` shifts the popover all the way to the left/top and `1` shifts it to the right/bottom. Currently only applies when placement is `floating`, but this will apply to all placements in a future version.
backgroundStyle | object | | The style of the background view. Default is a black background with 0.5 opacity.
arrowSize | object | `{ width: 16, height: 8 }` | The size of the arrow, as an object with `width` & `height` properties. The width of the arrow is the size of the arrow on the edge that touches the popover (base of isosceles triangle), while the height covers the distance from the popover to the source view, regardless of the placement of the popover. You can use `{ width: 0, height: 0 }` to hide the arrow completely.
arrowShift | number | 0 | How much to shift the arrow to either side, as a multiplier. `-1` will shift it all the way to the left (or top) corner of the source view, while `1` will shift all the way to the right (or bottom) corner. A value of `0.5` or `-0.8` will shift it partly to one side.
onOpenStart | function | | Callback to be fired when the open animation starts (before animation)
onOpenComplete | function | | Callback to be fired when the open animation ends (after animation)
onRequestClose | function | | Callback to be fired when the user taps outside the popover (on the background) or taps the "hardware" back button on Android
onRequestClose | function | | Callback to be fired when the user taps outside the popover (on the background) or taps the system back button on Android
onCloseStart | function | | Callback to be fired when the popover starts closing (before animation)
onCloseComplete | function | | Callback to be fired when the popover is finished closing (after animation)
onPositionChange | function | | Callback to be fired when the popover position finishes moving position (after animation)
animationConfig | object | | An object containing any configuration options that can be passed to Animated.timing (e.g. `{ duration: 600, easing: Easing.inOut(Easing.quad) }`). The configuration options you pass will override the defaults for all animations.
displayArea | rect | | Area where the popover is allowed to be displayed. By default, this will be automatically calculated to be the size of the display, or the size of the parent component if mode is not 'rn-modal'.
displayAreaInsets | object | | Insets to apply to the display area. The Popover will not be allowed to go beyond the display area minus the insets.
statusBarTranslucent | bool | | For 'rn-modal' mode on Android only. Determines whether the background should go under the status bar. Passed through to RN `Modal` component, see [their docs](https://reactnative.dev/docs/modal#statusbartranslucent-1) as well.
verticalOffset | number | 0 | The amount to vertically shift the popover on the screen, for use in correcting an occasional issue on Android. In certain Android configurations, you may need to apply a `verticalOffset` of `-StatusBar.currentHeight` for the popover to originate from the correct place. For shifting the popover in other situations, the `offset` prop should be used.
debug | bool | false | Set this to `true` to turn on debug logging to the console. This is useful for figuring out why a Popover isn't showing.
### <a name="from"/>From
The `from` prop can be:
* A React element. If that element has an `onPress` prop, that prop will be be used to open the `Popover`.
* A `ref` object created using `useRef` in function components or `createRef` in class components.
* A function that takes two ordered argument, a `ref` and a `showPopover` callback, and returns a React element. Pass the `ref` argument to `ref` prop of the element that will be the source of the `Popover` (the view that the arrow will point to), and call `showPopover` callback to trigger the popover (or pass to the `onPress` prop or similar of a component). You can also ignore the `showPopover` argument and control visibility using the `isVisible` prop.
* A point: `{ x: number, y: number }`. In React Native coordinate system, (0, 0) is the top left of the screen.
* A rectangle: `{ x: number, y: number, width: number, height: number }`. The popover arrow will be pinned to the edges of the rectangle.
If no `from` is provided, the popover will float in the center of the screen.

@@ -340,8 +342,14 @@

Shows the popover in the space provided, filling the `Popover` component's parent. This looks identical to `rn-modal` if the `Popover` component's parent is a top-level view, but may look weird if the `Popover` is nested inside a few views with their own padding and margins. With careful component hierarchy design, this can allow for nested popovers and popovers in other Modals. Taps to the area outside the popover will trigger the `onRequestClose` callback.
Use this if you want to show nested `Popover`s, or are showing the `Popover` from a view that is already inside a native modal. Unlike with `Tooltip`, taps to the area outside the popover will trigger the `onRequestClose` callback.
#### Tooltip
Shows the `Popover` without taking over the screen, no background is faded in and taps to the area around the popover fall through to those views (as expected). The `onRequestClose` callback will never be called, so the `Popover` will have to be dismissed some other way.
Shows the `Popover` without taking over the screen, no background is faded in and taps to the area around the popover fall are not blocked. The `onRequestClose` callback will never be called, so the `Popover` will have to be dismissed some other way.
#### Tips for using JS Modal and Tooltip
Both JS Modal and Tooltip modes render the `Popover` into the react component tree instead of in a dedicated native modal. Because of this, you must place the `Popover` component at the proper place in the component hiearchy for it to show, potentially in a different place than the `Touchable` that the `Popover` is anchored to.
The key thing to consider is that the `Popover` will only be able to render in the space of its immediate parent. If the parent of the `Popover` is a view with the same dimensions of the `Touchable`, the `Popover` will not be able to show. If you can't see the `Popover` when using these modes, try moving the `Popover` to the root view, or as close to the root view as possible, so that the `Popover`'s immediate parent fills the screen. If you render the `Touchable` inside a `ScrollView`, the `Popover` should be an immediate child of the `ScrollView`.
## <a name="safeArea" />Usage with Safe Area Context

@@ -391,4 +399,22 @@

#### Error when passing a functional component to the `from` prop
When passing a function component to the Popover `from` prop, you may see the following error:
```
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
```
This is because the Popover component uses `ref` to find the position of the `from` component on the screen, so that it can position the Popover correctly. Functional components don't have a `ref` like class components did, and thus causes this error.
As the error suggests, modifying your functional component to use [React.forwardRef](https://reactjs.org/docs/forwarding-refs.html) will fix this error. You should forward the ref to the underlying component that represents the bounds from which you want the Popover to show (usually the topmost component).
## <a name="upgrading" />Upgrading
#### `4.x` to `5.0`
* New Props: `offset` & `popoverShift`
* Breaking: Replaces `arrowStyle` with `arrowSize`. The only known customization of the arrow was the `width`, `height`, and `backgroundColor`. In version `5.0`, `arrowSize` prop allows customization of `width` and `height` (with some tweaks, explained in prop table), and the arrow will inherit the `backgroundColor` from `popoverStyle`. To hide arrow, instead of passing `backgroundColor: 'transparent'`, pass an `arrowSize` of `{ width: 0, height: 0 }`, and then use the `offset` prop to move popover away from source if desired. The shadow props passed to `popoverStyle` will apply to the arrow as well.
* Under-the-hood changes: Refactoring and optimizations may cause slight changes to content handling and placement, review and test your popovers after upgrade to make sure they still look as expected.
* Simplification: Using a shadow no longer requires `popoverStyle` to contain `overflow: visible` ([#15](https://github.com/SteffeyDev/react-native-popover-view/issues/15))
* Deprecation: `Rect` is deprecated and will be removed in the future. Instead of using `new Rect(x, y, width, height)`, just pass in an object of the form `{ x: number, y: number, width: number, height: number }`.
#### `3.x` to `4.0`

@@ -395,0 +421,0 @@

@@ -0,5 +1,48 @@

import { Dimensions, Platform, StyleSheet } from 'react-native';
import { Size } from './Types';
export const MULTIPLE_POPOVER_WARNING = `Popover Warning - Can't Show - Attempted to show a Popover while another one was already showing. You can only show one Popover at a time, and must wait for one to close completely before showing a different one. You can use the onCloseComplete prop to detect when a Popover has finished closing. To show multiple Popovers simultaneously, all but one should have mode={Popover.MODE.JS_MODAL}. Once you change the mode, you can show as many Popovers as you want, but you are responsible for keeping them above other views.`;
export const DEFAULT_ARROW_SIZE = { width: 16, height: 8 };
export const DEFAULT_ARROW_SIZE = new Size(16, 8);
export const DEFAULT_BORDER_RADIUS = 3;
export const POPOVER_MARGIN = 10;
export const DEBUG = false;
export const isIOS = Platform.OS === 'ios';
export const isWeb = Platform.OS === 'web';
/*
* FIX_SHIFT resolves an issue with useNativeDriver, where it would flash the
* popover on and off really quickly, and then animate in normally. Now, because
* of the shift, the flash happens off screen, and then it is shifted on screen
* just before beginning the actual animation.
*/
export const FIX_SHIFT = isWeb
? 0
: Dimensions.get('window').height * 2;
export const styles = StyleSheet.create({
container: {
top: 0,
bottom: 0,
left: 0,
right: 0,
position: 'absolute',
backgroundColor: 'transparent'
},
background: {
top: 0,
bottom: FIX_SHIFT,
left: 0,
right: 0,
position: 'absolute',
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
popoverContent: {
overflow: 'hidden',
position: 'absolute',
backgroundColor: 'white',
borderBottomColor: '#333438',
borderRadius: DEFAULT_BORDER_RADIUS
}
});
import { StyleProp, ViewStyle } from 'react-native';
import { Placement } from './Types';
import { Rect, Size, Point, getArrowSize, getBorderRadius } from './Utility';
import { Rect, Size, Point, Placement, PopoverProps } from './Types';
import { getBorderRadius } from './Utility';
import { POPOVER_MARGIN } from './Constants';

@@ -10,2 +10,3 @@

debug: (line: string, obj?: unknown) => void;
offset?: number;
}

@@ -17,5 +18,6 @@

fromRect: Rect | null;
arrowStyle: StyleProp<ViewStyle>;
arrowSize: Size;
popoverStyle: StyleProp<ViewStyle>;
arrowShift?: number;
popoverShift?: PopoverProps['popoverShift'];
}

@@ -25,3 +27,3 @@

fromRect: Rect;
arrowStyle: StyleProp<ViewStyle>;
arrowSize: Size;
borderRadius: number;

@@ -39,3 +41,3 @@ debug: (line: string, obj?: unknown) => void;

placement: Placement;
forcedContentSize: Size | null;
forcedContentSize: Size;
viewLargerThanDisplayArea: {

@@ -51,3 +53,3 @@ width: boolean,

placement: Placement;
forcedContentSize: Size | null;
forcedContentSize: Size;
viewLargerThanDisplayArea: {

@@ -66,7 +68,6 @@ width: boolean,

static equals(a: Geometry, b: Geometry): boolean {
return Point.equals(a.popoverOrigin, b.popoverOrigin) &&
Point.equals(a.anchorPoint, b.anchorPoint) &&
return a.popoverOrigin.equals(b.popoverOrigin) &&
a.anchorPoint.equals(b.anchorPoint) &&
a.placement === b.placement &&
a.forcedContentSize?.width === b.forcedContentSize?.width &&
a.forcedContentSize?.height === b.forcedContentSize?.height &&
a.forcedContentSize.equals(b.forcedContentSize) &&
a.viewLargerThanDisplayArea?.width === b.viewLargerThanDisplayArea?.width &&

@@ -78,3 +79,11 @@ a.viewLargerThanDisplayArea?.height === b.viewLargerThanDisplayArea?.height;

export function computeGeometry(options: ComputeGeometryProps): Geometry {
const { requestedContentSize, placement, displayArea, debug, popoverStyle, arrowShift } = options;
const {
requestedContentSize,
placement,
displayArea,
debug,
popoverStyle,
arrowShift,
popoverShift
} = options;

@@ -89,10 +98,2 @@ let newGeom = null;

// Check to see if fromRect is outside of displayArea, and adjust if it is
if (fromRect.x > displayArea.x + displayArea.width)
fromRect.x = displayArea.x + displayArea.width;
if (fromRect.y > displayArea.y + displayArea.height)
fromRect.y = displayArea.y + displayArea.height;
if (fromRect.x + fromRect.width < 0) fromRect.x = -1 * fromRect.width;
if (fromRect.y + fromRect.height < 0) fromRect.y = -1 * fromRect.height;
const borderRadius = getBorderRadius(popoverStyle);

@@ -113,3 +114,3 @@

break;
case Placement.CENTER:
case Placement.FLOATING:
newGeom = null;

@@ -124,3 +125,3 @@ break;

/*
* If the popover will be restricted and the view that the popover isshowing
* If the popover will be restricted and the view that the popover is showing
* from is sufficiently large, try to show the popover inside the view

@@ -154,6 +155,6 @@ */

const forcedContentSize = {
width: Math.min(fromRect.width - 20, displayArea.width),
height: Math.min(fromRect.height - 20, displayArea.height)
};
const forcedContentSize = new Size(
Math.min(fromRect.width - 20, displayArea.width),
Math.min(fromRect.height - 20, displayArea.height)
);

@@ -166,3 +167,3 @@ debug('computeGeometry - showing inside anchor');

new Point(fromRect.x + (fromRect.width / 2), fromRect.y + (fromRect.height / 2)),
placement: Placement.CENTER,
placement: Placement.FLOATING,
forcedContentSize,

@@ -176,3 +177,3 @@ viewLargerThanDisplayArea: {

/*
* If we can't fit inside or outside the fromRect, show the popover centered on the screen,
* If we can't fit inside or outside the fromRect, show the popover floating on the screen,
* but only do this if they haven't asked for a specifc placement type

@@ -204,3 +205,3 @@ * and if it will actually help show more content

debug('computeGeometry - showing centered on screen');
debug('computeGeometry - showing floating');
newGeom = new Geometry({

@@ -212,7 +213,4 @@ popoverOrigin: new Point(Math.max(minX, preferedX), Math.max(minY, preferedY)),

),
placement: Placement.CENTER,
forcedContentSize: {
width: displayArea.width,
height: displayArea.height
},
placement: Placement.FLOATING,
forcedContentSize: new Size(displayArea.width, displayArea.height),
viewLargerThanDisplayArea: {

@@ -223,2 +221,16 @@ width: preferedX < minX - 1,

});
// Apply popover shift
if (!newGeom.viewLargerThanDisplayArea.width && popoverShift?.x) {
debug('computeGeometry - applying popoverShift.x', popoverShift.x);
const horizontalMargin = (displayArea.width - requestedContentSize.width) / 2;
newGeom.popoverOrigin.x += popoverShift.x * horizontalMargin;
newGeom.anchorPoint.x = newGeom.popoverOrigin.x + (requestedContentSize.width / 2);
}
if (!newGeom.viewLargerThanDisplayArea.height && popoverShift?.y) {
debug('computeGeometry - applying popoverShift.y', popoverShift.y);
const verticalMargin = (displayArea.height - requestedContentSize.height) / 2;
newGeom.popoverOrigin.y += popoverShift.y * verticalMargin;
newGeom.anchorPoint.y = newGeom.popoverOrigin.y + (requestedContentSize.height / 2);
}
}

@@ -241,4 +253,5 @@

requestedContentSize,
arrowStyle,
borderRadius
arrowSize,
borderRadius,
offset
}: ComputeGeometryDirectionProps): Geometry {

@@ -253,10 +266,12 @@ // Apply a margin on non-arrow sides

const arrowSize = getArrowSize(Placement.TOP, arrowStyle);
if (offset) fromRect.y -= offset;
const minY = displayArea.y;
const maxY = displayArea.y + displayArea.height;
const preferredY = fromRect.y - requestedContentSize.height - arrowSize.height;
const forcedContentSize = {
height: (fromRect.y - arrowSize.height - displayArea.y),
width: displayArea.width
};
const forcedContentSize = new Size(
displayArea.width,
(fromRect.y - arrowSize.height - displayArea.y)
);

@@ -278,3 +293,3 @@ const viewLargerThanDisplayArea = {

Math.min(maxX, Math.max(minX, preferredX)),
Math.max(minY, preferredY)
Math.min(maxY, Math.max(minY, preferredY))
);

@@ -285,3 +300,3 @@

// Make sure the arrow isn't cut off
anchorPoint.x = Math.max(anchorPoint.x, (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.max(anchorPoint.x, popoverOrigin.x + (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.min(

@@ -305,4 +320,5 @@ anchorPoint.x,

requestedContentSize,
arrowStyle,
borderRadius
arrowSize,
borderRadius,
offset
}: ComputeGeometryDirectionProps): Geometry {

@@ -317,10 +333,13 @@ // Apply a margin on non-arrow sides

const arrowSize = getArrowSize(Placement.BOTTOM, arrowStyle);
const preferedY = fromRect.y + fromRect.height + arrowSize.height;
if (offset) fromRect.y += offset;
const forcedContentSize = {
height: displayArea.y + displayArea.height - preferedY,
width: displayArea.width
};
const minY = displayArea.y;
const maxY = displayArea.y + displayArea.height;
const preferedY = fromRect.y + fromRect.height;
const forcedContentSize = new Size(
displayArea.width,
displayArea.y + displayArea.height - preferedY
);
const viewLargerThanDisplayArea = {

@@ -341,3 +360,3 @@ height: preferedY + requestedContentSize.height >= displayArea.y + displayArea.height + 1,

Math.min(maxX, Math.max(minX, preferedX)),
preferedY
Math.min(maxY, Math.max(minY, preferedY))
);

@@ -348,3 +367,3 @@

// Make sure the arrow isn't cut off
anchorPoint.x = Math.max(anchorPoint.x, (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.max(anchorPoint.x, popoverOrigin.x + (arrowSize.width / 2) + borderRadius);
anchorPoint.x = Math.min(

@@ -369,3 +388,4 @@ anchorPoint.x,

borderRadius,
arrowStyle
arrowSize,
offset
}: ComputeGeometryDirectionProps): Geometry {

@@ -380,8 +400,8 @@ // Apply a margin on non-arrow sides

const arrowSize = getArrowSize(Placement.LEFT, arrowStyle);
if (offset) fromRect.x -= offset;
const forcedContentSize = {
height: displayArea.height,
width: fromRect.x - displayArea.x - arrowSize.width
};
const forcedContentSize = new Size(
fromRect.x - displayArea.x - arrowSize.width,
displayArea.height
);

@@ -400,3 +420,5 @@ const viewLargerThanDisplayArea = {

const preferedX = fromRect.x - viewWidth - arrowSize.width;
const preferedX = fromRect.x - viewWidth - arrowSize.height;
const minX = displayArea.x;
const maxX = displayArea.x + displayArea.width;

@@ -408,3 +430,3 @@ const preferedY = fromRect.y + ((fromRect.height - viewHeight) / 2);

const popoverOrigin = new Point(
preferedX,
Math.min(Math.max(minX, preferedX), maxX),
Math.min(Math.max(minY, preferedY), maxY)

@@ -416,3 +438,3 @@ );

// Make sure the arrow isn't cut off
anchorPoint.y = Math.max(anchorPoint.y, (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.max(anchorPoint.y, popoverOrigin.y + (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.min(

@@ -436,4 +458,5 @@ anchorPoint.y,

requestedContentSize,
arrowStyle,
borderRadius
arrowSize,
borderRadius,
offset
}: ComputeGeometryDirectionProps): Geometry {

@@ -448,10 +471,11 @@ // Apply a margin on non-arrow sides

const arrowSize = getArrowSize(Placement.RIGHT, arrowStyle);
if (offset) fromRect.x += offset;
const horizontalSpace =
displayArea.x + displayArea.width - (fromRect.x + fromRect.width) - arrowSize.width;
const forcedContentSize = {
height: displayArea.height,
width: horizontalSpace
};
const forcedContentSize = new Size(
horizontalSpace,
displayArea.height
);

@@ -467,10 +491,12 @@ const viewLargerThanDisplayArea = {

const preferedX = fromRect.x + fromRect.width + arrowSize.width;
const preferedX = fromRect.x + fromRect.width;
const minX = displayArea.x;
const maxX = displayArea.x + displayArea.width;
const preferedY = fromRect.y + ((fromRect.height - viewHeight) / 2);
const minY = displayArea.y + POPOVER_MARGIN;
const maxY = (displayArea.height - viewHeight) + displayArea.y - POPOVER_MARGIN;
const minY = displayArea.y;
const maxY = (displayArea.height - viewHeight) + displayArea.y;
const popoverOrigin = new Point(
preferedX,
Math.min(Math.max(minX, preferedX), maxX),
Math.min(Math.max(minY, preferedY), maxY)

@@ -482,3 +508,3 @@ );

// Make sure the arrow isn't cut off
anchorPoint.y = Math.max(anchorPoint.y, (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.max(anchorPoint.y, popoverOrigin.y + (arrowSize.height / 2) + borderRadius);
anchorPoint.y = Math.min(

@@ -505,3 +531,3 @@ anchorPoint.y,

debug,
arrowStyle
arrowSize
} = options;

@@ -531,3 +557,2 @@

*/
const arrowSize = getArrowSize(Placement.LEFT, arrowStyle);

@@ -534,0 +559,0 @@ // generating list of all possible sides with validity

export { default } from './Popover';
export { Placement as PopoverPlacement, Mode as PopoverMode } from './Types';
export { Rect, Size } from './Utility';
export { Rect, Size } from './Types';

@@ -0,1 +1,3 @@

import { Animated, StyleProp, ViewStyle } from 'react-native';
// eslint-disable-next-line

@@ -8,2 +10,4 @@ export enum Placement {

AUTO = 'auto',
FLOATING = 'floating',
// deprecated
CENTER = 'center'

@@ -25,1 +29,85 @@ }

}
export interface ModalPopoverState {
visible: boolean;
}
export type PopoverProps = {
isVisible?: boolean;
// config
placement?: Placement;
animationConfig?: Partial<Animated.TimingAnimationConfig>;
offset?: number;
verticalOffset?: number;
displayArea?: Rect;
displayAreaInsets?: Insets;
// style
popoverStyle?: StyleProp<ViewStyle>;
popoverShift?: { x?: number, y?: number };
backgroundStyle?: StyleProp<ViewStyle>;
arrowShift?: number;
arrowSize?: Size;
// lifecycle
onOpenStart?: () => void;
onOpenComplete?: () => void;
onRequestClose?: () => void;
onCloseStart?: () => void;
onCloseComplete?: () => void;
onPositionChange?: () => void;
debug?: boolean;
}
export class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
equals(b: Point): boolean {
return Math.round(this.x) === Math.round(b.x) && Math.round(this.y) === Math.round(b.y);
}
}
export class Size {
width: number;
height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
equals(b: Size): boolean {
return Math.round(this.width) === Math.round(b.width) &&
Math.round(this.height) === Math.round(b.height);
}
}
export class Rect {
x: number;
y: number;
width: number;
height: number;
constructor(x: number, y: number, width: number, height: number) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
equals(b: Rect): boolean {
return (Math.round(this.x) === Math.round(b.x) &&
Math.round(this.y) === Math.round(b.y) &&
Math.round(this.width) === Math.round(b.width) &&
Math.round(this.height) === Math.round(b.height));
}
static clone(rect: Rect): Rect {
return new Rect(rect.x, rect.y, rect.width, rect.height);
}
}
import { RefObject, ComponentClass, Component } from 'react';
import { NativeModules, findNodeHandle, StyleProp, ViewStyle, StyleSheet } from 'react-native';
import { Placement } from './Types';
import { Placement, Point, Rect, Size } from './Types';
import { DEFAULT_ARROW_SIZE, DEFAULT_BORDER_RADIUS } from './Constants';
export class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
static equals(a: Point, b: Point): boolean {
return Math.round(a.x) === Math.round(b.x) && Math.round(a.y) === Math.round(b.y);
}
}
export class Size {
width: number;
height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
}
static equals(a: Size, b: Size): boolean {
return Math.round(a.width) === Math.round(b.width) &&
Math.round(a.height) === Math.round(b.height);
}
}
export class Rect {
x: number;
y: number;
width: number;
height: number;
constructor(x: number, y: number, width: number, height: number) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
static equals(a: Rect, b: Rect): boolean {
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));
}
static clone(rect: Rect): Rect {
return new Rect(rect.x, rect.y, rect.width, rect.height);
}
}
// Need any here to match signature of findNodeHandle

@@ -92,3 +41,3 @@ // eslint-disable-next-line

}
} while (Rect.equals(first, second));
} while (first.equals(second));
}

@@ -140,1 +89,16 @@

}
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;
});
}

@@ -18,4 +18,2 @@ {

"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowSyntheticDefaultImports": true,

@@ -22,0 +20,0 @@ "strict": true

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc