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
This commit is contained in:
Domagoj Kriskovic 2024-10-25 15:25:01 +02:00 committed by Copybot
parent 44b2ca1830
commit 0329a18875
3 changed files with 37 additions and 14 deletions

View file

@ -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<Range<Decoration>>()
@ -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': {

View file

@ -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<SelectionRange>()
export const textSelectedEffect = StateEffect.define<EditorView>()
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),
})
}
}

View file

@ -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
}