You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

react-native-walkthrough-tooltip

Package Overview
Dependencies
Maintainers
1
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-native-walkthrough-tooltip - npm Package Compare versions

Comparing version

to
0.6.0

src/tooltip-children.context.js

8

package.json
{
"name": "react-native-walkthrough-tooltip",
"version": "0.6.0-alpha.4",
"version": "0.6.0",
"description": "An inline wrapper for calling out React Native components via tooltip",

@@ -27,4 +27,3 @@ "main": "src/tooltip.js",

"dependencies": {
"prop-types": "^15.6.1",
"react-fast-compare": "^2.0.4"
"prop-types": "^15.6.1"
},

@@ -50,2 +49,5 @@ "devDependencies": {

},
"peerDependencies": {
"@types/react": "^16.8.24"
},
"jest": {

@@ -52,0 +54,0 @@ "preset": "react-native",

@@ -51,3 +51,2 @@ # React Native Walkthrough Tooltip [![npm](https://img.shields.io/npm/v/react-native-walkthrough-tooltip.svg)](https://www.npmjs.com/package/react-native-walkthrough-tooltip) [![npm](https://img.shields.io/npm/dm/react-native-walkthrough-tooltip.svg)](https://www.npmjs.com/package/react-native-walkthrough-tooltip)

| content | function/Element | `<View />` | This is the view displayed in the tooltip popover bubble |
| contentBoundByDisplayArea | bool | true | When true, the tooltip content will respect the displayArea, preventing tooltip from rendering outside of the displayArea (helpful to prevent tooltips rendering partially off screen) |
| displayArea | `Rect` | fullscreen `Rect` with 24px of padding | Screen area where the tooltip may be displayed |

@@ -92,1 +91,20 @@ | isVisible | bool | false | When true, tooltip is displayed |

One possible use case for these functions would be a scenerio where you are highlighting new functionality and want to restrict a user to ONLY do a certain action when they press on an element. While perhaps uncommon, this use case was relevant for another library I am working on, so it may be useful for you. When these props are NOT provided, all touch events on children occur as expected.
### TooltipChildrenConsumer
[React Context](https://reactjs.org/docs/context.html) consumer that can be used to distinguish "real" children rendered inside parent's layout from their copies rendered inside tooltip's modal. The duplicate child rendered in the tooltip modal is wrapped in a Context.Provider which provides object with prop `tooltipDuplicate` set to `true`, so informed decisions may be made, if necessary, based on where the child rendered.
```js
import Tooltip, { TooltipChildrenConsumer } from 'react-native-walkthrough-tooltip';
...
<Tooltip withContext>
<ComponentA />
<ComponentB>
<TooltipChildrenConsumer>
{({ tooltipDuplicate }) => (
// will only assign a ref to the original component
<FlatList {...(!tooltipDuplicate && { ref: this.listRef })} />
)}
</WalkthroughConsumer>
</ComponentB>
</Tooltip>
```

@@ -24,46 +24,23 @@ class Point {

const getBoundsForDisplayArea = displayArea => ({
x: {
min: displayArea.x,
max: displayArea.x + displayArea.width,
},
y: {
min: displayArea.y,
max: displayArea.y + displayArea.height,
},
});
const computeTopGeometry = ({ displayArea, childRect, contentSize, arrowSize }) => {
const computeTopGeometry = ({
displayArea,
childRect,
contentSize,
arrowSize,
}) => {
const tooltipOrigin = new Point(
Math.min(
displayArea.x + displayArea.width - contentSize.width,
Math.max(displayArea.x, childRect.x + (childRect.width - contentSize.width) / 2),
Math.max(
displayArea.x,
childRect.x + (childRect.width - contentSize.width) / 2,
),
),
childRect.y - contentSize.height - arrowSize.height,
);
const anchorPoint = new Point(childRect.x + childRect.width / 2.0, childRect.y);
const anchorPoint = new Point(
childRect.x + childRect.width / 2.0,
childRect.y,
);
// compute bound content size
const boundTooltipOrigin = new Point(tooltipOrigin.x, tooltipOrigin.y);
const boundContentSize = new Size(contentSize.width, contentSize.height);
const bounds = getBoundsForDisplayArea(displayArea);
const topPlacementBottomBound = anchorPoint.y - arrowSize.height;
if (tooltipOrigin.x < bounds.x.min) {
boundTooltipOrigin.x = bounds.x.min;
}
if (tooltipOrigin.y < bounds.y.min) {
boundTooltipOrigin.y = bounds.y.min;
}
if (boundTooltipOrigin.x + contentSize.width > bounds.x.max) {
boundContentSize.width = bounds.x.max - boundTooltipOrigin.x;
}
if (boundTooltipOrigin.y + contentSize.height > topPlacementBottomBound) {
boundContentSize.height = topPlacementBottomBound - boundTooltipOrigin.y;
}
return {

@@ -73,12 +50,18 @@ tooltipOrigin,

placement: 'top',
boundContentSize,
boundTooltipOrigin,
};
};
const computeBottomGeometry = ({ displayArea, childRect, contentSize, arrowSize }) => {
const computeBottomGeometry = ({
displayArea,
childRect,
contentSize,
arrowSize,
}) => {
const tooltipOrigin = new Point(
Math.min(
displayArea.x + displayArea.width - contentSize.width,
Math.max(displayArea.x, childRect.x + (childRect.width - contentSize.width) / 2),
Math.max(
displayArea.x,
childRect.x + (childRect.width - contentSize.width) / 2,
),
),

@@ -92,19 +75,2 @@ childRect.y + childRect.height + arrowSize.height,

// compute bound content size
const boundTooltipOrigin = new Point(tooltipOrigin.x, tooltipOrigin.y);
const boundContentSize = new Size(contentSize.width, contentSize.height);
const bounds = getBoundsForDisplayArea(displayArea);
if (tooltipOrigin.x < bounds.x.min) {
boundTooltipOrigin.x = bounds.x.min;
}
if (boundTooltipOrigin.x + contentSize.width > bounds.x.max) {
boundContentSize.width = bounds.x.max - boundTooltipOrigin.x;
}
if (boundTooltipOrigin.y + contentSize.height > bounds.y.max) {
boundContentSize.height = bounds.y.max - boundTooltipOrigin.y;
}
return {

@@ -114,8 +80,11 @@ tooltipOrigin,

placement: 'bottom',
boundContentSize,
boundTooltipOrigin,
};
};
const computeLeftGeometry = ({ displayArea, childRect, contentSize, arrowSize }) => {
const computeLeftGeometry = ({
displayArea,
childRect,
contentSize,
arrowSize,
}) => {
const tooltipOrigin = new Point(

@@ -125,30 +94,13 @@ childRect.x - contentSize.width - arrowSize.width,

displayArea.y + displayArea.height - contentSize.height,
Math.max(displayArea.y, childRect.y + (childRect.height - contentSize.height) / 2),
Math.max(
displayArea.y,
childRect.y + (childRect.height - contentSize.height) / 2,
),
),
);
const anchorPoint = new Point(childRect.x, childRect.y + childRect.height / 2.0);
const anchorPoint = new Point(
childRect.x,
childRect.y + childRect.height / 2.0,
);
// compute bound content size
const boundTooltipOrigin = new Point(tooltipOrigin.x, tooltipOrigin.y);
const boundContentSize = new Size(contentSize.width, contentSize.height);
const bounds = getBoundsForDisplayArea(displayArea);
const leftPlacementRightBound = anchorPoint.x - arrowSize.width;
if (tooltipOrigin.x < bounds.x.min) {
boundTooltipOrigin.x = bounds.x.min;
}
if (tooltipOrigin.y < bounds.y.min) {
boundTooltipOrigin.y = bounds.y.min;
}
if (boundTooltipOrigin.x + contentSize.width > leftPlacementRightBound) {
boundContentSize.width = leftPlacementRightBound - boundTooltipOrigin.x;
}
if (boundTooltipOrigin.y + contentSize.height > bounds.y.max) {
boundContentSize.height = bounds.y.max - boundTooltipOrigin.y;
}
return {

@@ -158,8 +110,11 @@ tooltipOrigin,

placement: 'left',
boundContentSize,
boundTooltipOrigin,
};
};
const computeRightGeometry = ({ displayArea, childRect, contentSize, arrowSize }) => {
const computeRightGeometry = ({
displayArea,
childRect,
contentSize,
arrowSize,
}) => {
const tooltipOrigin = new Point(

@@ -169,3 +124,6 @@ childRect.x + childRect.width + arrowSize.width,

displayArea.y + displayArea.height - contentSize.height,
Math.max(displayArea.y, childRect.y + (childRect.height - contentSize.height) / 2),
Math.max(
displayArea.y,
childRect.y + (childRect.height - contentSize.height) / 2,
),
),

@@ -178,25 +136,2 @@ );

// compute bound content size
const boundTooltipOrigin = new Point(tooltipOrigin.x, tooltipOrigin.y);
const boundContentSize = new Size(contentSize.width, contentSize.height);
const bounds = getBoundsForDisplayArea(displayArea);
const rightPlacementLeftBound = anchorPoint.x + arrowSize.width;
if (tooltipOrigin.x < rightPlacementLeftBound) {
boundTooltipOrigin.x = rightPlacementLeftBound;
}
if (tooltipOrigin.y < bounds.y.min) {
boundTooltipOrigin.y = bounds.y.min;
}
if (boundTooltipOrigin.x + contentSize.width > bounds.x.max) {
boundContentSize.width = bounds.x.max - boundTooltipOrigin.x;
}
if (boundTooltipOrigin.y + contentSize.height > bounds.y.max) {
boundContentSize.height = bounds.y.max - boundTooltipOrigin.y;
}
return {

@@ -206,4 +141,2 @@ tooltipOrigin,

placement: 'right',
boundContentSize,
boundTooltipOrigin,
};

@@ -210,0 +143,0 @@ };

@@ -9,3 +9,2 @@ import React, { Component } from 'react';

Modal,
Platform,
StyleSheet,

@@ -15,3 +14,2 @@ TouchableWithoutFeedback,

} from 'react-native';
import rfcIsEqual from 'react-fast-compare';
import {

@@ -27,2 +25,3 @@ Point,

import styles from './styles';
import TooltipChildrenContext from './tooltip-children.context';

@@ -34,8 +33,2 @@ const SCREEN_HEIGHT = Dimensions.get('window').height;

const DEFAULT_PADDING = 24;
const DEFAULT_DISPLAY_AREA = new Rect(
DEFAULT_PADDING,
DEFAULT_PADDING,
SCREEN_WIDTH - DEFAULT_PADDING * 2,
SCREEN_HEIGHT - DEFAULT_PADDING * 2,
);

@@ -57,2 +50,5 @@ const invertPlacement = (placement) => {

const TooltipChildrenProvider = TooltipChildrenContext.Provider;
export const TooltipChildrenConsumer = TooltipChildrenContext.Consumer;
class Tooltip extends Component {

@@ -66,4 +62,8 @@ static defaultProps = {

content: <View />,
contentBoundByDisplayArea: true,
displayArea: DEFAULT_DISPLAY_AREA,
displayArea: new Rect(
DEFAULT_PADDING,
DEFAULT_PADDING,
SCREEN_WIDTH - DEFAULT_PADDING * 2,
SCREEN_HEIGHT - DEFAULT_PADDING * 2,
),
isVisible: false,

@@ -74,48 +74,14 @@ onChildLongPress: null,

placement: 'auto',
rotationDeg: 0,
useInteractionManager: false,
};
static propTypes = {
animated: PropTypes.bool,
arrowSize: PropTypes.shape({
height: PropTypes.number,
width: PropTypes.number,
}),
backgroundColor: PropTypes.string,
childlessPlacementPadding: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]),
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
contentBoundByDisplayArea: PropTypes.bool,
displayArea: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number,
height: PropTypes.number,
width: PropTypes.number,
}),
isVisible: PropTypes.bool,
onChildLongPress: PropTypes.func,
onChildPress: PropTypes.func,
onClose: PropTypes.func,
placement: PropTypes.oneOf(['top', 'left', 'bottom', 'right', 'auto']),
rotationDeg: PropTypes.oneOf([0, 90, 180, 270]),
useInteractionManager: PropTypes.bool,
};
constructor(props) {
super(props);
const { isVisible, useInteractionManager } = props;
const { isVisible } = props;
this.isMeasuringChild = false;
this.childWrapper = React.createRef();
this.state = {
// no need to wait for interactions if not visible initially
waitingForInteractions: isVisible && useInteractionManager,
waitingForInteractions: isVisible,
contentSize: new Size(0, 0),
boundContentSize: new Size(0, 0),
anchorPoint: new Point(0, 0),

@@ -126,5 +92,3 @@ tooltipOrigin: new Point(0, 0),

// behave like placement "bottom", i.e. display below the top of the screen
placement: !props.children
? invertPlacement(props.placement)
: props.placement,
placement: !props.children ? invertPlacement(props.placement) : props.placement,
readyToComputeGeom: false,

@@ -143,32 +107,33 @@ waitingToComputeGeom: false,

if (this.state.waitingForInteractions) {
this.measureChildRect();
InteractionManager.runAfterInteractions(() => {
this.measureChildRect();
this.setState({ waitingForInteractions: false });
});
}
}
componentDidUpdate(prevProps) {
const { content, isVisible, placement, rotationDeg } = this.props;
componentWillReceiveProps(nextProps) {
const willBeVisible = nextProps.isVisible;
const nextContent = nextProps.content;
const { isVisible, content } = this.props;
const contentChanged = !rfcIsEqual(prevProps.content, content);
const placementChanged = prevProps.placement !== placement;
const rotationChanged = prevProps.rotationDeg !== rotationDeg;
const becameVisible = isVisible && !prevProps.isVisible;
const becameHidden = !isVisible && prevProps.isVisiblel;
if (becameHidden) {
this._startAnimation({ show: false });
} else if (
contentChanged ||
placementChanged ||
rotationChanged ||
becameVisible
) {
setTimeout(() => {
if (nextContent !== content && willBeVisible) {
// The location of the child element may have changed based on
// transition animations in the corresponding view, so remeasure
InteractionManager.runAfterInteractions(() => {
this.measureChildRect();
});
if (becameVisible) {
// TODO: Move setState out of didUpdate
} else if (willBeVisible !== isVisible) {
if (willBeVisible) {
// We want to start the show animation only when contentSize is known
// so that we can have some logic depending on the geometry
this.setState({ contentSize: new Size(0, 0) });
// The location of the child element may have changed based on
// transition animations in the corresponding view, so remeasure
InteractionManager.runAfterInteractions(() => {
this.measureChildRect();
});
} else {
this._startAnimation({ show: false });
}

@@ -178,11 +143,8 @@ }

static getDerivedStateFromProps(nextProps, prevState) {
// set measurements finished flag to false when tooltip closes
if (prevState.measurementsFinished && !nextProps.isVisible) {
return {
measurementsFinished: false,
};
componentDidUpdate() {
// We always want the measurements finished flag to be false
// after the tooltip is closed
if (this.state.measurementsFinished && !this.props.isVisible) {
this.setState({ measurementsFinished: false });
}
return null;
}

@@ -249,86 +211,2 @@

getPlacementForCurrentRotation = () => {
const { placement: basePlacement, rotationDeg } = this.props;
switch (basePlacement) {
case 'top':
switch (rotationDeg) {
case 90:
return 'right';
case 180:
return 'bottom';
case 270:
return 'left';
case 0:
default:
return 'top';
}
case 'left':
switch (rotationDeg) {
case 90:
return 'top';
case 180:
return 'right';
case 270:
return 'bottom';
case 0:
default:
return 'left';
}
case 'bottom':
switch (rotationDeg) {
case 90:
return 'left';
case 180:
return 'top';
case 270:
return 'right';
case 0:
default:
return 'bottom';
}
case 'right':
switch (rotationDeg) {
case 90:
return 'bottom';
case 180:
return 'left';
case 270:
return 'top';
case 0:
default:
return 'right';
}
default:
return basePlacement;
}
};
getTranslationForCurrentRotation = () => {
const { placement: basePlacement, rotationDeg } = this.props;
const {
width: contentWidth,
height: contentHeight,
} = this.state.contentSize;
const offset = Math.abs(contentWidth - contentHeight) / 2;
if (rotationDeg % 180 !== 0) {
switch (basePlacement) {
case 'top':
return { y: offset };
case 'left':
return { x: -offset };
case 'bottom':
return { y: -offset };
case 'right':
return { x: offset };
default:
break;
}
}
return {};
};
getTooltipPlacementStyles = () => {

@@ -374,8 +252,15 @@ const { height } = this.props.arrowSize;

);
return new Point(
anchorPoint.x - tooltipCenter.x,
anchorPoint.y - tooltipCenter.y,
);
return new Point(anchorPoint.x - tooltipCenter.x, anchorPoint.y - tooltipCenter.y);
};
waitAndMeasureChildRect = (clearWaitingForInteractions) => {
setTimeout(() => {
this.measureChildRect();
if (clearWaitingForInteractions) {
this.setState({ waitingForInteractions: false });
}
}, 1000);
};
measureContent = (e) => {

@@ -393,6 +278,2 @@ const { width, height } = e.nativeEvent.layout;

}
if (!this.props.children) {
this.mockChildRect();
}
};

@@ -407,112 +288,73 @@

}
this.setState({ measurementsFinished: true });
};
measureChildRect = () => {
const doMeasurement = () => {
if (!this.isMeasuringChild) {
this.isMeasuringChild = true;
if (
this.childWrapper.current &&
typeof this.childWrapper.current.measureInWindow === 'function'
) {
this.childWrapper.current.measureInWindow((x, y, width, height) => {
this.setState(
{
childRect: new Rect(x, y, width, height),
readyToComputeGeom: true,
},
() => this.finishMeasurements(),
);
});
} else {
// mock the placement of a child to compute geom
let rectForChildlessPlacement = { ...this.state.childRect };
let placementPadding = DEFAULT_PADDING;
if (
this.childWrapper.current &&
typeof this.childWrapper.current.measureInWindow === 'function'
) {
this.childWrapper.current.measureInWindow((x, y, width, height) => {
this.setState({
childRect: new Rect(x, y, width, height),
readyToComputeGeom: true,
waitingForInteractions: false,
placement: this.getPlacementForCurrentRotation(),
},
() => {
this.isMeasuringChild = false;
this.finishMeasurements();
});
});
const { childlessPlacementPadding, placement } = this.props;
// handle percentages
if (typeof childlessPlacementPadding === 'string') {
const isPercentage =
childlessPlacementPadding.substring(childlessPlacementPadding.length - 1) === '%';
const paddingValue = parseFloat(childlessPlacementPadding, 10);
const verticalPlacement = placement === 'top' || placement === 'bottom';
if (isPercentage) {
placementPadding =
(paddingValue / 100.0) * (verticalPlacement ? SCREEN_HEIGHT : SCREEN_WIDTH);
} else {
this.mockChildRect();
placementPadding = paddingValue;
}
} else {
placementPadding = childlessPlacementPadding;
}
};
if (this.props.useInteractionManager) {
InteractionManager.runAfterInteractions(() => {
doMeasurement();
});
} else {
doMeasurement();
}
};
if (Number.isNaN(placementPadding)) {
throw new Error('[Tooltip] Invalid value passed to childlessPlacementPadding');
}
mockChildRect = () => {
// mock the placement of a child to compute geom
let rectForChildlessPlacement = { ...this.state.childRect };
let placementPadding = DEFAULT_PADDING;
const CENTER_X = SCREEN_WIDTH / 2;
const CENTER_Y = SCREEN_HEIGHT / 2;
const { childlessPlacementPadding, placement } = this.props;
// handle percentages
if (typeof childlessPlacementPadding === 'string') {
const isPercentage =
childlessPlacementPadding.substring(
childlessPlacementPadding.length - 1,
) === '%';
const paddingValue = parseFloat(childlessPlacementPadding, 10);
const verticalPlacement = placement === 'top' || placement === 'bottom';
if (isPercentage) {
placementPadding =
(paddingValue / 100.0) *
(verticalPlacement ? SCREEN_HEIGHT : SCREEN_WIDTH);
} else {
placementPadding = paddingValue;
switch (placement) {
case 'bottom':
rectForChildlessPlacement = new Rect(CENTER_X, SCREEN_HEIGHT - placementPadding, 0, 0);
break;
case 'left':
rectForChildlessPlacement = new Rect(placementPadding, CENTER_Y, 0, 0);
break;
case 'right':
rectForChildlessPlacement = new Rect(SCREEN_WIDTH - placementPadding, CENTER_Y, 0, 0);
break;
default:
case 'top':
rectForChildlessPlacement = new Rect(CENTER_X, placementPadding, 0, 0);
break;
}
} else {
placementPadding = childlessPlacementPadding;
}
if (Number.isNaN(placementPadding)) {
throw new Error(
'[Tooltip] Invalid value passed to childlessPlacementPadding',
this.setState(
{
childRect: rectForChildlessPlacement,
readyToComputeGeom: true,
},
() => this.finishMeasurements(),
);
}
const CENTER_X = SCREEN_WIDTH / 2;
const CENTER_Y = SCREEN_HEIGHT / 2;
switch (placement) {
case 'bottom':
rectForChildlessPlacement = new Rect(
CENTER_X,
SCREEN_HEIGHT - placementPadding,
0,
0,
);
break;
case 'left':
rectForChildlessPlacement = new Rect(placementPadding, CENTER_Y, 0, 0);
break;
case 'right':
rectForChildlessPlacement = new Rect(
SCREEN_WIDTH - placementPadding,
CENTER_Y,
0,
0,
);
break;
default:
case 'top':
rectForChildlessPlacement = new Rect(CENTER_X, placementPadding, 0, 0);
break;
}
this.setState(
{
childRect: rectForChildlessPlacement,
readyToComputeGeom: true,
},
() => {
this.isMeasuringChild = false;
this.finishMeasurements();
},
);
};

@@ -522,10 +364,3 @@

const geom = this.computeGeometry({ contentSize });
const {
tooltipOrigin,
anchorPoint,
placement,
boundContentSize,
boundTooltipOrigin,
} = geom;
const { contentBoundByDisplayArea } = this.props;
const { tooltipOrigin, anchorPoint, placement } = geom;

@@ -535,6 +370,3 @@ this.setState(

contentSize,
boundContentSize,
tooltipOrigin: contentBoundByDisplayArea
? boundTooltipOrigin
: tooltipOrigin,
tooltipOrigin,
anchorPoint,

@@ -544,3 +376,2 @@ placement,

waitingToComputeGeom: false,
measurementsFinished: true,
},

@@ -555,19 +386,8 @@ () => {

const geom = this.computeGeometry({ contentSize });
const {
tooltipOrigin,
anchorPoint,
placement,
boundContentSize,
boundTooltipOrigin,
} = geom;
const { contentBoundByDisplayArea } = this.props;
const { tooltipOrigin, anchorPoint, placement } = geom;
this.setState({
boundContentSize,
tooltipOrigin: contentBoundByDisplayArea
? boundTooltipOrigin
: tooltipOrigin,
tooltipOrigin,
anchorPoint,
placement,
measurementsFinished: true,
});

@@ -614,7 +434,5 @@ };

tooltipOrigin.x >= displayArea.x &&
tooltipOrigin.x <=
displayArea.x + displayArea.width - contentSize.width &&
tooltipOrigin.x <= displayArea.x + displayArea.width - contentSize.width &&
tooltipOrigin.y >= displayArea.y &&
tooltipOrigin.y <=
displayArea.y + displayArea.height - contentSize.height
tooltipOrigin.y <= displayArea.y + displayArea.height - contentSize.height
) {

@@ -688,4 +506,2 @@ break;

_getExtendedStyles = () => {
const { animated, rotationDeg } = this.props;
const background = [];

@@ -696,3 +512,3 @@ const tooltip = [];

const animatedStyles = animated ? this._getDefaultAnimatedStyles() : null;
const animatedStyles = this.props.animated ? this._getDefaultAnimatedStyles() : null;

@@ -708,29 +524,2 @@ [animatedStyles, this.props].forEach((source) => {

if (rotationDeg !== 0) {
const translation = this.getTranslationForCurrentRotation();
const transformArray = []; // StyleSheet.flatten(content).transform;
transformArray.push({
rotate: `${rotationDeg}deg`,
});
if (translation.x) {
transformArray.push({
translateX: translation.x,
});
}
if (translation.y) {
transformArray.push({
translateY: translation.y,
});
}
if (Platform.OS === 'android') {
tooltip.push({ transform: transformArray });
} else {
content.push({ transform: transformArray });
}
}
return {

@@ -748,4 +537,3 @@ background,

const wrapInTouchable =
typeof onChildPress === 'function' ||
typeof onChildLongPress === 'function';
typeof onChildPress === 'function' || typeof onChildLongPress === 'function';

@@ -765,3 +553,5 @@ const childElement = (

>
{children}
<TooltipChildrenProvider value={{ tooltipDuplicate: true }}>
{children}
</TooltipChildrenProvider>
</View>

@@ -772,6 +562,3 @@ );

return (
<TouchableWithoutFeedback
onPress={onChildPress}
onLongPress={onChildLongPress}
>
<TouchableWithoutFeedback onPress={onChildPress} onLongPress={onChildLongPress}>
{childElement}

@@ -786,46 +573,16 @@ </TouchableWithoutFeedback>

render() {
const {
measurementsFinished,
placement,
waitingForInteractions,
contentSize,
boundContentSize,
} = this.state;
const {
backgroundColor,
children,
content,
isVisible,
onClose,
contentBoundByDisplayArea,
rotationDeg,
} = this.props;
const { measurementsFinished, placement, waitingForInteractions } = this.state;
const { backgroundColor, children, content, isVisible, onClose } = this.props;
const sizeAvailable = contentBoundByDisplayArea
? boundContentSize.width
: contentSize.width;
const extendedStyles = this._getExtendedStyles();
const contentStyle = [
styles.content,
contentBoundByDisplayArea && boundContentSize.width
? { ...boundContentSize }
: {},
...extendedStyles.content,
];
const contentStyle = [styles.content, ...extendedStyles.content];
const arrowColor = StyleSheet.flatten(contentStyle).backgroundColor;
const arrowColorStyle = this.getArrowColorStyle(arrowColor);
const arrowDynamicStyle = this.getArrowDynamicStyle();
const contentSizeAvailable = this.state.contentSize.width;
const tooltipPlacementStyles = this.getTooltipPlacementStyles();
// Special case, force the arrow rotation even if it was overriden
let arrowStyle = [
styles.arrow,
arrowDynamicStyle,
arrowColorStyle,
...extendedStyles.arrow,
];
const arrowTransform = (
StyleSheet.flatten(arrowStyle).transform || []
).slice(0);
let arrowStyle = [styles.arrow, arrowDynamicStyle, arrowColorStyle, ...extendedStyles.arrow];
const arrowTransform = (StyleSheet.flatten(arrowStyle).transform || []).slice(0);
arrowTransform.unshift({ rotate: this.getArrowRotation(placement) });

@@ -836,14 +593,6 @@ arrowStyle = [...arrowStyle, { transform: arrowTransform }];

// TODO: handle rotation better on android
const tempHideArrowAndChild =
rotationDeg !== 0 && Platform.OS === 'android';
return (
<View>
{/* This renders the fullscreen tooltip */}
<Modal
transparent
visible={isVisible && !waitingForInteractions}
onRequestClose={onClose}
>
<Modal transparent visible={isVisible && !waitingForInteractions} onRequestClose={onClose}>
<TouchableWithoutFeedback onPress={onClose}>

@@ -853,34 +602,17 @@ <View

styles.container,
sizeAvailable &&
measurementsFinished &&
styles.containerVisible,
contentSizeAvailable && measurementsFinished && styles.containerVisible,
]}
>
<Animated.View
style={[
styles.background,
...extendedStyles.background,
{ backgroundColor },
]}
style={[styles.background, ...extendedStyles.background, { backgroundColor }]}
/>
<Animated.View
style={[
styles.tooltip,
...extendedStyles.tooltip,
tooltipPlacementStyles,
]}
style={[styles.tooltip, ...extendedStyles.tooltip, tooltipPlacementStyles]}
>
{noChildren || tempHideArrowAndChild ? null : (
<Animated.View style={arrowStyle} />
)}
<Animated.View
onLayout={this.measureContent}
style={contentStyle}
>
{noChildren ? null : <Animated.View style={arrowStyle} />}
<Animated.View onLayout={this.measureContent} style={contentStyle}>
{content}
</Animated.View>
</Animated.View>
{noChildren || tempHideArrowAndChild
? null
: this.renderChildInTooltip()}
{noChildren ? null : this.renderChildInTooltip()}
</View>

@@ -901,2 +633,25 @@ </TouchableWithoutFeedback>

Tooltip.propTypes = {
animated: PropTypes.bool,
arrowSize: PropTypes.shape({
height: PropTypes.number,
width: PropTypes.number,
}),
backgroundColor: PropTypes.string,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
displayArea: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number,
height: PropTypes.number,
width: PropTypes.number,
}),
isVisible: PropTypes.bool,
onChildLongPress: PropTypes.func,
onChildPress: PropTypes.func,
onClose: PropTypes.func,
placement: PropTypes.string,
childlessPlacementPadding: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};
export default Tooltip;
SocketSocket SOC 2 Logo

Product

About

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.

  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc

U.S. Patent No. 12,346,443 & 12,314,394. Other pending.