mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 18:51:59 +00:00
codeintellify: Fix hover reposition jank (#34028)
This commit is contained in:
parent
f2847e1daf
commit
adb53dea29
@ -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 => {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 }
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user