Skip to content

Commit 8d452cd

Browse files
committed
feat: adding the Tooltip component
1 parent da17702 commit 8d452cd

27 files changed

+10228
-6
lines changed

package.json

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "react-native-tooltiplize",
33
"version": "0.1.0",
4-
"description": "test",
4+
"description": "react native tooltip like never before, using leading packages like `react-native-reanimated` v2, `framer-motion`, and `@gorhom/portal`",
55
"main": "lib/commonjs/index",
66
"module": "lib/module/index",
77
"types": "lib/typescript/index.d.ts",
@@ -38,7 +38,16 @@
3838
"keywords": [
3939
"react-native",
4040
"ios",
41-
"android"
41+
"android",
42+
"web",
43+
"tooltip",
44+
"tooltiplize",
45+
"react-native-tooltiplize",
46+
"react-native-tooltip",
47+
"react-native-tooltip-lib",
48+
"popover",
49+
"react-native-popover",
50+
"react-native-popover-view"
4251
],
4352
"repository": "https://github.com/Stringsaeed/react-native-tooltiplize",
4453
"author": "Muhammed Saeed <[email protected]> (https://github.com/Stringsaeed)",
@@ -53,6 +62,7 @@
5362
"devDependencies": {
5463
"@arkweid/lefthook": "^0.7.7",
5564
"@commitlint/config-conventional": "^17.0.2",
65+
"@gorhom/portal": "^1.0.14",
5666
"@react-native-community/eslint-config": "^3.0.2",
5767
"@release-it/conventional-changelog": "^5.0.0",
5868
"@types/jest": "^28.1.2",
@@ -68,15 +78,18 @@
6878
"react": "18.0.0",
6979
"react-native": "0.69.6",
7080
"react-native-builder-bob": "^0.20.1",
81+
"react-native-reanimated": "^2.12.0",
7182
"release-it": "^15.0.0",
7283
"typescript": "^4.5.2"
7384
},
7485
"resolutions": {
7586
"@types/react": "17.0.21"
7687
},
7788
"peerDependencies": {
89+
"@gorhom/portal": "^1",
7890
"react": "*",
79-
"react-native": "*"
91+
"react-native": "*",
92+
"react-native-reanimated": "^2"
8093
},
8194
"jest": {
8295
"preset": "react-native",
@@ -150,5 +163,8 @@
150163
}
151164
]
152165
]
166+
},
167+
"dependencies": {
168+
"framer-motion": "^7.6.4"
153169
}
154170
}

src/Tooltip.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { cloneElement, Fragment } from 'react';
2+
import { Portal } from '@gorhom/portal';
3+
import { AnimatePresence } from 'framer-motion';
4+
import { StyleSheet, View } from 'react-native';
5+
6+
import { useLayout } from './hooks';
7+
8+
import { Wrapper } from './components';
9+
import type { TooltipProps } from './types';
10+
11+
const Tooltip: React.FC<TooltipProps> = (props) => {
12+
const { children, isVisible, renderContent, childrenStyle, ...restProps } =
13+
props;
14+
const { onLayout, ...layout } = useLayout();
15+
16+
return (
17+
<Fragment>
18+
<View style={childrenStyle} onLayout={onLayout}>
19+
{cloneElement(children as React.ReactElement)}
20+
</View>
21+
<Portal>
22+
<AnimatePresence>
23+
{isVisible && (
24+
<Fragment>
25+
<Wrapper {...restProps} childrenLayout={layout}>
26+
{renderContent()}
27+
</Wrapper>
28+
{restProps?.withOverlay && (
29+
<View
30+
style={[
31+
styles.absolute,
32+
childrenStyle,
33+
{ top: layout.y, left: layout.x },
34+
]}
35+
>
36+
{cloneElement(children as React.ReactElement)}
37+
</View>
38+
)}
39+
</Fragment>
40+
)}
41+
</AnimatePresence>
42+
</Portal>
43+
</Fragment>
44+
);
45+
};
46+
47+
export default Tooltip;
48+
49+
const styles = StyleSheet.create({
50+
absolute: {
51+
position: 'absolute',
52+
},
53+
});

