Avoid errant scroll on context menu hide

This commit is contained in:
trevor-signal
2025-02-19 12:22:48 -05:00
committed by GitHub
parent 78cfa7eca3
commit 1d44c70393
6 changed files with 33 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
diff --git a/node_modules/react-contextmenu/modules/ContextMenu.js b/node_modules/react-contextmenu/modules/ContextMenu.js diff --git a/node_modules/react-contextmenu/modules/ContextMenu.js b/node_modules/react-contextmenu/modules/ContextMenu.js
index 2f88213..4cf584a 100644 index 2f88213..41e47ea 100644
--- a/node_modules/react-contextmenu/modules/ContextMenu.js --- a/node_modules/react-contextmenu/modules/ContextMenu.js
+++ b/node_modules/react-contextmenu/modules/ContextMenu.js +++ b/node_modules/react-contextmenu/modules/ContextMenu.js
@@ -81,6 +81,11 @@ var ContextMenu = function (_AbstractMenu) { @@ -81,6 +81,11 @@ var ContextMenu = function (_AbstractMenu) {
@@ -35,13 +35,15 @@ index 2f88213..4cf584a 100644
}); });
}); });
} else { } else {
@@ -248,6 +259,14 @@ var ContextMenu = function (_AbstractMenu) { @@ -248,6 +259,16 @@ var ContextMenu = function (_AbstractMenu) {
if (!_this2.menu) return; if (!_this2.menu) return;
_this2.menu.style.opacity = 0; _this2.menu.style.opacity = 0;
_this2.menu.style.pointerEvents = 'none'; _this2.menu.style.pointerEvents = 'none';
+ +
+ // Return to the previous focus state when dismissing the menu, unless the + // Return to the previous focus state when dismissing the menu, unless the
+ // menu option focused another element. This is important for keyboard mode. + // menu option focused another element. This is important for keyboard mode.
+ if (_this2.props.avoidFocusRestoreOnBlur) return;
+
+ var isFocusWithinMenu = _this2.menu.contains(document.activeElement); + var isFocusWithinMenu = _this2.menu.contains(document.activeElement);
+ if (isFocusWithinMenu && _this2.previousFocus && _this2.previousFocus.focus) { + if (isFocusWithinMenu && _this2.previousFocus && _this2.previousFocus.focus) {
+ _this2.previousFocus.focus(); + _this2.previousFocus.focus();
@@ -139,3 +141,15 @@ index ad1dc70..c919be8 100644
this.subMenu.classList.remove(_helpers.cssClasses.menuVisible); this.subMenu.classList.remove(_helpers.cssClasses.menuVisible);
} }
} }
diff --git a/node_modules/react-contextmenu/src/index.d.ts b/node_modules/react-contextmenu/src/index.d.ts
index 753ce90..c5971a4 100644
--- a/node_modules/react-contextmenu/src/index.d.ts
+++ b/node_modules/react-contextmenu/src/index.d.ts
@@ -14,6 +14,7 @@ declare module "react-contextmenu" {
preventHideOnResize?: boolean,
preventHideOnScroll?: boolean,
style?: React.CSSProperties,
+ avoidFocusRestoreOnBlur?: boolean;
}
export interface ContextMenuTriggerProps {

View File

@@ -62,6 +62,7 @@ const getCommonProps = (options: {
id: 'message-id', id: 'message-id',
conversationId: conversation.id, conversationId: conversation.id,
i18n, i18n,
interactionMode: 'mouse',
isNextItemCallingNotification: false, isNextItemCallingNotification: false,
onOutgoingAudioCallInConversation: action( onOutgoingAudioCallInConversation: action(
'onOutgoingAudioCallInConversation' 'onOutgoingAudioCallInConversation'

View File

@@ -38,6 +38,7 @@ import {
import { MINUTE } from '../../util/durations'; import { MINUTE } from '../../util/durations';
import { isMoreRecentThan } from '../../util/timestamp'; import { isMoreRecentThan } from '../../util/timestamp';
import { InAnotherCallTooltip } from './InAnotherCallTooltip'; import { InAnotherCallTooltip } from './InAnotherCallTooltip';
import type { InteractionModeType } from '../../state/ducks/conversations';
export type PropsActionsType = { export type PropsActionsType = {
onOutgoingAudioCallInConversation: (conversationId: string) => void; onOutgoingAudioCallInConversation: (conversationId: string) => void;
@@ -50,6 +51,7 @@ type PropsHousekeeping = {
i18n: LocalizerType; i18n: LocalizerType;
id: string; id: string;
conversationId: string; conversationId: string;
interactionMode: InteractionModeType;
isNextItemCallingNotification: boolean; isNextItemCallingNotification: boolean;
}; };
@@ -120,6 +122,7 @@ export const CallingNotification: React.FC<PropsType> = React.memo(
<MessageContextMenu <MessageContextMenu
i18n={i18n} i18n={i18n}
triggerId={props.id} triggerId={props.id}
interactionMode={props.interactionMode}
onDeleteMessage={() => { onDeleteMessage={() => {
props.toggleDeleteMessagesModal({ props.toggleDeleteMessagesModal({
conversationId: props.conversationId, conversationId: props.conversationId,

View File

@@ -5,6 +5,7 @@ import React, { type RefObject } from 'react';
import { ContextMenu, MenuItem } from 'react-contextmenu'; import { ContextMenu, MenuItem } from 'react-contextmenu';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import type { LocalizerType } from '../../types/I18N'; import type { LocalizerType } from '../../types/I18N';
import type { InteractionModeType } from '../../state/ducks/conversations';
export type ContextMenuTriggerType = { export type ContextMenuTriggerType = {
handleContextClick: ( handleContextClick: (
@@ -16,7 +17,7 @@ type MessageContextProps = {
i18n: LocalizerType; i18n: LocalizerType;
triggerId: string; triggerId: string;
shouldShowAdditional: boolean; shouldShowAdditional: boolean;
interactionMode: InteractionModeType;
onDownload: (() => void) | undefined; onDownload: (() => void) | undefined;
onEdit: (() => void) | undefined; onEdit: (() => void) | undefined;
onReplyToMessage: (() => void) | undefined; onReplyToMessage: (() => void) | undefined;
@@ -33,6 +34,7 @@ export const MessageContextMenu = ({
i18n, i18n,
triggerId, triggerId,
shouldShowAdditional, shouldShowAdditional,
interactionMode,
onDownload, onDownload,
onEdit, onEdit,
onReplyToMessage, onReplyToMessage,
@@ -46,7 +48,14 @@ export const MessageContextMenu = ({
onDeleteMessage, onDeleteMessage,
}: MessageContextProps): JSX.Element => { }: MessageContextProps): JSX.Element => {
const menu = ( const menu = (
<ContextMenu id={triggerId}> // We avoid restoring focus on this context menu because it is not intended for
// keyboard use and restoring focus to the message could cause an unwanted scroll
<ContextMenu
id={triggerId}
// In keyboard mode, we do want to restore focus to the message; the message is very
// likely already scrolled into view in this case.
avoidFocusRestoreOnBlur={interactionMode !== 'keyboard'}
>
{shouldShowAdditional && ( {shouldShowAdditional && (
<> <>
{onDownload && ( {onDownload && (

View File

@@ -286,6 +286,7 @@ export const TimelineItem = memo(function TimelineItem({
<CallingNotification <CallingNotification
id={id} id={id}
conversationId={conversationId} conversationId={conversationId}
interactionMode={reducedProps.interactionMode}
i18n={i18n} i18n={i18n}
isNextItemCallingNotification={isNextItemCallingNotification} isNextItemCallingNotification={isNextItemCallingNotification}
onOutgoingAudioCallInConversation={onOutgoingAudioCallInConversation} onOutgoingAudioCallInConversation={onOutgoingAudioCallInConversation}

View File

@@ -365,6 +365,7 @@ export function TimelineMessage(props: Props): JSX.Element {
i18n={i18n} i18n={i18n}
triggerId={triggerId} triggerId={triggerId}
shouldShowAdditional={shouldShowAdditional} shouldShowAdditional={shouldShowAdditional}
interactionMode={props.interactionMode}
onDownload={handleDownload} onDownload={handleDownload}
onEdit={ onEdit={
canEditMessage canEditMessage