Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutliple tooltip enhancements #151

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ The tooltip wraps an element _in place_ in your React Native rendering. When it
| Prop name | Type | Default value | Description |
| ---------------- | ---------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| accessible | bool| true | Set this to `false` if you do not want the root touchable element to be accessible. [See docs on accessible here](https://reactnative.dev/docs/accessibility#accessibility-properties)
| allowBackgroundInteraction | bool| false | By default, the user can dismiss the tooltip by tapping on the background. Set this to true to enable interactions with components behind the tooltip background. This is useful if you want the user to interact with background elements without closing the tooltip. |
| allowChildInteraction | bool| true | By default, the user can touch and interact with the child element. When this prop is false, the user cannot interact with the child element while the tooltip is visible. |
| arrowSize | `Size` | { width: 16, height: 8 } | The dimensions of the arrow on the bubble pointing to the highlighted element |
| backgroundColor | string | 'rgba(0,0,0,0.5)' | Color of the fullscreen background beneath the tooltip. **_Overrides_** the `backgroundStyle` prop |
| childContentSpacing | number | 4 | The distance between the tooltip-rendered child and the arrow pointing to it |
| closeOnBackgroundInteraction | bool | true | When enabled, `onClose` is called when the user taps on the background element |
| closeOnChildInteraction | bool | true | When child interaction is allowed, this prop determines if `onClose` should be called when the user interacts with the child element. Default is true (usually means the tooltip will dismiss once the user touches the element highlighted) |
| closeOnContentInteraction | bool | true | this prop determines if `onClose` should be called when the user interacts with the content element. Default is true (usually means the tooltip will dismiss once the user touches the content element) |
| content | function/Element | `<View />` | This is the view displayed in the tooltip popover bubble |
Expand All @@ -85,6 +87,7 @@ The tooltip wraps an element _in place_ in your React Native rendering. When it
| isVisible | bool | false | When true, tooltip is displayed | |
| onClose | function | null | Callback fired when the user taps the tooltip background overlay |
| placement | string | "top" \| "center" | Where to position the tooltip - options: `top, bottom, left, right, center`. Default is `top` for tooltips rendered with children Default is `center` for tooltips rendered without children. <br><br>NOTE: `center` is only available with a childless placement, and the content will be centered within the bounds defined by the `displayInsets`. |
| preventCloseOnTapCancel | bool | false | Set this to `true` if cancelled taps should keep the tooltip displayed. User's can cancel a tap (like on a button), by drawing their finger off the button and releasing off of the button. Without this set to `true`, the tooltip will close if `closeOnChildInteraction` is true. |
| showChildInTooltip | bool | true | Set this to `false` if you do NOT want to display the child alongside the tooltip when the tooltip is visible |
| supportedOrientations | array | ["portrait", "landscape"] | This prop allows you to control the supported orientations the tooltip modal can be displayed. It correlates directly with [the prop for React Native's Modal component](https://facebook.github.io/react-native/docs/modal#supportedorientations) (has no effect if `useReactNativeModal` is false) |
| topAdjustment | number | 0 | Value which provides additional vertical offest for the child element displayed in a tooltip. Commonly set to: `Platform.OS === 'android' ? -StatusBar.currentHeight : 0` due to an issue with React Native's measure function on Android
Expand Down
9 changes: 9 additions & 0 deletions src/tooltip.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ declare module 'react-native-walkthrough-tooltip' {
}

export interface TooltipProps extends Partial<TooltipStyleProps> {
// When true (default is false), user can interact with background components
allowBackgroundInteraction?: boolean;

// When true (default), user can interact with child element
allowChildInteraction?: boolean;

Expand All @@ -59,6 +62,9 @@ declare module 'react-native-walkthrough-tooltip' {
// Color of the fullscreen background beneath the tooltip. Overrides the backgroundStyle prop
backgroundColor?: string;

// When true (default), onClose prop is called when user touches background element
closeOnBackgroundInteraction?: boolean;

// When true (default), onClose prop is called when user touches child element
closeOnChildInteraction?: boolean;

Expand Down Expand Up @@ -89,6 +95,9 @@ declare module 'react-native-walkthrough-tooltip' {
*/
placement?: 'top' | 'bottom' | 'left' | 'right' | 'center';

// When false (default), user's can cancel taps inside child elements and the tooltip will still close.
preventCloseOnTapCancel?: boolean;

// Determines if the tooltip's children should be shown in the foreground when the tooltip is visible.
showChildInTooltip?: boolean;

Expand Down
45 changes: 39 additions & 6 deletions src/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@ const invertPlacement = placement => {

class Tooltip extends Component {
static defaultProps = {
allowBackgroundInteraction: false,
allowChildInteraction: true,
arrowSize: new Size(16, 8),
backgroundColor: 'rgba(0,0,0,0.5)',
childContentSpacing: 4,
children: null,
closeOnBackgroundInteraction: true,
closeOnChildInteraction: true,
closeOnContentInteraction: true,
content: <View />,
Expand All @@ -69,6 +71,7 @@ class Tooltip extends Component {
);
},
placement: 'center', // falls back to "top" if there ARE children
preventCloseOnTapCancel: false,
showChildInTooltip: true,
supportedOrientations: ['portrait', 'landscape'],
useInteractionManager: false,
Expand All @@ -78,6 +81,7 @@ class Tooltip extends Component {
};

static propTypes = {
allowBackgroundInteraction: PropTypes.bool,
allowChildInteraction: PropTypes.bool,
arrowSize: PropTypes.shape({
height: PropTypes.number,
Expand All @@ -86,6 +90,7 @@ class Tooltip extends Component {
backgroundColor: PropTypes.string,
childContentSpacing: PropTypes.number,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
closeOnBackgroundInteraction: PropTypes.bool,
closeOnChildInteraction: PropTypes.bool,
closeOnContentInteraction: PropTypes.bool,
content: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
Expand All @@ -99,6 +104,7 @@ class Tooltip extends Component {
isVisible: PropTypes.bool,
onClose: PropTypes.func,
placement: PropTypes.oneOf(['top', 'left', 'bottom', 'right', 'center']),
preventCloseOnTapCancel: PropTypes.bool,
showChildInTooltip: PropTypes.bool,
supportedOrientations: PropTypes.arrayOf(PropTypes.string),
useInteractionManager: PropTypes.bool,
Expand Down Expand Up @@ -320,7 +326,7 @@ class Tooltip extends Component {
childContentSpacing,
};

let geom = computeTopGeometry(options);
let geom;

// special case for centered, childless placement tooltip
if (
Expand All @@ -341,7 +347,8 @@ class Tooltip extends Component {
break;
case 'top':
default:
break; // computed just above if-else-block
geom = computeTopGeometry(options);
break;
}
}

Expand All @@ -359,7 +366,16 @@ class Tooltip extends Component {
renderChildInTooltip = () => {
const { height, width, x, y } = this.state.childRect;

const onTouchEnd = () => {
const onTouchEnd = event => {
if (
this.props.preventCloseOnTapCancel &&
(event.nativeEvent.locationY < 0 ||
event.nativeEvent.locationX < 0 ||
event.nativeEvent.locationY > height ||
event.nativeEvent.locationX > width)
) {
return;
}
if (this.props.closeOnChildInteraction) {
this.props.onClose();
}
Expand Down Expand Up @@ -404,19 +420,36 @@ class Tooltip extends Component {

const hasChildren = React.Children.count(this.props.children) > 0;

const onPressBackground = () => {
if (this.props.closeOnBackgroundInteraction) {
this.props.onClose();
}
};

const onPressContent = () => {
if (this.props.closeOnContentInteraction) {
this.props.onClose();
}
};

const pointerEventsMode = this.props.allowBackgroundInteraction
? 'box-none'
: undefined;

return (
<TouchableWithoutFeedback
onPress={this.props.onClose}
disabled={this.props.allowBackgroundInteraction}
onPress={onPressBackground}
accessible={this.props.accessible}
>
<View style={generatedStyles.containerStyle}>
<View style={[generatedStyles.backgroundStyle]}>
<View
style={generatedStyles.containerStyle}
pointerEvents={pointerEventsMode}
>
<View
style={generatedStyles.backgroundStyle}
pointerEvents={pointerEventsMode}
>
<View style={generatedStyles.tooltipStyle}>
{hasChildren ? <View style={generatedStyles.arrowStyle} /> : null}
<View
Expand Down