src/components/Overlay/Overlay.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import Animated, { useAnimatedStyle } from 'react-native-reanimated';
3+
import { StyleSheet, TouchableWithoutFeedback } from 'react-native';
4+
5+
import type { OverlayProps } from '../../types';
6+
7+
const Overlay: React.FC<OverlayProps> = ({
8+
children,
9+
animatedPresence,
10+
onDismiss,
11+
overlayStyle,
12+
}) => {
13+
const stylez = useAnimatedStyle(
14+
() => ({
15+
opacity: animatedPresence?.value,
16+
}),
17+
[animatedPresence]
18+
);
19+
20+
return (
21+
<TouchableWithoutFeedback onPress={onDismiss}>
22+
<Animated.View style={[styles.overlay, overlayStyle, stylez]}>
23+
{children}
24+
</Animated.View>
25+
</TouchableWithoutFeedback>
26+
);
27+
};
28+
29+
export default Overlay;
30+
31+
const styles = StyleSheet.create({
32+
overlay: {
33+
...StyleSheet.absoluteFillObject,
34+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
35+
},
36+
});

src/components/Overlay/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './Overlay';

src/components/Pointer/Pointer.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import React, { useMemo } from 'react';
2+
import { View } from 'react-native';
3+
4+
import { getPointerStyles } from '../../utils';
5+
import type { PointerProps } from '../../types';
6+
7+
import styles from './styles';
8+
9+
const PointerComponent: React.FC<PointerProps> = (props) => {
10+
const borderStyles = useMemo(() => getPointerStyles(props), [props]);
11+
12+
return <View style={[styles.pointer, borderStyles]} />;
13+
};
14+
15+
const Pointer = React.memo<PointerProps>(PointerComponent);
16+
export default Pointer;

src/components/Pointer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './Pointer';

src/components/Pointer/styles.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { StyleSheet } from 'react-native';
2+
3+
const pointerStyles = StyleSheet.create({
4+
pointer: {
5+
width: 0,
6+
height: 0,
7+
backgroundColor: 'transparent',
8+
borderStyle: 'solid',
9+
},
10+
});
11+
12+
export default pointerStyles;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React, { useMemo } from 'react';
2+
import { StyleSheet, View, ViewStyle } from 'react-native';
3+
import Animated, { useAnimatedStyle } from 'react-native-reanimated';
4+
5+
import { useLayout } from '../../hooks';
6+
import type { TooltipWrapperProps } from '../../types';
7+
import { getPointerWrapperStyle, getPositionStyles } from '../../utils';
8+
9+
import Pointer from '../Pointer';
10+
11+
import styles from './styles';
12+
13+
const TooltipWrapperComponent: React.FC<TooltipWrapperProps> = (props) => {
14+
const {
15+
children,
16+
position = 'bottom',
17+
animatedPresence,
18+
...pointerProps
19+
} = props;
20+
21+
const { onLayout, ...layout } = useLayout();
22+
23+
const stylez = useAnimatedStyle(
24+
() => ({ opacity: animatedPresence?.value }),
25+
[animatedPresence]
26+
);
27+
28+
const positionStyles = useMemo(() => {
29+
const { offset, childrenLayout, pointerStyle: pointerStyles } = props;
30+
31+
return getPositionStyles({
32+
offset,
33+
childrenLayout,
34+
pointerStyle: pointerStyles,
35+
position,
36+
layout,
37+
});
38+
}, [layout, position, props]);
39+
40+
const pointerStyles = useMemo(() => {
41+
return StyleSheet.flatten<ViewStyle>([
42+
styles.pointer,
43+
getPointerWrapperStyle({ position, ...pointerProps }, layout),
44+
]);
45+
}, [position, pointerProps, layout]);
46+
47+
return (
48+
<Animated.View
49+
onLayout={onLayout}
50+
style={[styles.tooltipWrapper, positionStyles, stylez]}
51+
>
52+
{children}
53+
<View style={pointerStyles}>
54+
<Pointer {...pointerProps} position={position} />
55+
</View>
56+
</Animated.View>
57+
);
58+
};
59+
60+
const TooltipWrapper = React.memo<TooltipWrapperProps>(TooltipWrapperComponent);
61+
export default TooltipWrapper;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './TooltipWrapper';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { StyleSheet } from 'react-native';
2+
3+
const tooltipWrapperStyles = StyleSheet.create({
4+
tooltipWrapper: {
5+
position: 'absolute',
6+
opacity: 0,
7+
},
8+
pointer: {
9+
position: 'absolute',
10+
},
11+
});
12+
13+
export default tooltipWrapperStyles;

0 commit comments

Comments
 (0)