Skip to content
This repository was archived by the owner on Nov 25, 2021. It is now read-only.

Commit 0923e9b

Browse files
ijsnowfelixfbecker
authored andcommitted
feat: hover overlay positioning in browser extension
1 parent a11d2d7 commit 0923e9b

File tree

2 files changed

+58
-13
lines changed

2 files changed

+58
-13
lines changed

src/hoverifier.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ interface HoverifierOptions {
6969

7070
fetchHover: HoverFetcher
7171
fetchJumpURL: JumpURLFetcher
72+
73+
containerIsScrollable: boolean
7274
}
7375

7476
/**
@@ -211,6 +213,7 @@ export const createHoverifier = ({
211213
fetchJumpURL,
212214
logTelemetryEvent,
213215
dom,
216+
containerIsScrollable,
214217
}: HoverifierOptions): Hoverifier => {
215218
// Internal state that is not exposed to the caller
216219
// Shared between all hoverified code views
@@ -335,7 +338,7 @@ export const createHoverifier = ({
335338
// with the latest target that came from either a mouseover, click or location change (whatever was the most recent)
336339
withLatestFrom(merge(codeMouseOverTargets, codeClickTargets, jumpTargets)),
337340
map(([{ hoverOverlayElement, scrollElement }, { target }]) =>
338-
calculateOverlayPosition(scrollElement, target, hoverOverlayElement)
341+
calculateOverlayPosition(scrollElement, target, hoverOverlayElement, containerIsScrollable)
339342
)
340343
)
341344
.subscribe(hoverOverlayPosition => {

src/overlay_position.ts

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,7 @@
44
*/
55
const BLOB_PADDING_TOP = 8
66

7-
/**
8-
* Calculates the desired position of the hover overlay depending on the container,
9-
* the hover target and the size of the hover overlay
10-
*
11-
* @param scrollable The closest container that is scrollable
12-
* @param target The DOM Node that was hovered
13-
* @param tooltip The DOM Node of the tooltip
14-
*/
15-
export const calculateOverlayPosition = (
7+
const calculateOverlayPositionWithinScrollable = (
168
scrollable: HTMLElement,
179
target: HTMLElement,
1810
tooltip: HTMLElement
@@ -25,18 +17,68 @@ export const calculateOverlayPosition = (
2517
// changes to vertical height if the tooltip is at the edge of the viewport.
2618
const relLeft = targetBound.left - scrollableBounds.left
2719

20+
const scrollTop = scrollable === document.documentElement ? window.pageYOffset : scrollable.scrollTop
21+
2822
// Anchor the tooltip vertically.
2923
const tooltipBound = tooltip.getBoundingClientRect()
30-
const relTop = targetBound.top + scrollable.scrollTop - scrollableBounds.top
24+
const relTop = targetBound.top + scrollTop - scrollableBounds.top
3125
// This is the padding-top of the blob element
3226
let tooltipTop = relTop - (tooltipBound.height - BLOB_PADDING_TOP)
33-
if (tooltipTop - scrollable.scrollTop < 0) {
27+
if (tooltipTop - scrollTop < 0) {
3428
// Tooltip wouldn't be visible from the top, so display it at the
3529
// bottom.
36-
const relBottom = targetBound.bottom + scrollable.scrollTop - scrollableBounds.top
30+
const relBottom = targetBound.bottom + scrollTop - scrollableBounds.top
3731
tooltipTop = relBottom
3832
} else {
3933
tooltipTop -= BLOB_PADDING_TOP
4034
}
4135
return { left: relLeft, top: tooltipTop }
4236
}
37+
38+
const calculateOverlayPositionWithoutScrollable = (
39+
container: HTMLElement,
40+
target: HTMLElement,
41+
tooltip: HTMLElement
42+
): { left: number; top: number } => {
43+
const containerBound = container.getBoundingClientRect()
44+
45+
// Anchor it horizontally, prior to rendering to account for wrapping
46+
// changes to vertical height if the tooltip is at the edge of the viewport.
47+
const targetBound = target.getBoundingClientRect()
48+
const tooltipLeft = targetBound.left - containerBound.left + window.scrollX
49+
50+
// Anchor the tooltip vertically.
51+
const tooltipBound = tooltip.getBoundingClientRect()
52+
const relTop = targetBound.top - containerBound.top + container.offsetTop
53+
54+
let tooltipTop = relTop - tooltipBound.height
55+
if (tooltipTop - window.scrollY < 0) {
56+
// Tooltip wouldn't be visible from the top, so display it at the bottom.
57+
const relBottom = relTop + targetBound.height
58+
tooltipTop = relBottom
59+
}
60+
61+
return { top: tooltipTop, left: tooltipLeft }
62+
}
63+
64+
/**
65+
* Calculates the desired position of the hover overlay depending on the container,
66+
* the hover target and the size of the hover overlay
67+
*
68+
* @param container The container. If the code view is scrollable, it's the scrollable element, otherwise its the codeView itself.
69+
* @param target The DOM Node that was hovered.
70+
* @param tooltip The DOM Node of the tooltip.
71+
* @param isScrollable Whether the code view is scrollable or not.
72+
*/
73+
export const calculateOverlayPosition = (
74+
container: HTMLElement,
75+
target: HTMLElement,
76+
tooltip: HTMLElement,
77+
isScrollable?: boolean
78+
): { left: number; top: number } => {
79+
if (isScrollable) {
80+
return calculateOverlayPositionWithinScrollable(container, target, tooltip)
81+
}
82+
83+
return calculateOverlayPositionWithoutScrollable(container, target, tooltip)
84+
}

0 commit comments

Comments
 (0)