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 { isSplitTestEnabled } from '@/utils/splitTestUtils'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import { textSelected, textSelectedEffect } from './text-selected' import { textSelected, textSelectedEffect } from './text-selected'
import { isCursorNearViewportTop } from '../utils/is-cursor-near-edge'
export const addNewCommentRangeEffect = StateEffect.define<Range<Decoration>>() export const addNewCommentRangeEffect = StateEffect.define<Range<Decoration>>()
@ -88,14 +89,15 @@ export const reviewTooltipStateField = StateField.define<{
], ],
}) })
function buildTooltip(range: SelectionRange): Tooltip | null { function buildTooltip(view: EditorView): Tooltip | null {
if (range.empty) { if (view.state.selection.main.empty) {
return null return null
} }
const pos = view.state.selection.main.head
return { return {
pos: range.assoc < 0 ? range.to : range.from, pos,
above: true, above: !isCursorNearViewportTop(view, pos, 50),
strictSide: true, strictSide: true,
arrow: false, arrow: false,
create() { create() {
@ -113,6 +115,7 @@ const reviewTooltipTheme = EditorView.baseTheme({
'.review-tooltip-menu-container.cm-tooltip': { '.review-tooltip-menu-container.cm-tooltip': {
backgroundColor: 'transparent', backgroundColor: 'transparent',
border: 'none', border: 'none',
zIndex: 0,
}, },
'&light': { '&light': {

View file

@ -1,13 +1,13 @@
import { SelectionRange, StateEffect } from '@codemirror/state' import { StateEffect } from '@codemirror/state'
import { ViewPlugin } from '@codemirror/view' import { EditorView, ViewPlugin } from '@codemirror/view'
export const textSelectedEffect = StateEffect.define<SelectionRange>() export const textSelectedEffect = StateEffect.define<EditorView>()
export const textSelected = ViewPlugin.define(view => { export const textSelected = ViewPlugin.define(view => {
function mouseUpListener() { function mouseUpListener() {
if (!view.state.selection.main.empty) { if (!view.state.selection.main.empty) {
view.dispatch({ 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.state.selection.main.empty
) { ) {
view.dispatch({ 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 const BOTTOM_EDGE_THRESHOLD = 200
export function isCursorNearViewportEdge(view: EditorView, pos: number) { 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) const cursorCoords = view.coordsAtPos(pos)
if (!cursorCoords) { if (!cursorCoords) {
@ -12,12 +22,22 @@ export function isCursorNearViewportEdge(view: EditorView, pos: number) {
const scrollInfo = view.scrollDOM.getBoundingClientRect() const scrollInfo = view.scrollDOM.getBoundingClientRect()
// check if the cursor is near the top of the viewport return Math.abs(cursorCoords.bottom - scrollInfo.top) <= threshold
if (Math.abs(cursorCoords.bottom - scrollInfo.top) <= TOP_EDGE_THRESHOLD) { }
return true
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 viewportHeight = view.scrollDOM.clientHeight
const viewportBottom = scrollInfo.top + viewportHeight const viewportBottom = scrollInfo.top + viewportHeight
return Math.abs(cursorCoords.bottom - viewportBottom) <= BOTTOM_EDGE_THRESHOLD return Math.abs(cursorCoords.bottom - viewportBottom) <= threshold
} }