diff --git a/CHANGELOG.md b/CHANGELOG.md index b7001574d8b6..96cd7d7a10f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Issues can be created many times when initial submit () - Running deep learning models on non-jpeg compressed tif images () - Paddings on tasks/projects/models pages () +- Hotkeys handlers triggered instead of default behaviour with focus when modal windows opened + () +- Need to move a mouse to get brush/eraser effect, just click not enough () - Memory leak in the logging system () - A race condition during initial `secret_key.py` creation () diff --git a/cvat-canvas/src/typescript/masksHandler.ts b/cvat-canvas/src/typescript/masksHandler.ts index e884bef766f1..dfb9d6b11291 100644 --- a/cvat-canvas/src/typescript/masksHandler.ts +++ b/cvat-canvas/src/typescript/masksHandler.ts @@ -359,6 +359,8 @@ export class MasksHandlerImpl implements MasksHandler { if (!continueInserting) { this.releasePaste(); } + } else { + this.canvas.fire('mouse:move', options); } }); diff --git a/cvat-ui/src/actions/auth-actions.ts b/cvat-ui/src/actions/auth-actions.ts index aaf8d9929789..94d01aade7f7 100644 --- a/cvat-ui/src/actions/auth-actions.ts +++ b/cvat-ui/src/actions/auth-actions.ts @@ -54,8 +54,8 @@ export const authActions = { changePassword: () => createAction(AuthActionTypes.CHANGE_PASSWORD), changePasswordSuccess: () => createAction(AuthActionTypes.CHANGE_PASSWORD_SUCCESS), changePasswordFailed: (error: any) => createAction(AuthActionTypes.CHANGE_PASSWORD_FAILED, { error }), - switchChangePasswordDialog: (showChangePasswordDialog: boolean) => ( - createAction(AuthActionTypes.SWITCH_CHANGE_PASSWORD_DIALOG, { showChangePasswordDialog }) + switchChangePasswordModalVisible: (visible: boolean) => ( + createAction(AuthActionTypes.SWITCH_CHANGE_PASSWORD_DIALOG, { visible }) ), requestPasswordReset: () => createAction(AuthActionTypes.REQUEST_PASSWORD_RESET), requestPasswordResetSuccess: () => createAction(AuthActionTypes.REQUEST_PASSWORD_RESET_SUCCESS), diff --git a/cvat-ui/src/actions/settings-actions.ts b/cvat-ui/src/actions/settings-actions.ts index 0e37fe7d3da5..82a7a0e73259 100644 --- a/cvat-ui/src/actions/settings-actions.ts +++ b/cvat-ui/src/actions/settings-actions.ts @@ -327,12 +327,10 @@ export function changeCanvasBackgroundColor(color: string): AnyAction { }; } -export function switchSettingsDialog(show?: boolean): AnyAction { +export function switchSettingsModalVisible(visible: boolean): AnyAction { return { type: SettingsActionTypes.SWITCH_SETTINGS_DIALOG, - payload: { - show, - }, + payload: { visible }, }; } diff --git a/cvat-ui/src/actions/shortcuts-actions.ts b/cvat-ui/src/actions/shortcuts-actions.ts index 8f35d0a57e63..ddcfc040ec52 100644 --- a/cvat-ui/src/actions/shortcuts-actions.ts +++ b/cvat-ui/src/actions/shortcuts-actions.ts @@ -1,6 +1,8 @@ // Copyright (C) 2020-2022 Intel Corporation +// Copyright (C) 2023 CVAT.ai Corporation // // SPDX-License-Identifier: MIT + import { ActionUnion, createAction } from 'utils/redux'; export enum ShortcutsActionsTypes { @@ -8,7 +10,9 @@ export enum ShortcutsActionsTypes { } export const shortcutsActions = { - switchShortcutsDialog: () => createAction(ShortcutsActionsTypes.SWITCH_SHORTCUT_DIALOG), + switchShortcutsModalVisible: (visible: boolean) => ( + createAction(ShortcutsActionsTypes.SWITCH_SHORTCUT_DIALOG, { visible }) + ), }; export type ShortcutsActions = ActionUnion; diff --git a/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx b/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx index b90f610c2016..48d90fd25ef6 100644 --- a/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx +++ b/cvat-ui/src/components/annotation-page/review/create-issue-dialog.tsx @@ -43,7 +43,7 @@ function MessageForm(props: FormProps): JSX.Element { name='issue_description' rules={[{ required: true, message: 'Please, fill out the field' }]} > - + diff --git a/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx b/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx index edfb5b01a8bb..3b4d1954ab79 100644 --- a/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx +++ b/cvat-ui/src/components/annotation-page/review/issue-dialog.tsx @@ -93,8 +93,8 @@ export default function IssueDialog(props: Props): JSX.Element { }, okButtonProps: { type: 'primary', - danger: true, }, + autoFocusButton: 'cancel', okText: 'Delete', }); }, []); diff --git a/cvat-ui/src/components/annotation-page/standard-workspace/remove-confirm.tsx b/cvat-ui/src/components/annotation-page/standard-workspace/remove-confirm.tsx index 06086fa3431c..c86d11c0266b 100644 --- a/cvat-ui/src/components/annotation-page/standard-workspace/remove-confirm.tsx +++ b/cvat-ui/src/components/annotation-page/standard-workspace/remove-confirm.tsx @@ -68,9 +68,13 @@ export default function RemoveConfirmComponent(): JSX.Element | null { cancelText='Cancel' title={title} visible={visible} + cancelButtonProps={{ + autoFocus: true, + }} onOk={onOk} onCancel={onCancel} - className='cvat-modal-confirm' + destroyOnClose + className='cvat-modal-confirm-remove-object' >
{description} diff --git a/cvat-ui/src/components/cvat-app.tsx b/cvat-ui/src/components/cvat-app.tsx index b2a7938ddc18..986793042fd9 100644 --- a/cvat-ui/src/components/cvat-app.tsx +++ b/cvat-ui/src/components/cvat-app.tsx @@ -60,7 +60,6 @@ import GuidePage from 'components/md-guide/guide-page'; import AnnotationPageContainer from 'containers/annotation-page/annotation-page'; import { getCore } from 'cvat-core-wrapper'; -import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react'; import { NotificationsState, PluginsState } from 'reducers'; import { customWaViewHit } from 'utils/environment'; import showPlatformNotification, { @@ -88,11 +87,8 @@ interface CVATAppProps { initModels: () => void; resetErrors: () => void; resetMessages: () => void; - switchShortcutsDialog: () => void; - switchSettingsDialog: () => void; loadAuthActions: () => void; loadOrganizations: () => void; - keyMap: KeyMap; userInitialized: boolean; userFetching: boolean; organizationsFetching: boolean; @@ -137,7 +133,6 @@ class CVATApplication extends React.PureComponent void)[] = []; @@ -396,11 +391,8 @@ class CVATApplication extends React.PureComponent { - if (event) event.preventDefault(); - - switchShortcutsDialog(); - }, - SWITCH_SETTINGS: (event: KeyboardEvent) => { - if (event) event.preventDefault(); - - switchSettingsDialog(); - }, - }; - const routesToRender = pluginComponents.router .filter(({ data: { shouldBeRendered } }) => shouldBeRendered(this.props, this.state)) .map(({ component: Component }) => Component()); @@ -455,61 +429,59 @@ class CVATApplication extends React.PureComponent - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + { routesToRender } + {isModelPluginActive && ( - - - - - { routesToRender } - {isModelPluginActive && ( - - - - - - - )} - - - + path='/models' + > + + + + + + )} + + diff --git a/cvat-ui/src/components/header/header.tsx b/cvat-ui/src/components/header/header.tsx index 4eb45aead988..0fd5be0b7c26 100644 --- a/cvat-ui/src/components/header/header.tsx +++ b/cvat-ui/src/components/header/header.tsx @@ -35,34 +35,21 @@ import config from 'config'; import { CVATLogo } from 'icons'; import ChangePasswordDialog from 'components/change-password-modal/change-password-modal'; import CVATTooltip from 'components/common/cvat-tooltip'; -import { switchSettingsDialog as switchSettingsDialogAction } from 'actions/settings-actions'; +import { switchSettingsModalVisible as switchSettingsModalVisibleAction } from 'actions/settings-actions'; import { logoutAsync, authActions } from 'actions/auth-actions'; -import { CombinedState } from 'reducers'; +import { shortcutsActions } from 'actions/shortcuts-actions'; +import { AboutState, CombinedState } from 'reducers'; import { usePlugins } from 'utils/hooks'; +import GlobalHotKeys, { KeyMap } from 'utils/mousetrap-react'; import SettingsModal from './settings-modal/settings-modal'; -interface Tool { - name: string; - description: string; - server: { - version: string; - }; - core: { - version: string; - }; - canvas: { - version: string; - }; - ui: { - version: string; - }; -} - interface StateToProps { user: any; - tool: Tool; + about: AboutState; + keyMap: KeyMap; switchSettingsShortcut: string; - settingsDialogShown: boolean; + settingsModalVisible: boolean; + shortcutsModalVisible: boolean; changePasswordDialogShown: boolean; changePasswordFetching: boolean; logoutFetching: boolean; @@ -77,8 +64,9 @@ interface StateToProps { interface DispatchToProps { onLogout: () => void; - switchSettingsDialog: (show: boolean) => void; - switchChangePasswordDialog: (show: boolean) => void; + switchSettingsModalVisible: (visible: boolean) => void; + switchShortcutsModalVisible: (visible: boolean) => void; + switchChangePasswordModalVisible: (visible: boolean) => void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -91,32 +79,19 @@ function mapStateToProps(state: CombinedState): StateToProps { allowChangePassword: renderChangePasswordItem, }, plugins: { list }, - about: { server, packageVersion }, - shortcuts: { normalizedKeyMap }, - settings: { showDialog: settingsDialogShown }, + about, + shortcuts: { normalizedKeyMap, keyMap, visibleShortcutsHelp: shortcutsModalVisible }, + settings: { showDialog: settingsModalVisible }, organizations: { fetching: organizationsFetching, current: currentOrganization, list: organizationsList }, } = state; return { user, - tool: { - name: server.name as string, - description: server.description as string, - server: { - version: server.version as string, - }, - canvas: { - version: packageVersion.canvas, - }, - core: { - version: packageVersion.core, - }, - ui: { - version: packageVersion.ui, - }, - }, + about, switchSettingsShortcut: normalizedKeyMap.SWITCH_SETTINGS, - settingsDialogShown, + keyMap, + settingsModalVisible, + shortcutsModalVisible, changePasswordDialogShown, changePasswordFetching, logoutFetching, @@ -133,29 +108,39 @@ function mapStateToProps(state: CombinedState): StateToProps { function mapDispatchToProps(dispatch: any): DispatchToProps { return { onLogout: (): void => dispatch(logoutAsync()), - switchSettingsDialog: (show: boolean): void => dispatch(switchSettingsDialogAction(show)), - switchChangePasswordDialog: (show: boolean): void => dispatch(authActions.switchChangePasswordDialog(show)), + switchShortcutsModalVisible: (visible: boolean): void => dispatch( + shortcutsActions.switchShortcutsModalVisible(visible), + ), + switchSettingsModalVisible: (visible: boolean): void => dispatch( + switchSettingsModalVisibleAction(visible), + ), + switchChangePasswordModalVisible: (visible: boolean): void => dispatch( + authActions.switchChangePasswordModalVisible(visible), + ), }; } type Props = StateToProps & DispatchToProps; -function HeaderContainer(props: Props): JSX.Element { +function HeaderComponent(props: Props): JSX.Element { const { user, - tool, + about, + keyMap, logoutFetching, changePasswordFetching, - settingsDialogShown, + settingsModalVisible, + shortcutsModalVisible, switchSettingsShortcut, - switchSettingsDialog, - switchChangePasswordDialog, renderChangePasswordItem, isAnalyticsPluginActive, isModelsPluginActive, organizationsFetching, currentOrganization, organizationsList, + switchSettingsModalVisible, + switchShortcutsModalVisible, + switchChangePasswordModalVisible, } = props; const { @@ -165,27 +150,47 @@ function HeaderContainer(props: Props): JSX.Element { const history = useHistory(); const location = useLocation(); + const subKeyMap = { + SWITCH_SHORTCUTS: keyMap.SWITCH_SHORTCUTS, + SWITCH_SETTINGS: keyMap.SWITCH_SETTINGS, + }; + + const handlers = { + SWITCH_SHORTCUTS: (event: KeyboardEvent) => { + if (event) event.preventDefault(); + if (!settingsModalVisible) { + switchShortcutsModalVisible(!shortcutsModalVisible); + } + }, + SWITCH_SETTINGS: (event: KeyboardEvent) => { + if (event) event.preventDefault(); + if (!shortcutsModalVisible) { + switchSettingsModalVisible(!settingsModalVisible); + } + }, + }; + const showAboutModal = useCallback((): void => { Modal.info({ - title: `${tool.name}`, + title: `${about.server.name}`, content: (
-

{`${tool.description}`}

+

{`${about.server.description}`}

Server version: - {` ${tool.server.version}`} + {` ${about.server.version}`}

Core version: - {` ${tool.core.version}`} + {` ${about.packageVersion.core}`}

Canvas version: - {` ${tool.canvas.version}`} + {` ${about.packageVersion.canvas}`}

UI version: - {` ${tool.ui.version}`} + {` ${about.packageVersion.ui}`}

@@ -218,10 +223,10 @@ function HeaderContainer(props: Props): JSX.Element { }, }, }); - }, [tool]); + }, [about]); const closeSettings = useCallback(() => { - switchSettingsDialog(false); + switchSettingsModalVisible(false); }, []); const resetOrganization = (): void => { @@ -347,7 +352,7 @@ function HeaderContainer(props: Props): JSX.Element { icon={} key='settings' title={`Press ${switchSettingsShortcut} to switch`} - onClick={() => switchSettingsDialog(true)} + onClick={() => switchSettingsModalVisible(true)} > Settings @@ -365,7 +370,7 @@ function HeaderContainer(props: Props): JSX.Element { key='change_password' icon={changePasswordFetching ? : } className='cvat-header-menu-change-password' - onClick={(): void => switchChangePasswordDialog(true)} + onClick={(): void => switchChangePasswordModalVisible(true)} disabled={changePasswordFetching} > Change password @@ -409,6 +414,7 @@ function HeaderContainer(props: Props): JSX.Element { return ( +
- - {renderChangePasswordItem && switchChangePasswordDialog(false)} />} + + {renderChangePasswordItem && ( + switchChangePasswordModalVisible(false)} /> + )}
); } -function propsAreTheSame(prevProps: Props, nextProps: Props): boolean { - let equal = true; - for (const prop in nextProps) { - if (prop in prevProps && (prevProps as any)[prop] !== (nextProps as any)[prop]) { - if (prop !== 'tool') { - equal = false; - } - } - } - - return equal; -} - -export default connect(mapStateToProps, mapDispatchToProps)(React.memo(HeaderContainer, propsAreTheSame)); +export default connect(mapStateToProps, mapDispatchToProps)(React.memo(HeaderComponent)); diff --git a/cvat-ui/src/components/header/styles.scss b/cvat-ui/src/components/header/styles.scss index e67fbd498e9a..42f54ccc54cd 100644 --- a/cvat-ui/src/components/header/styles.scss +++ b/cvat-ui/src/components/header/styles.scss @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: MIT -@import '../../base.scss'; +@import '../../base'; .cvat-header.ant-layout-header { display: flex; @@ -94,3 +94,10 @@ .cvat-modal-organization-selector { width: 100%; } + +.cvat-shortcuts-modal-window-table { + .ant-table { + max-height: $grid-unit-size * 70; + overflow-y: auto; + } +} diff --git a/cvat-ui/src/components/shortcuts-dialog/shortcuts-dialog.tsx b/cvat-ui/src/components/shortcuts-dialog/shortcuts-dialog.tsx index 48967c33f818..bd88206bf5ba 100644 --- a/cvat-ui/src/components/shortcuts-dialog/shortcuts-dialog.tsx +++ b/cvat-ui/src/components/shortcuts-dialog/shortcuts-dialog.tsx @@ -16,7 +16,7 @@ interface StateToProps { } interface DispatchToProps { - switchShortcutsDialog(): void; + switchShortcutsModalVisible(visible: boolean): void; } function mapStateToProps(state: CombinedState): StateToProps { @@ -27,22 +27,19 @@ function mapStateToProps(state: CombinedState): StateToProps { }, } = state; - return { - visible, - jobInstance, - }; + return { visible, jobInstance }; } function mapDispatchToProps(dispatch: any): DispatchToProps { return { - switchShortcutsDialog(): void { - dispatch(shortcutsActions.switchShortcutsDialog()); + switchShortcutsModalVisible(visible: boolean): void { + dispatch(shortcutsActions.switchShortcutsModalVisible(visible)); }, }; } function ShortcutsDialog(props: StateToProps & DispatchToProps): JSX.Element | null { - const { visible, switchShortcutsDialog, jobInstance } = props; + const { visible, switchShortcutsModalVisible, jobInstance } = props; const keyMap = getApplicationKeyMap(); const splitToRows = (data: string[]): JSX.Element[] => data.map( @@ -97,7 +94,7 @@ function ShortcutsDialog(props: StateToProps & DispatchToProps): JSX.Element | n visible={visible} closable={false} width={800} - onOk={switchShortcutsDialog} + onOk={() => switchShortcutsModalVisible(false)} cancelButtonProps={{ style: { display: 'none' } }} zIndex={1001} /* default antd is 1000 */ className='cvat-shortcuts-modal-window' diff --git a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx index 067d85fe1ad0..a034f9799665 100644 --- a/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx +++ b/cvat-ui/src/containers/annotation-page/top-bar/top-bar.tsx @@ -528,6 +528,14 @@ class AnnotationTopBarContainer extends React.PureComponent { restoreFrame(frameNumber); }; + private changeWorkspace = (workspace: Workspace): void => { + const { changeWorkspace } = this.props; + changeWorkspace(workspace); + if (window.document.activeElement) { + (window.document.activeElement as HTMLElement).blur(); + } + }; + private beforeUnloadCallback = (event: BeforeUnloadEvent): string | undefined => { const { jobInstance, forceExit, setForceExitAnnotationFlag } = this.props; if (jobInstance.annotations.hasUnsavedChanges() && !forceExit) { @@ -645,7 +653,6 @@ class AnnotationTopBarContainer extends React.PureComponent { normalizedKeyMap, activeControl, searchAnnotations, - changeWorkspace, switchNavigationBlocked, toolsBlockerState, } = this.props; @@ -765,7 +772,7 @@ class AnnotationTopBarContainer extends React.PureComponent { onURLIconClick={this.onURLIconClick} onDeleteFrame={this.onDeleteFrame} onRestoreFrame={this.onRestoreFrame} - changeWorkspace={changeWorkspace} + changeWorkspace={this.changeWorkspace} switchNavigationBlocked={switchNavigationBlocked} workspace={workspace} playing={playing} diff --git a/cvat-ui/src/index.tsx b/cvat-ui/src/index.tsx index 789825fb0256..067a647b379f 100644 --- a/cvat-ui/src/index.tsx +++ b/cvat-ui/src/index.tsx @@ -13,15 +13,12 @@ import { authorizedAsync, loadAuthActionsAsync } from 'actions/auth-actions'; import { getFormatsAsync } from 'actions/formats-actions'; import { getModelsAsync } from 'actions/models-actions'; import { getPluginsAsync } from 'actions/plugins-actions'; -import { switchSettingsDialog } from 'actions/settings-actions'; -import { shortcutsActions } from 'actions/shortcuts-actions'; import { getUserAgreementsAsync } from 'actions/useragreements-actions'; import CVATApplication from 'components/cvat-app'; import PluginsEntrypoint from 'components/plugins-entrypoint'; import LayoutGrid from 'components/layout-grid/layout-grid'; import logger, { LogType } from 'cvat-logger'; import createCVATStore, { getCVATStore } from 'cvat-store'; -import { KeyMap } from 'utils/mousetrap-react'; import createRootReducer from 'reducers/root-reducer'; import { getOrganizationsAsync } from 'actions/organization-actions'; import { resetErrors, resetMessages } from 'actions/notification-actions'; @@ -52,7 +49,6 @@ interface StateToProps { allowResetPassword: boolean; notifications: NotificationsState; user: any; - keyMap: KeyMap; isModelPluginActive: boolean; pluginComponents: PluginsState['components']; } @@ -65,9 +61,7 @@ interface DispatchToProps { initPlugins: () => void; resetErrors: () => void; resetMessages: () => void; - switchShortcutsDialog: () => void; loadUserAgreements: () => void; - switchSettingsDialog: () => void; loadAuthActions: () => void; loadOrganizations: () => void; } @@ -77,7 +71,6 @@ function mapStateToProps(state: CombinedState): StateToProps { const { auth } = state; const { formats } = state; const { about } = state; - const { shortcuts } = state; const { userAgreements } = state; const { models } = state; const { organizations } = state; @@ -103,7 +96,6 @@ function mapStateToProps(state: CombinedState): StateToProps { allowResetPassword: auth.allowResetPassword, notifications: state.notifications, user: auth.user, - keyMap: shortcuts.keyMap, pluginComponents: plugins.components, isModelPluginActive: plugins.list.MODELS, }; @@ -119,8 +111,6 @@ function mapDispatchToProps(dispatch: any): DispatchToProps { loadAbout: (): void => dispatch(getAboutAsync()), resetErrors: (): void => dispatch(resetErrors()), resetMessages: (): void => dispatch(resetMessages()), - switchShortcutsDialog: (): void => dispatch(shortcutsActions.switchShortcutsDialog()), - switchSettingsDialog: (): void => dispatch(switchSettingsDialog()), loadAuthActions: (): void => dispatch(loadAuthActionsAsync()), loadOrganizations: (): void => dispatch(getOrganizationsAsync()), }; diff --git a/cvat-ui/src/reducers/auth-reducer.ts b/cvat-ui/src/reducers/auth-reducer.ts index 7d9527f3d62c..a2f3c47ff260 100644 --- a/cvat-ui/src/reducers/auth-reducer.ts +++ b/cvat-ui/src/reducers/auth-reducer.ts @@ -99,10 +99,7 @@ export default function (state = defaultState, action: AuthActions | BoundariesA case AuthActionTypes.SWITCH_CHANGE_PASSWORD_DIALOG: return { ...state, - showChangePasswordDialog: - typeof action.payload.showChangePasswordDialog === 'undefined' ? - !state.showChangePasswordDialog : - action.payload.showChangePasswordDialog, + showChangePasswordDialog: action.payload.visible, }; case AuthActionTypes.REQUEST_PASSWORD_RESET: return { diff --git a/cvat-ui/src/reducers/settings-reducer.ts b/cvat-ui/src/reducers/settings-reducer.ts index e732f26a7e7d..101c78f63832 100644 --- a/cvat-ui/src/reducers/settings-reducer.ts +++ b/cvat-ui/src/reducers/settings-reducer.ts @@ -367,7 +367,7 @@ export default (state = defaultState, action: AnyAction): SettingsState => { case SettingsActionTypes.SWITCH_SETTINGS_DIALOG: { return { ...state, - showDialog: typeof action.payload.show === 'undefined' ? !state.showDialog : action.payload.show, + showDialog: action.payload.visible, }; } case SettingsActionTypes.SET_SETTINGS: { diff --git a/cvat-ui/src/reducers/shortcuts-reducer.ts b/cvat-ui/src/reducers/shortcuts-reducer.ts index c2c18f78ee5b..27ec835564ef 100644 --- a/cvat-ui/src/reducers/shortcuts-reducer.ts +++ b/cvat-ui/src/reducers/shortcuts-reducer.ts @@ -452,7 +452,7 @@ export default (state = defaultState, action: ShortcutsActions | BoundariesActio case ShortcutsActionsTypes.SWITCH_SHORTCUT_DIALOG: { return { ...state, - visibleShortcutsHelp: !state.visibleShortcutsHelp, + visibleShortcutsHelp: action.payload.visible, }; } case BoundariesActionTypes.RESET_AFTER_ERROR: diff --git a/cvat-ui/src/utils/mousetrap-react.tsx b/cvat-ui/src/utils/mousetrap-react.tsx index 3e5225071198..690fb3c4cd31 100644 --- a/cvat-ui/src/utils/mousetrap-react.tsx +++ b/cvat-ui/src/utils/mousetrap-react.tsx @@ -52,6 +52,21 @@ export default function GlobalHotKeys(props: Props): JSX.Element { return children || <>; } +Mousetrap.prototype.stopCallback = function (e: KeyboardEvent, element: Element, combo: string): boolean { + // stop when modals are opened + const someModalsOpened = Array.from( + window.document.getElementsByClassName('ant-modal'), + ).some((el) => (el as HTMLElement).style.display !== 'none'); + if (someModalsOpened && !['f1', 'f2'].includes(combo)) { + return true; + } + + // stop for input, select, and textarea + return element.tagName === 'INPUT' || + element.tagName === 'SELECT' || + element.tagName === 'TEXTAREA'; +}; + export function getApplicationKeyMap(): KeyMap { return { ...applicationKeyMap, diff --git a/tests/cypress/e2e/actions_objects/case_24_delete_unlock_lock_object.js b/tests/cypress/e2e/actions_objects/case_24_delete_unlock_lock_object.js index 288388e0b207..435962f91d7a 100644 --- a/tests/cypress/e2e/actions_objects/case_24_delete_unlock_lock_object.js +++ b/tests/cypress/e2e/actions_objects/case_24_delete_unlock_lock_object.js @@ -25,12 +25,10 @@ context('Delete unlock/lock object', () => { }); } - function deleteObjectViaShortcut(shortcut, stateLockObject) { - if (stateLockObject === 'unlock') { - cy.get('.cvat-canvas-container').within(() => { - cy.get('.cvat_canvas_shape').trigger('mousemove').should('have.class', 'cvat_canvas_shape_activated'); - }); - } + function deleteObjectViaShortcut(shortcut) { + cy.get('body').click(); + cy.get('.cvat-objects-sidebar-state-item').trigger('mouseover'); + cy.get('.cvat-objects-sidebar-state-item').should('have.class', 'cvat-objects-sidebar-state-active-item'); cy.get('body').type(shortcut); } @@ -60,7 +58,7 @@ context('Delete unlock/lock object', () => { } function actionOnConfirmWindow(textBuntton) { - cy.get('.cvat-modal-confirm').within(() => { + cy.get('.cvat-modal-confirm-remove-object').within(() => { cy.contains(new RegExp(`^${textBuntton}$`, 'g')).click(); }); } @@ -71,11 +69,12 @@ context('Delete unlock/lock object', () => { } function checkFailDeleteLockObject(shortcut) { - deleteObjectViaShortcut(shortcut, 'lock'); + deleteObjectViaShortcut(shortcut); checkExistObject('exist'); - cy.get('.cvat-modal-confirm').should('exist'); - cy.get('.cvat-modal-confirm').within(() => { + cy.get('.cvat-modal-confirm-remove-object').should('exist'); + cy.get('.cvat-modal-confirm-remove-object').within(() => { cy.contains('Cancel').click(); + cy.get('.cvat-modal-confirm-remove-object').should('not.exist'); }); } @@ -86,7 +85,7 @@ context('Delete unlock/lock object', () => { describe(`Testing case "${caseId}"`, () => { it('Create and delete object via "Delete" shortcut', () => { cy.createRectangle(createRectangleShape2Points); - deleteObjectViaShortcut('{del}', 'unlock'); + deleteObjectViaShortcut('{del}'); checkExistObject('not.exist'); }); @@ -100,7 +99,7 @@ context('Delete unlock/lock object', () => { cy.createRectangle(createRectangleShape2Points); lockObject(); checkFailDeleteLockObject('{del}'); - deleteObjectViaShortcut('{shift}{del}', 'lock'); + deleteObjectViaShortcut('{shift}{del}'); checkExistObject('not.exist'); });