From 0329a1887553e7db5bed0fa5efaddc66da33f03c Mon Sep 17 00:00:00 2001 From: Domagoj Kriskovic Date: Fri, 25 Oct 2024 15:25:01 +0200 Subject: [PATCH] Show "add comment" tooltip below cursor if near viewport top (#21348) * Scroll selection anchor into view when adding new comment * check if cursor is near viewport edge * Show "add comment" tooltip below cursor if near viewport top GitOrigin-RevId: 0dc2234bc03b1d88a3719ba01a4a865f218b9bfa --- .../extensions/review-tooltip.ts | 11 ++++--- .../source-editor/extensions/text-selected.ts | 10 +++---- .../utils/is-cursor-near-edge.ts | 30 +++++++++++++++---- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/services/web/frontend/js/features/source-editor/extensions/review-tooltip.ts b/services/web/frontend/js/features/source-editor/extensions/review-tooltip.ts index 0c7d917b68..bcce6ea6ed 100644 --- a/services/web/frontend/js/features/source-editor/extensions/review-tooltip.ts +++ b/services/web/frontend/js/features/source-editor/extensions/review-tooltip.ts @@ -15,6 +15,7 @@ import { import { isSplitTestEnabled } from '@/utils/splitTestUtils' import { v4 as uuid } from 'uuid' import { textSelected, textSelectedEffect } from './text-selected' +import { isCursorNearViewportTop } from '../utils/is-cursor-near-edge' export const addNewCommentRangeEffect = StateEffect.define>() @@ -88,14 +89,15 @@ export const reviewTooltipStateField = StateField.define<{ ], }) -function buildTooltip(range: SelectionRange): Tooltip | null { - if (range.empty) { +function buildTooltip(view: EditorView): Tooltip | null { + if (view.state.selection.main.empty) { return null } + const pos = view.state.selection.main.head return { - pos: range.assoc < 0 ? range.to : range.from, - above: true, + pos, + above: !isCursorNearViewportTop(view, pos, 50), strictSide: true, arrow: false, create() { @@ -113,6 +115,7 @@ const reviewTooltipTheme = EditorView.baseTheme({ '.review-tooltip-menu-container.cm-tooltip': { backgroundColor: 'transparent', border: 'none', + zIndex: 0, }, '&light': { diff --git a/services/web/frontend/js/features/source-editor/extensions/text-selected.ts b/services/web/frontend/js/features/source-editor/extensions/text-selected.ts index 371aafe04b..71456bac97 100644 --- a/services/web/frontend/js/features/source-editor/extensions/text-selected.ts +++ b/services/web/frontend/js/features/source-editor/extensions/text-selected.ts @@ -1,13 +1,13 @@ -import { SelectionRange, StateEffect } from '@codemirror/state' -import { ViewPlugin } from '@codemirror/view' +import { StateEffect } from '@codemirror/state' +import { EditorView, ViewPlugin } from '@codemirror/view' -export const textSelectedEffect = StateEffect.define() +export const textSelectedEffect = StateEffect.define() export const textSelected = ViewPlugin.define(view => { function mouseUpListener() { if (!view.state.selection.main.empty) { view.dispatch({ - effects: textSelectedEffect.of(view.state.selection.main), + effects: textSelectedEffect.of(view), }) } } @@ -17,7 +17,7 @@ export const textSelected = ViewPlugin.define(view => { !view.state.selection.main.empty ) { view.dispatch({ - effects: textSelectedEffect.of(view.state.selection.main), + effects: textSelectedEffect.of(view), }) } } diff --git a/services/web/frontend/js/features/source-editor/utils/is-cursor-near-edge.ts b/services/web/frontend/js/features/source-editor/utils/is-cursor-near-edge.ts index 6878a2e798..e79688f142 100644 --- a/services/web/frontend/js/features/source-editor/utils/is-cursor-near-edge.ts +++ b/services/web/frontend/js/features/source-editor/utils/is-cursor-near-edge.ts @@ -4,6 +4,16 @@ const TOP_EDGE_THRESHOLD = 100 const BOTTOM_EDGE_THRESHOLD = 200 export function isCursorNearViewportEdge(view: EditorView, pos: number) { + return ( + isCursorNearViewportTop(view, pos) || isCursorNearViewportBottom(view, pos) + ) +} + +export function isCursorNearViewportTop( + view: EditorView, + pos: number, + threshold = TOP_EDGE_THRESHOLD +) { const cursorCoords = view.coordsAtPos(pos) if (!cursorCoords) { @@ -12,12 +22,22 @@ export function isCursorNearViewportEdge(view: EditorView, pos: number) { const scrollInfo = view.scrollDOM.getBoundingClientRect() - // check if the cursor is near the top of the viewport - if (Math.abs(cursorCoords.bottom - scrollInfo.top) <= TOP_EDGE_THRESHOLD) { - return true + return Math.abs(cursorCoords.bottom - scrollInfo.top) <= threshold +} + +export function isCursorNearViewportBottom( + view: EditorView, + pos: number, + threshold = BOTTOM_EDGE_THRESHOLD +) { + const cursorCoords = view.coordsAtPos(pos) + + if (!cursorCoords) { + return false } - // check if the cursor is near the bottom of the viewport + + const scrollInfo = view.scrollDOM.getBoundingClientRect() const viewportHeight = view.scrollDOM.clientHeight const viewportBottom = scrollInfo.top + viewportHeight - return Math.abs(cursorCoords.bottom - viewportBottom) <= BOTTOM_EDGE_THRESHOLD + return Math.abs(cursorCoords.bottom - viewportBottom) <= threshold }