Skip to content

Commit

Permalink
chore(files): use useHotKey for keyboard shortcuts events registrat…
Browse files Browse the repository at this point in the history
…ions

Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
  • Loading branch information
skjnldsv committed Nov 29, 2024
1 parent 91f9f28 commit 0eaa520
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 222 deletions.
36 changes: 10 additions & 26 deletions apps/files/src/components/FileEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@
<script lang="ts">
import { defineComponent } from 'vue'
import { formatFileSize } from '@nextcloud/files'
import { useHotKey } from '@nextcloud/vue/dist/Composables/useHotKey.js'
import moment from '@nextcloud/moment'
import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'

import { useNavigation } from '../composables/useNavigation.ts'
import { useFileListWidth } from '../composables/useFileListWidth.ts'
Expand All @@ -97,14 +99,12 @@ import { useFilesStore } from '../store/files.ts'
import { useRenamingStore } from '../store/renaming.ts'
import { useSelectionStore } from '../store/selection.ts'

import FileEntryMixin from './FileEntryMixin.ts'
import NcDateTime from '@nextcloud/vue/dist/Components/NcDateTime.js'
import CustomElementRender from './CustomElementRender.vue'
import FileEntryActions from './FileEntry/FileEntryActions.vue'
import FileEntryCheckbox from './FileEntry/FileEntryCheckbox.vue'
import FileEntryMixin from './FileEntryMixin.ts'
import FileEntryName from './FileEntry/FileEntryName.vue'
import FileEntryPreview from './FileEntry/FileEntryPreview.vue'
import { isDialogOpened } from '../utils/dialogUtils.ts'

