diff --git a/apps/files/src/components/FilesListTableHeader.vue b/apps/files/src/components/FilesListTableHeader.vue index c8334abda5e85..595b5b454b163 100644 --- a/apps/files/src/components/FilesListTableHeader.vue +++ b/apps/files/src/components/FilesListTableHeader.vue @@ -69,6 +69,7 @@ import { useFilesStore } from '../store/files.ts' import { useSelectionStore } from '../store/selection.ts' import filesSortingMixin from '../mixins/filesSorting.ts' import logger from '../logger.ts' +import { isDialogOpen } from '../utils/dialogUtils.ts' export default defineComponent({ name: 'FilesListTableHeader', @@ -155,6 +156,14 @@ export default defineComponent({ }, }, + beforeMount() { + document.addEventListener('keydown', this.onKeyDown) + }, + + beforeDestroy() { + document.removeEventListener('keydown', this.onKeyDown) + }, + methods: { ariaSortForMode(mode: string): ARIAMixin['ariaSort'] { if (this.sortingMode === mode) { @@ -172,6 +181,28 @@ export default defineComponent({ } }, + onKeyDown(event: KeyboardEvent) { + // Don't react to the event if a dialog is open + if (isDialogOpen()) { + return + } + + // Escape key cancels selection + if (event.key === 'Escape' && (this.isSomeSelected || this.isAllSelected)) { + this.resetSelection() + event.preventDefault() + event.stopPropagation() + return + } + + // ctrl+a selects all + if (event.key === 'a' && event.ctrlKey) { + this.onToggleAll(true) + event.preventDefault() + event.stopPropagation() + } + }, + onToggleAll(selected) { if (selected) { const selection = this.nodes.map(node => node.source).filter(Boolean) as FileSource[] diff --git a/apps/files/src/utils/dialogUtils.ts b/apps/files/src/utils/dialogUtils.ts new file mode 100644 index 0000000000000..2384aa476cfb2 --- /dev/null +++ b/apps/files/src/utils/dialogUtils.ts @@ -0,0 +1,14 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +export function isDialogOpen() { + // Select all elements with role="dialog" + const dialogs = document.querySelectorAll('[role="dialog"]') + + // Check if any dialog is visible + return Array.from(dialogs).some(dialog => { + const style = window.getComputedStyle(dialog) + return style.display !== 'none' && style.visibility !== 'hidden' + }) +}