-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathQuestionTooltip.tsx
More file actions
105 lines (93 loc) · 3.2 KB
/
QuestionTooltip.tsx
File metadata and controls
105 lines (93 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { createSignal, JSX, Show } from "solid-js";
import QuestionMarkIcon from "../icon/QuestionMarkIcon";
export enum TooltipAlignment {
Center = "center",
Left = "left",
Right = "right",
}
interface TooltipProps {
text: string;
children?: JSX.Element;
}
export default function QuestionTooltip(props: TooltipProps) {
const [isVisible, setIsVisible] = createSignal(false);
const [position, setPosition] = createSignal<{
x: number;
align: TooltipAlignment;
containerWidth: number;
}>({ x: 0, align: TooltipAlignment.Center, containerWidth: 0 });
let containerRef: HTMLDivElement | undefined;
const determineAlignment = (rect: DOMRect): TooltipAlignment => {
const tooltipWidth = 256; // w-64 is 16rem = 256px
// Calculate potential left and right bounds if centered
const centerLeft = rect.left + rect.width / 2 - tooltipWidth / 2;
const centerRight = centerLeft + tooltipWidth;
const viewportWidth = window.innerWidth;
const padding = 16; // 16px safety padding from screen edges
if (centerLeft < padding) {
return TooltipAlignment.Left;
} else if (centerRight > viewportWidth - padding) {
return TooltipAlignment.Right;
} else {
return TooltipAlignment.Center;
}
};
const handleMouseEnter = () => {
if (containerRef) {
const rect = containerRef.getBoundingClientRect();
const align = determineAlignment(rect);
setPosition({ x: 0, align, containerWidth: rect.width });
}
setIsVisible(true);
};
const handleMouseLeave = () => setIsVisible(false);
return (
<div
ref={containerRef}
class="relative inline-flex items-center justify-center cursor-help"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onFocus={handleMouseEnter}
onBlur={handleMouseLeave}
tabIndex={0}
aria-label="More information"
>
<Show
when={props.children}
fallback={
<QuestionMarkIcon class="size-5 text-gray-500 hover:text-gray-700 transition-colors" />
}
>
{props.children}
</Show>
<Show when={isVisible()}>
<div
class={`absolute z-50 w-64 p-3 mt-2 text-sm text-gray-800 bg-white border border-gray-200 rounded-lg shadow-lg top-full pointer-events-none fade-in ${
position().align === TooltipAlignment.Center
? "left-1/2 -translate-x-1/2"
: position().align === TooltipAlignment.Left
? "left-0"
: "right-0"
}`}
>
{props.text}
{/* Decorative arrow pointing up */}
<div
class={`absolute w-3 h-3 bg-white border-t border-l border-gray-200 rotate-45 -top-[7px] ${
position().align === TooltipAlignment.Center
? "left-1/2 -translate-x-1/2"
: ""
}`}
style={
position().align === TooltipAlignment.Left
? { left: `${position().containerWidth / 2 - 6}px` }
: position().align === TooltipAlignment.Right
? { right: `${position().containerWidth / 2 - 6}px` }
: {}
}
/>
</div>
</Show>
</div>
);
}