export default defineComponent({
name: 'FileEntry',
Expand Down Expand Up @@ -229,39 +229,23 @@ export default defineComponent({
},
},

beforeMount() {
document.addEventListener('keydown', this.onKeyDown)
},

beforeDestroy() {
document.removeEventListener('keydown', this.onKeyDown)
created() {
useHotKey('Enter', this.triggerDefaultAction, {
stop: true,
prevent: true,
})
},

methods: {
formatFileSize,

onKeyDown(event: KeyboardEvent) {
// Don't react to the event if a dialog is open
if (isDialogOpened()) {
return
}

// Don't react if ctrl, meta or alt key is pressed, we don't need those here
if (event.ctrlKey || event.altKey || event.metaKey) {
return
}

triggerDefaultAction() {
// Don't react to the event if the file row is not active
if (!this.isActive) {
return
}

// Enter opens the file
if (event.key === 'Enter') {
event.preventDefault()
event.stopPropagation()
this.defaultFileAction?.exec(this.source, this.currentView, this.currentDir)
}
this.defaultFileAction?.exec(this.source, this.currentView, this.currentDir)
},
},
})
Expand Down
76 changes: 39 additions & 37 deletions apps/files/src/components/FileEntry/FileEntryActions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,28 +81,28 @@ import type { PropType } from 'vue'
import type { FileAction, Node } from '@nextcloud/files'

import { DefaultType, NodeStatus } from '@nextcloud/files'
import { defineComponent, inject } from 'vue'
import { showError, showSuccess } from '@nextcloud/dialogs'
import { translate as t } from '@nextcloud/l10n'
import { defineComponent, inject } from 'vue'

import { useHotKey } from '@nextcloud/vue/dist/Composables/useHotKey.js'
import ArrowLeftIcon from 'vue-material-design-icons/ArrowLeft.vue'
import CustomElementRender from '../CustomElementRender.vue'
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcActions from '@nextcloud/vue/dist/Components/NcActions.js'
import NcActionSeparator from '@nextcloud/vue/dist/Components/NcActionSeparator.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import ArrowLeftIcon from 'vue-material-design-icons/ArrowLeft.vue'
import CustomElementRender from '../CustomElementRender.vue'

import { isDialogOpened } from '../../utils/dialogUtils.ts'
import { ACTION_DELETE } from '../../actions/deleteAction.ts'
import { ACTION_DETAILS } from '../../actions/sidebarAction.ts'
import { ACTION_FAVORITE } from '../../actions/favoriteAction.ts'
import { ACTION_RENAME } from '../../actions/renameAction.ts'
import { useActiveStore } from '../../store/active.ts'
import { useFileListWidth } from '../../composables/useFileListWidth.ts'
import { useNavigation } from '../../composables/useNavigation'
import { useRouteParameters } from '../../composables/useRouteParameters.ts'
import logger from '../../logger.ts'
import { ACTION_DETAILS } from '../../actions/sidebarAction.ts'
import { ACTION_RENAME } from '../../actions/renameAction.ts'
import { ACTION_DELETE } from '../../actions/deleteAction.ts'
import { ACTION_FAVORITE } from '../../actions/favoriteAction.ts'
import { useActiveStore } from '../../store/active.ts'

export default defineComponent({
name: 'FileEntryActions',
Expand Down Expand Up @@ -245,12 +245,36 @@ export default defineComponent({
},
},

beforeMount() {
document.addEventListener('keydown', this.onKeyDown)
},

beforeDestroy() {
document.removeEventListener('keydown', this.onKeyDown)
created() {
useHotKey('Escape', this.onKeyDown, {
stop: true,
prevent: true,
})

useHotKey('a', this.onKeyDown, {
stop: true,
prevent: true,
})

useHotKey('d', this.onKeyDown, {
stop: true,
prevent: true,
})

useHotKey('F2', this.onKeyDown, {
stop: true,
prevent: true,
})

useHotKey('Delete', this.onKeyDown, {
stop: true,
prevent: true,
})

useHotKey('s', this.onKeyDown, {
stop: true,
prevent: true,
})
},

methods: {
Expand Down Expand Up @@ -332,60 +356,38 @@ export default defineComponent({
},

onKeyDown(event: KeyboardEvent) {
// Don't react to the event if a dialog is open
if (isDialogOpened()) {
return
}

// Don't react if ctrl, meta or alt key is pressed, we don't need those here
if (event.ctrlKey || event.altKey || event.metaKey) {
return
}

// Don't react to the event if the file row is not active
if (!this.isActive) {
return
}

// ESC close the action menu if opened
if (event.key === 'Escape' && this.openedMenu) {
event.preventDefault()
event.stopPropagation()
this.openedMenu = false
}

// a open the action menu
if (event.key === 'a' && !this.openedMenu) {
event.preventDefault()
event.stopPropagation()
this.openedMenu = true
}

// d opens the sidebar
if (event.key === 'd') {
event.preventDefault()
event.stopPropagation()
this.onActionClick(this.enabledFileActions.find(action => action.id === ACTION_DETAILS)!)
}

// F2 renames the file
if (event.key === 'F2') {
event.preventDefault()
event.stopPropagation()
this.onActionClick(this.enabledFileActions.find(action => action.id === ACTION_RENAME)!)
}

// Delete key deletes the file with confirmation
if (event.key === 'Delete') {
event.preventDefault()
event.stopPropagation()
this.onActionClick(this.enabledFileActions.find(action => action.id === ACTION_DELETE)!)
}

// s toggle favorite
if (event.key === 's') {
event.preventDefault()
event.stopPropagation()
this.onActionClick(this.enabledFileActions.find(action => action.id === ACTION_FAVORITE)!)
}

Expand Down
38 changes: 21 additions & 17 deletions apps/files/src/components/FileEntry/FileEntryCheckbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import { FileType } from '@nextcloud/files'
import { translate as t } from '@nextcloud/l10n'
import { defineComponent } from 'vue'

import { useHotKey } from '@nextcloud/vue/dist/Composables/useHotKey.js'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'

import { isDialogOpened } from '../../utils/dialogUtils.ts'
import { useActiveStore } from '../../store/active.ts'
import { useKeyboardStore } from '../../store/keyboard.ts'
import { useSelectionStore } from '../../store/selection.ts'
Expand Down Expand Up @@ -101,12 +101,21 @@ export default defineComponent({
},
},

beforeMount() {
document.addEventListener('keydown', this.onKeyDown)
},

beforeDestroy() {
document.removeEventListener('keydown', this.onKeyDown)
created() {
// ctrl+space toggle selection
useHotKey(' ', this.onToggleSelect, {
stop: true,
prevent: true,
ctrl: true,
})

// ctrl+shift+space toggle range selection
useHotKey(' ', this.onToggleSelect, {
stop: true,
prevent: true,
ctrl: true,
shift: true,
})
},

methods: {
Expand Down Expand Up @@ -150,19 +159,14 @@ export default defineComponent({
this.selectionStore.reset()
},

onKeyDown(event: KeyboardEvent) {
// Don't react to the event if a dialog is open or the nde is not active
if (isDialogOpened() || !this.isActive) {
onToggleSelect(event: KeyboardEvent) {

Check failure on line 162 in apps/files/src/components/FileEntry/FileEntryCheckbox.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'event' is defined but never used
// Don't react if the node is not active
if (!this.isActive) {
return
}

// ctrl+space toggle selection
if (event.key === ' ' && event.ctrlKey) {
event.preventDefault()
event.stopPropagation()
logger.debug('Toggling selection for file', { source: this.source })
this.onSelectionChange(!this.isSelected)
}
logger.debug('Toggling selection for file', { source: this.source })
this.onSelectionChange(!this.isSelected)
},
},
})
Expand Down
54 changes: 17 additions & 37 deletions apps/files/src/components/FilesListTableHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,15 @@ import type { Node } from '@nextcloud/files'
import type { PropType } from 'vue'
import type { FileSource } from '../types.ts'

import { translate as t } from '@nextcloud/l10n'
import { defineComponent } from 'vue'

import { translate as t } from '@nextcloud/l10n'
import { useHotKey } from '@nextcloud/vue/dist/Composables/useHotKey.js'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import FilesListTableHeaderButton from './FilesListTableHeaderButton.vue'

import { isDialogOpened } from '../utils/dialogUtils.ts'
import { useFilesStore } from '../store/files.ts'
import { useNavigation } from '../composables/useNavigation'
import { useSelectionStore } from '../store/selection.ts'
import FilesListTableHeaderButton from './FilesListTableHeaderButton.vue'
import filesSortingMixin from '../mixins/filesSorting.ts'
import logger from '../logger.ts'

Expand Down Expand Up @@ -156,12 +155,19 @@ export default defineComponent({
},
},

beforeMount() {
document.addEventListener('keydown', this.onKeyDown)
},

beforeDestroy() {
document.removeEventListener('keydown', this.onKeyDown)
created() {
// ctrl+a selects all
useHotKey('a', this.onToggleAll, {
ctrl: true,
stop: true,
prevent: true,
})

// Escape key cancels selection
useHotKey('Escape', this.resetSelection, {
stop: true,
prevent: true,
})
},

methods: {
Expand All @@ -181,33 +187,7 @@ export default defineComponent({
}
},

onKeyDown(event: KeyboardEvent) {
// Don't react to the event if a dialog is open
if (isDialogOpened()) {
return
}

// ctrl+a selects all
if (event.key === 'a' && event.ctrlKey) {
this.onToggleAll(true)
event.preventDefault()
event.stopPropagation()
}

// Don't react if ctrl, meta or alt key is pressed, we don't need those anymore
if (event.ctrlKey || event.altKey || event.metaKey) {
return
}

// Escape key cancels selection
if (event.key === 'Escape' && (this.isSomeSelected || this.isAllSelected)) {
this.resetSelection()
event.preventDefault()
event.stopPropagation()
}
},

onToggleAll(selected) {
onToggleAll(selected = true) {
if (selected) {
const selection = this.nodes.map(node => node.source).filter(Boolean) as FileSource[]
logger.debug('Added all nodes to selection', { selection })
Expand Down
Loading

0 comments on commit 0eaa520

Please sign in to comment.