Skip to content

Commit fb520f7

Browse files
committed
fix: the tooltip issue
1 parent a6bfb5c commit fb520f7

5 files changed

Lines changed: 82 additions & 17 deletions

File tree

.storybook/preview.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { withThemeByClassName } from "@storybook/addon-themes";
22
import type { Preview } from "@storybook/react-webpack5";
3+
import "../src/styles.css";
34

45
export const decorators = [
56
withThemeByClassName({
67
themes: {
7-
light: "light-theme",
8-
dark: "dark-theme",
8+
light: "light",
9+
dark: "dark",
910
},
1011
defaultTheme: "light",
1112
}),

src/Odontogram.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ export const Odontogram: FC<OdontogramProps> = ({
108108
colors = {},
109109
notation,
110110
tooltip = {
111-
margin: 10,
111+
margin: -10,
112+
placement: "top"
112113
},
113114
showTooltip = true,
114115
showHalf = "full",
@@ -234,6 +235,11 @@ export const Odontogram: FC<OdontogramProps> = ({
234235

235236
// Compute tooltip position just above or below depending on space
236237

238+
console.log(
239+
"toothbox box", toothBox,
240+
"svgBOx", svgBox
241+
)
242+
237243
const { x, y } =
238244
placements[placement]?.(toothBox, margin) ??
239245
placements.right(toothBox, margin);

src/Tooltip.tsx

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef } from "react";
1+
import { useEffect, useRef, useState } from "react";
22

33
export type TooltipContentRenderer = (payload?: any) => React.ReactNode;
44

@@ -23,18 +23,22 @@ export const OdontogramTooltip: React.FC<OdontogramTooltipProps> = ({
2323
content,
2424
}) => {
2525
const ref = useRef<HTMLDivElement>(null);
26+
const [coords, setCoords] = useState({ left: 0, top: 0 });
2627

2728
useEffect(() => {
28-
if (ref.current && position) {
29-
const { x, y } = position;
30-
ref.current.style.left = `${x + 10}px`;
31-
ref.current.style.top = `${y + 10}px`;
32-
}
33-
}, [position]);
29+
if (!ref.current || !position) return;
3430

35-
if (!(active && payload)) {
36-
return null;
37-
}
31+
const tooltipBox = ref.current.getBoundingClientRect();
32+
const { x, y } = position;
33+
34+
// place tooltip above the hover point
35+
const left = x - tooltipBox.width / 2;
36+
const top = y - tooltipBox.height - 12; // space for arrow
37+
38+
setCoords({ left, top });
39+
}, [position, content, payload]);
40+
41+
if (!(active && payload)) return null;
3842

3943
return (
4044
<div
@@ -43,18 +47,23 @@ export const OdontogramTooltip: React.FC<OdontogramTooltipProps> = ({
4347
style={{
4448
position: "fixed",
4549
pointerEvents: "none",
46-
background: "rgba(0, 0, 0, 0.75)",
50+
background: "rgba(0,0,0,0.85)",
4751
color: "#fff",
4852
padding: "6px 10px",
4953
borderRadius: "6px",
5054
fontSize: "12px",
5155
lineHeight: 1.3,
5256
whiteSpace: "nowrap",
5357
zIndex: 1000,
54-
transform: "translate(-50%, -100%)",
58+
59+
left: coords.left,
60+
top: coords.top,
61+
62+
opacity: active ? 1 : 0,
5563
transition: "opacity 0.15s ease",
5664
}}
5765
>
66+
{/* tooltip content */}
5867
{getContent(content, payload) ?? (
5968
<>
6069
<div>Tooth: {payload?.notations?.fdi}</div>
@@ -65,6 +74,22 @@ export const OdontogramTooltip: React.FC<OdontogramTooltipProps> = ({
6574
</div>
6675
</>
6776
)}
77+
78+
{/* ARROW */}
79+
<div
80+
className="odontogram-tooltip-arrow"
81+
style={{
82+
position: "absolute",
83+
bottom: "-6px",
84+
left: "50%",
85+
transform: "translateX(-50%)",
86+
width: 0,
87+
height: 0,
88+
borderLeft: "6px solid transparent",
89+
borderRight: "6px solid transparent",
90+
borderTop: "6px solid rgba(0, 0, 0, 0.85)", // arrow color
91+
}}
92+
/>
6893
</div>
6994
);
7095
};

src/stories/Example.stories.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Odontogram from "..";
44
export default {
55
title: "Components/Odontogram",
66
component: Odontogram,
7+
78
parameters: {
89
layout: "centered",
910
backgrounds: {
@@ -27,6 +28,7 @@ export default {
2728
control: "object",
2829
},
2930
onChange: { action: "changed" },
31+
3032
},
3133
} as Meta<typeof Odontogram>;
3234

@@ -40,9 +42,22 @@ Light.args = {
4042
onChange: (selected) => {
4143
alert(JSON.stringify(selected));
4244
},
45+
tooltip: {
46+
margin: -10,
47+
placement: "top"
48+
}
4349
};
4450

45-
export const Dark = Template.bind({});
51+
52+
53+
const DarkTemplate: StoryFn<typeof Odontogram> = (args) =>
54+
<div className="dark-template">
55+
<Odontogram {...args} />
56+
</div>
57+
;
58+
59+
60+
export const Dark = DarkTemplate.bind({});
4661
Dark.args = {
4762
theme: "dark",
4863
colors: {

src/styles.css

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@
44
--light-blue: #c6ccf8;
55
}
66

7+
8+
9+
#storybook-root {
10+
width: 100%;
11+
height: 100%;
12+
}
13+
14+
.dark-template {
15+
width: 100%;
16+
height: 100%;
17+
display: flex;
18+
justify-content: center;
19+
align-items: center;
20+
background-color: #0b0d1a;
21+
}
22+
723
/* 🌙 Dark theme overrides */
824
.Odontogram svg.dark-theme {
925
--dark-blue: #aab6ff; /* lighter blue for contrast on dark bg */
@@ -87,4 +103,6 @@ g[class^="teeth-"]:hover path:nth-of-type(2) {
87103

88104
.Odontogram g[role="option"]:focus-visible {
89105
outline: 4px solid var(--dark-blue) !important;
90-
}
106+
}
107+
108+

0 commit comments

Comments
 (0)