Skip to content

Commit f3c4c53

Browse files
Karthik-B-06kodiakhq[bot]
authored andcommitted
chore(tooltip): 🎉 wrap tooltip component
- combined `side` and `align` props into a single prop `placement` - changed action prop type and set default value - refactored Tooltip arrow component to work on flip
1 parent bdc26c2 commit f3c4c53

File tree

2 files changed

+128
-91
lines changed

2 files changed

+128
-91
lines changed

src/components/tooltip/Tooltip.tsx

Lines changed: 8 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo } from "react";
1+
import React from "react";
22
import { Platform } from "react-native";
33
import { Popover } from "react-native-popper";
44

@@ -22,23 +22,14 @@ export type TooltipPlacement =
2222
| "left top"
2323
| "left bottom";
2424

25-
export type TooltipSide = "top" | "bottom" | "left" | "right";
26-
27-
export type TooltipAlign = "end" | "start" | "center";
28-
2925
export type TooltipTriggerAction = "press" | "hover" | "longPress" | undefined;
3026

3127
export interface TooltipProps {
3228
/**
33-
* Tooltip/Popover Position
29+
* Tooltip/Popover Placement
3430
* @default bottom
3531
*/
36-
side: TooltipSide;
37-
/**
38-
* Tooltip/Popover Alignment
39-
* @default center
40-
*/
41-
align: TooltipAlign;
32+
placement: TooltipPlacement;
4233
/**
4334
* Content of Tooltip
4435
*/
@@ -51,7 +42,7 @@ export interface TooltipProps {
5142
* Action in which to show the Tooltip
5243
* @default onPress
5344
*/
54-
action: "onPress" | "onPressIn" | "onLongPress";
45+
action: TooltipTriggerAction;
5546
/**
5647
* Distance between popover and trigger's main axis
5748
* @default 0
@@ -89,87 +80,27 @@ export const RNTooltip: React.FC<Partial<TooltipProps>> = props => {
8980
const {
9081
trigger,
9182
content,
92-
side = "bottom",
93-
align = "center",
83+
placement = "bottom",
9484
mainOffset = 0,
9585
crossOffset = 0,
9686
hasArrow = false,
9787
shouldFlip = true,
98-
action = "onPress",
88+
action = Platform.OS === "web" ? "hover" : "press",
9989
} = props;
10090

101-
const placement = useMemo(() => {
102-
if (align === "center") {
103-
return side;
104-
} else if (align === "start") {
105-
if (side === "top" || side === "bottom") {
106-
return `${side} left`;
107-
} else {
108-
return `${side} top`;
109-
}
110-
} else {
111-
if (side === "top" || side === "bottom") {
112-
return `${side} right`;
113-
} else {
114-
return `${side} bottom`;
115-
}
116-
}
117-
}, [side, align]) as TooltipPlacement;
118-
119-
const tooltipTriggerAction = useMemo(() => {
120-
if (Platform.OS === "web") {
121-
return "hover";
122-
} else {
123-
if (action === "onPress") {
124-
return "press";
125-
} else {
126-
return "longPress";
127-
}
128-
}
129-
}, [action]) as TooltipTriggerAction;
130-
131-
const getArrowPosition = (arrowPlacement: TooltipPlacement) => {
132-
if (arrowPlacement.split(" ")[0] === "top") {
133-
return {
134-
transform: [{ rotate: "0deg" }, { translateX: -6 }],
135-
bottom: -4,
136-
};
137-
} else if (arrowPlacement?.split(" ")[0] === "bottom") {
138-
return {
139-
transform: [{ rotate: "0deg" }, { translateX: -6 }],
140-
};
141-
} else if (arrowPlacement?.split(" ")[0] === "left") {
142-
return {
143-
transform: [{ rotate: "0deg" }, { translateY: -6 }, { translateX: 5 }],
144-
};
145-
} else if (arrowPlacement?.split(" ")[0] === "right") {
146-
return {
147-
transform: [{ rotate: "0deg" }, { translateY: -6 }, { translateX: -1 }],
148-
};
149-
}
150-
return {};
151-
};
15291
return (
15392
<Popover
15493
offset={mainOffset}
15594
crossOffset={crossOffset}
15695
shouldFlip={shouldFlip}
15796
placement={placement}
158-
on={tooltipTriggerAction}
97+
on={action}
15998
// @ts-ignore
16099
trigger={trigger}
161100
>
162101
{Platform.OS !== "web" && <Popover.Backdrop />}
163102
<Popover.Content>
164-
{hasArrow && (
165-
<Popover.Arrow
166-
height={16}
167-
width={16}
168-
style={getArrowPosition(placement)}
169-
>
170-
<TooltipArrow placement={placement} />
171-
</Popover.Arrow>
172-
)}
103+
{hasArrow && <TooltipArrow />}
173104
<Box style={tailwind.style(tooltipTheme.contentWrapper)}>
174105
{typeof content === "string" ? (
175106
<Text style={tailwind.style(tooltipTheme.content)}>{content}</Text>
Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import React from "react";
2+
import { ViewStyle } from "react-native";
23
import Svg, { Path } from "react-native-svg";
34

5+
import { useTheme } from "../../theme";
46
import { IconProps } from "../../utils";
57
import { Icon } from "../icon";
68

79
import { TooltipPlacement } from "./Tooltip";
810

9-
const UpArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
11+
const UpArrow: React.FC<IconProps> = ({ fill = "#171717" }) => {
1012
return (
1113
<Svg width="100%" height="100%" viewBox="0 0 12 5" fill="none">
1214
<Path
@@ -17,7 +19,7 @@ const UpArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
1719
);
1820
};
1921

20-
const LeftArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
22+
const LeftArrow: React.FC<IconProps> = ({ fill = "#171717" }) => {
2123
return (
2224
<Svg width="100%" height="100%" viewBox="0 0 5 12" fill="none">
2325
<Path
@@ -28,7 +30,7 @@ const LeftArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
2830
);
2931
};
3032

31-
const RightArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
33+
const RightArrow: React.FC<IconProps> = ({ fill = "#171717" }) => {
3234
return (
3335
<Svg width="100%" height="100%" viewBox="0 0 5 12" fill="none">
3436
<Path
@@ -39,7 +41,7 @@ const RightArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
3941
);
4042
};
4143

42-
const DownArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
44+
const DownArrow: React.FC<IconProps> = ({ fill = "#171717" }) => {
4345
return (
4446
<Svg width="12" height="5" viewBox="0 0 12 5" fill="none">
4547
<Path
@@ -50,22 +52,126 @@ const DownArrow: React.FC<IconProps> = ({ fill = "#27272A" }) => {
5052
);
5153
};
5254

55+
interface ArrowStyle {
56+
style: Pick<ViewStyle, "left" | "top">;
57+
}
5358
interface TooltipArrowProps {
54-
placement: TooltipPlacement;
59+
actualPlacement?: TooltipPlacement;
60+
arrowProps?: ArrowStyle;
5561
}
5662

57-
const TooltipArrow: React.FC<TooltipArrowProps> = ({ placement }) => {
58-
if (placement.split(" ")[0] === "top") {
59-
return <Icon icon={<DownArrow />} size={12} />;
60-
} else if (placement.split(" ")[0] === "left") {
61-
return <Icon icon={<RightArrow />} size={12} />;
62-
} else if (placement.split(" ")[0] === "right") {
63-
return <Icon icon={<LeftArrow />} size={12} />;
64-
} else if (placement.split(" ")[0] === "bottom") {
65-
return <Icon icon={<UpArrow />} size={12} />;
63+
const defaultArrowHeight = 12;
64+
65+
const getArrowStyles = (props: {
66+
placement: TooltipPlacement;
67+
height: number;
68+
width: number;
69+
}) => {
70+
let additionalStyles: any = {
71+
transform: [],
72+
};
73+
74+
const diagonalLength = getDiagonalLength(
75+
defaultArrowHeight,
76+
defaultArrowHeight,
77+
);
78+
if (props.placement === "top" && props.width) {
79+
additionalStyles.transform.push({ translateX: -props.width / 2 });
80+
additionalStyles.transform.push({ translateY: 4.5 });
81+
82+
additionalStyles.bottom = Math.ceil(
83+
(diagonalLength - defaultArrowHeight) / 2,
84+
);
85+
}
86+
87+
if (props.placement === "bottom" && props.width) {
88+
additionalStyles.transform.push({ translateX: -props.width / 2 });
89+
additionalStyles.transform.push({ translateY: 2.3 });
90+
additionalStyles.top = Math.ceil((diagonalLength - defaultArrowHeight) / 2);
91+
}
92+
93+
if (props.placement === "left" && props.height) {
94+
additionalStyles.transform.push({ translateY: -props.height / 2 });
95+
additionalStyles.transform.push({ translateX: 0.7 });
96+
additionalStyles.right = Math.ceil(
97+
(diagonalLength - defaultArrowHeight) / 2,
98+
);
99+
}
100+
101+
if (props.placement === "right" && props.height) {
102+
additionalStyles.transform.push({ translateY: -props.height / 2 });
103+
additionalStyles.transform.push({ translateX: 2.3 });
104+
additionalStyles.left = Math.ceil(
105+
(diagonalLength - defaultArrowHeight) / 2,
106+
);
107+
}
108+
109+
return additionalStyles;
110+
};
111+
112+
const getDiagonalLength = (height: number, width: number) => {
113+
return Math.pow(height * height + width * width, 0.5);
114+
};
115+
116+
const TooltipArrow: React.FC<TooltipArrowProps> = ({
117+
actualPlacement = "bottom",
118+
arrowProps,
119+
}) => {
120+
const tailwind = useTheme();
121+
const additonalArrowStyles = getArrowStyles({
122+
placement: actualPlacement,
123+
height: 12,
124+
width: 12,
125+
});
126+
const arrowStyles: ViewStyle = {
127+
position: "absolute",
128+
height: 12,
129+
width: 12,
130+
...additonalArrowStyles,
131+
...arrowProps?.style,
132+
};
133+
console.log(arrowStyles);
134+
if (actualPlacement?.split(" ")[0] === "top") {
135+
return (
136+
<Icon
137+
color={tailwind.getColor("text-gray-900")}
138+
style={arrowStyles}
139+
icon={<DownArrow />}
140+
size={12}
141+
/>
142+
);
143+
} else if (actualPlacement?.split(" ")[0] === "left") {
144+
return (
145+
<Icon
146+
color={tailwind.getColor("text-gray-900")}
147+
style={arrowStyles}
148+
icon={<RightArrow />}
149+
size={12}
150+
/>
151+
);
152+
} else if (actualPlacement?.split(" ")[0] === "right") {
153+
return (
154+
<Icon
155+
color={tailwind.getColor("text-gray-900")}
156+
style={arrowStyles}
157+
icon={<LeftArrow />}
158+
size={12}
159+
/>
160+
);
161+
} else if (actualPlacement?.split(" ")[0] === "bottom") {
162+
return (
163+
<Icon
164+
color={tailwind.getColor("text-gray-900")}
165+
style={arrowStyles}
166+
icon={<UpArrow />}
167+
size={12}
168+
/>
169+
);
66170
} else {
67171
return null;
68172
}
69173
};
70174

175+
TooltipArrow.displayName = "PopperArrow";
176+
71177
export default TooltipArrow;

0 commit comments

Comments
 (0)