codeintellify: Fix hover reposition jank (#34028)

This commit is contained in:
Chris Wendt 2022-04-19 09:38:39 -06:00 committed by GitHub
parent f2847e1daf
commit adb53dea29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 44 deletions

View File

@ -7,7 +7,7 @@ import { NotificationType } from '@sourcegraph/shared/src/api/extension/extensio
import { toAbsoluteBlobURL } from '@sourcegraph/shared/src/util/url'
import { background } from '../../../browser-extension/web-extension-api/runtime'
import { CodeHost } from '../shared/codeHost'
import { CodeHost, OverlayPosition } from '../shared/codeHost'
import { CodeView } from '../shared/codeViews'
import { createNotificationClassNameGetter } from '../shared/getNotificationClassName'
import { getSelectionsFromHash, observeSelectionsFromHash } from '../shared/util/selections'
@ -24,21 +24,26 @@ export function checkIsGitlab(): boolean {
return !!document.head.querySelector('meta[content="GitLab"]')
}
const adjustOverlayPosition: CodeHost['adjustOverlayPosition'] = ({ top, left }) => {
const adjustOverlayPosition: CodeHost['adjustOverlayPosition'] = args => {
const topOrBottom = 'top' in args ? 'top' : 'bottom'
let topOrBottomValue = 'top' in args ? args.top : args.bottom
const header = document.querySelector('header')
if (header) {
top += header.getBoundingClientRect().height
topOrBottomValue += header.getBoundingClientRect().height
}
// When running GitLab from source, we also need to take into account
// the debug header shown at the top of the page.
const debugHeader = document.querySelector('#js-peek.development')
if (debugHeader) {
top += debugHeader.getBoundingClientRect().height
topOrBottomValue += debugHeader.getBoundingClientRect().height
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return {
top,
left,
}
[topOrBottom]: topOrBottomValue,
left: args.left,
} as OverlayPosition
}
export const getToolbarMount = (codeView: HTMLElement, pageKind?: GitLabPageKind): HTMLElement => {

View File

@ -136,10 +136,7 @@ import styles from './codeHost.module.scss'
registerHighlightContributions()
export interface OverlayPosition {
top: number
left: number
}
export type OverlayPosition = { left: number } & ({ top: number } | { bottom: number })
export type ObserveMutations = (
target: Node,

View File

@ -272,7 +272,7 @@ interface InternalHoverifierState<C extends object, D, A> {
hoverOrError?: typeof LOADING | (HoverAttachment & D) | null | ErrorLike
/** The desired position of the hover overlay */
hoverOverlayPosition?: { left: number; top: number }
hoverOverlayPosition?: { left: number } & ({ top: number } | { bottom: number })
/** The currently hovered token */
hoveredToken?: HoveredToken & C

View File

@ -31,7 +31,7 @@ describe('overlay_position', () => {
target: rectangle(128, 220, 60, 16),
hoverOverlayElement: rectangle(28, 38, 350, 150),
}),
{ left: 100, top: 50 }
{ left: 100, bottom: 400 }
)
})
@ -62,7 +62,7 @@ describe('overlay_position', () => {
target: rectangle(128, 220, 60, 16),
hoverOverlayElement: rectangle(28, -362, 350, 150),
}),
{ left: 100, top: 450 }
{ left: 100, bottom: 2400 }
)
})
@ -96,7 +96,7 @@ describe('overlay_position', () => {
target: rectangle(128, 220, 60, 16),
hoverOverlayElement: rectangle(28, 38, 350, 150),
}),
{ left: 100, top: 50 }
{ left: 100, bottom: 400 }
)
})
@ -127,7 +127,7 @@ describe('overlay_position', () => {
target: rectangle(128, 220, 60, 16),
hoverOverlayElement: rectangle(-172, -362, 350, 150),
}),
{ left: 300, top: 450 }
{ left: 300, bottom: 0 }
)
})

View File

@ -12,12 +12,7 @@ export interface CalculateOverlayPositionOptions {
hoverOverlayElement: HasGetBoundingClientRect
}
export interface CSSOffsets {
/** Offset from the left in pixel */
left: number
/** Offset from the top in pixel */
top: number
}
export type CSSOffsets = { left: number } & ({ top: number } | { bottom: number })
/**
* Calculates the desired position of the hover overlay depending on the container,
@ -35,19 +30,21 @@ export const calculateOverlayPosition = ({
// If the relativeElement is scrolled horizontally, we need to account for the offset (if not scrollLeft will be 0)
const relativeHoverOverlayLeft = targetBounds.left + relativeElement.scrollLeft - relativeElementBounds.left
let relativeHoverOverlayTop: number
// Check if the top of the hover overlay would be outside of the relative element or the viewport
if (targetBounds.top - hoverOverlayBounds.height < Math.max(relativeElementBounds.top, 0)) {
// Position it below the target
// If the relativeElement is scrolled, we need to account for the offset (if not scrollTop will be 0)
relativeHoverOverlayTop = targetBounds.bottom - relativeElementBounds.top + relativeElement.scrollTop
} else {
// Else position it above the target
// Caculate the offset from the top of the relativeElement content to the top of the target
// If the relativeElement is scrolled, we need to account for the offset (if not scrollTop will be 0)
const relativeTargetTop = targetBounds.top - relativeElementBounds.top + relativeElement.scrollTop
relativeHoverOverlayTop = relativeTargetTop - hoverOverlayBounds.height
return {
left: relativeHoverOverlayLeft,
top: targetBounds.bottom - relativeElementBounds.top + relativeElement.scrollTop,
}
}
return { left: relativeHoverOverlayLeft, top: relativeHoverOverlayTop }
// Else position it above the target
// If the relativeElement is scrolled, we need to account for the offset (if not scrollTop will be 0)
return {
left: relativeHoverOverlayLeft,
bottom:
relativeElementBounds.height - (targetBounds.top - relativeElementBounds.top + relativeElement.scrollTop),
}
}

View File

@ -14,7 +14,7 @@ export interface HoverOverlayProps<C extends object, D, A> {
hoverOrError?: typeof LOADING | (HoverAttachment & D) | null | ErrorLike
/** The position of the tooltip (assigned to `style`) */
overlayPosition?: { left: number; top: number }
overlayPosition?: { left: number } & ({ top: number } | { bottom: number })
/**
* The hovered token (position and word).

View File

@ -62,18 +62,24 @@ export interface HoverOverlayProps
useBrandedLogo?: boolean
}
const getOverlayStyle = (overlayPosition: HoverOverlayProps['overlayPosition']): CSSProperties =>
overlayPosition
? {
opacity: 1,
visibility: 'visible',
left: `${overlayPosition.left}px`,
top: `${overlayPosition.top}px`,
}
: {
opacity: 0,
visibility: 'hidden',
}
const getOverlayStyle = (overlayPosition: HoverOverlayProps['overlayPosition']): CSSProperties => {
if (!overlayPosition) {
return {
opacity: 0,
visibility: 'hidden',
}
}
const topOrBottom = 'top' in overlayPosition ? 'top' : 'bottom'
const topOrBottomValue = 'top' in overlayPosition ? overlayPosition.top : overlayPosition.bottom
return {
opacity: 1,
visibility: 'visible',
left: `${overlayPosition.left}px`,
[topOrBottom]: `${topOrBottomValue}px`,
}
}
export const HoverOverlay: React.FunctionComponent<HoverOverlayProps> = props => {
const {