From e5a4a8606f42eeac9a84c46c57bdf09f482c22b1 Mon Sep 17 00:00:00 2001 From: Domagoj Kriskovic Date: Mon, 20 Jan 2025 15:35:48 +0100 Subject: [PATCH] Prevent scrolling when reply input is focused (#22968) * Prevent scrolling when reply textarea is focused * remove focusIsOnTextarea check as it is handled above * remove unnecessery setSelected GitOrigin-RevId: 5ce3fc6691a19fe2566875785607bb4faa3e9f52 --- .../components/review-panel-entry.tsx | 27 +++++++++++++++---- .../review-panel-new/utils/position-items.ts | 2 +- .../app/editor/review-panel-new.less | 3 ++- .../pages/editor/review-panel-new.scss | 3 ++- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx b/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx index ed79727622..20784e45f9 100644 --- a/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx +++ b/services/web/frontend/js/features/review-panel-new/components/review-panel-entry.tsx @@ -1,4 +1,4 @@ -import { FC, useCallback, useEffect, useState } from 'react' +import { FC, useCallback, useEffect, useRef, useState } from 'react' import { AnyOperation } from '../../../../../types/change' import { useCodeMirrorStateContext, @@ -15,6 +15,7 @@ import { useLayoutContext } from '@/shared/context/layout-context' import { EditorSelection } from '@codemirror/state' import { EditorView } from '@codemirror/view' import MaterialIcon from '@/shared/components/material-icon' +import { OFFSET_FOR_ENTRIES_ABOVE } from '../utils/position-items' export const ReviewPanelEntry: FC<{ position: number @@ -47,8 +48,10 @@ export const ReviewPanelEntry: FC<{ const { openDocId, getCurrentDocId } = useEditorManagerContext() const [selected, setSelected] = useState(false) const [focused, setFocused] = useState(false) + const [textareaFocused, setTextareaFocused] = useState(false) const { setReviewPanelOpen, reviewPanelOpen } = useLayoutContext() const highlighted = isSelectionWithinOp(op, state.selection.main) + const entryRef = useRef(null) const openReviewPanel = useCallback(() => { setReviewPanelOpen(true) @@ -69,6 +72,17 @@ export const ReviewPanelEntry: FC<{ return } + if (event.target instanceof HTMLTextAreaElement) { + const entryBottom = + (entryRef.current?.offsetTop || 0) + + (entryRef.current?.offsetHeight || 0) + + if (entryBottom > OFFSET_FOR_ENTRIES_ABOVE) { + setTextareaFocused(true) + return + } + } + setSelected(true) if (!selectLineOnFocus) { @@ -76,10 +90,7 @@ export const ReviewPanelEntry: FC<{ } if (getCurrentDocId() !== docId) { - const focusIsOnTextarea = event.target instanceof HTMLTextAreaElement - if (focusIsOnTextarea === false) { - openDocId(docId, { gotoOffset: position, keepCurrentView: true }) - } + openDocId(docId, { gotoOffset: position, keepCurrentView: true }) } else { setTimeout(() => view.dispatch({ @@ -113,10 +124,12 @@ export const ReviewPanelEntry: FC<{ return (
{ setSelected(false) setFocused(false) + setTextareaFocused(false) }} onMouseEnter={() => { if (hoverRanges) { @@ -143,6 +156,10 @@ export const ReviewPanelEntry: FC<{ // 'highlighted' is set if the selection is within op but that doesn't necessarily mean it should be selected // multiple entries can be highlighted at the same time 'review-panel-entry-highlighted': highlighted, + // 'textarea-focused' only changes entry styling (border, shadow etc) + // it doesnt change selected entry because that moves the cursor + // and repositions entries which can cause textarea to be scrolled out of view + 'review-panel-entry-textarea-focused': textareaFocused, 'review-panel-entry-disabled': disabled, }, className diff --git a/services/web/frontend/js/features/review-panel-new/utils/position-items.ts b/services/web/frontend/js/features/review-panel-new/utils/position-items.ts index e5e66ac8bd..3db0244adf 100644 --- a/services/web/frontend/js/features/review-panel-new/utils/position-items.ts +++ b/services/web/frontend/js/features/review-panel-new/utils/position-items.ts @@ -1,8 +1,8 @@ import getMeta from '@/utils/meta' import { debounce } from 'lodash' +export const OFFSET_FOR_ENTRIES_ABOVE = 70 const COLLAPSED_HEADER_HEIGHT = getMeta('ol-isReviewerRoleEnabled') ? 42 : 75 -const OFFSET_FOR_ENTRIES_ABOVE = 70 const GAP_BETWEEN_ENTRIES = 4 export const positionItems = debounce( diff --git a/services/web/frontend/stylesheets/app/editor/review-panel-new.less b/services/web/frontend/stylesheets/app/editor/review-panel-new.less index fe6302dd3f..338e2897e8 100644 --- a/services/web/frontend/stylesheets/app/editor/review-panel-new.less +++ b/services/web/frontend/stylesheets/app/editor/review-panel-new.less @@ -50,7 +50,8 @@ } .review-panel-entry.review-panel-entry-selected, - .review-panel-entry.review-panel-entry-highlighted { + .review-panel-entry.review-panel-entry-highlighted, + .review-panel-entry.review-panel-entry-textarea-focused { margin-left: @spacing-01; border: 1px solid @blue-50; // shadow-md diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel-new.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel-new.scss index 01538a922d..2ea853e8e8 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel-new.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel-new.scss @@ -57,7 +57,8 @@ } .review-panel-entry.review-panel-entry-selected, - .review-panel-entry.review-panel-entry-highlighted { + .review-panel-entry.review-panel-entry-highlighted, + .review-panel-entry.review-panel-entry-textarea-focused { margin-left: var(--spacing-01); border: 1px solid var(--border-active);