Skip to content

Commit 70c879b

Browse files
dschmidtJan
authored andcommitted
keyboardActions: Use event.preventDefault only when an action has been found (#9559)
* keyboardActions: Use event.preventDefault only when an action has been found ... and handle multiple registered actions for bound keys * Simplify code for event listener addition/removal * Use useEventListener from @vueuse * Fix lint * Register keyboardActions globally to avoid focus issues * Disable shortcut handling in certain contexts
1 parent 738d0f4 commit 70c879b

File tree

7 files changed

+65
-38
lines changed

7 files changed

+65
-38
lines changed

packages/web-app-files/src/components/Search/List.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export default defineComponent({
202202
203203
const resourcesView = useResourcesViewDefaults<Resource, any, any[]>()
204204
205-
const keyActions = useKeyboardActions('files-view')
205+
const keyActions = useKeyboardActions()
206206
useKeyboardTableNavigation(keyActions, resourcesView.paginatedResources)
207207
useKeyboardTableMouseActions(keyActions)
208208
useKeyboardTableActions(keyActions)

packages/web-app-files/src/components/SideBar/SideBar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
:loading="loading"
1313
:is-header-compact="!!loadedResource"
1414
v-bind="$attrs"
15-
data-custom-key-bindings="true"
15+
data-custom-key-bindings-disabled="true"
1616
@before-unmount="destroySideBar"
1717
@mounted="focusSideBar"
1818
@file-changed="focusSideBar"

packages/web-app-files/src/views/spaces/GenericSpace.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ export default defineComponent({
428428
429429
const resourcesViewDefaults = useResourcesViewDefaults<Resource, any, any[]>()
430430
431-
const keyActions = useKeyboardActions('files-view')
431+
const keyActions = useKeyboardActions()
432432
useKeyboardTableNavigation(keyActions, resourcesViewDefaults.paginatedResources)
433433
useKeyboardTableMouseActions(keyActions)
434434
useKeyboardTableSpaceActions(keyActions, props.space)

packages/web-app-search/src/portals/SearchBar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
id="files-global-search"
55
ref="searchBar"
66
class="oc-flex"
7-
data-custom-key-bindings="true"
7+
data-custom-key-bindings-disabled="true"
88
>
99
<oc-search-bar
1010
id="files-global-search-bar"

packages/web-pkg/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
"directory": "packages/web-pkg"
1515
},
1616
"peerDependencies": {
17-
"@microsoft/fetch-event-source": "^2.0.1",
1817
"@casl/ability": "^6.3.3",
1918
"@casl/vue": "^2.2.1",
19+
"@microsoft/fetch-event-source": "^2.0.1",
20+
"@vueuse/core": "^10.0.0",
2021
"axios": "^0.27.2 || ^1.0.0",
2122
"design-system": "workspace:@ownclouders/design-system@*",
2223
"filesize": "^9.0.11",

packages/web-pkg/src/composables/keyboardActions/useKeyboardActions.ts

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
import { onBeforeUnmount, onMounted, Ref, ref } from 'vue'
1+
import { Ref, ref, unref } from 'vue'
2+
import { useEventListener } from '@vueuse/core'
23
import * as uuid from 'uuid'
34

45
export enum Key {
56
C = 'c',
67
V = 'v',
78
X = 'x',
89
A = 'a',
10+
S = 's',
911
Space = ' ',
1012
ArrowUp = 'ArrowUp',
1113
ArrowDown = 'ArrowDown',
@@ -32,24 +34,59 @@ export interface KeyboardAction {
3234
callback: (event: KeyboardEvent) => void
3335
}
3436

35-
export const useKeyboardActions = (keyBindOnElementId: string | null = null): KeyboardActions => {
37+
const areCustomKeyBindingsDisabled = () => {
38+
const activeElementTag = document.activeElement.tagName
39+
const type = document.activeElement.getAttribute('type')
40+
if (
41+
['textarea', 'input', 'select'].includes(activeElementTag.toLowerCase()) &&
42+
type !== 'checkbox'
43+
) {
44+
return true
45+
}
46+
const closestSelectionEl = window.getSelection().focusNode as HTMLElement
47+
if (!closestSelectionEl) {
48+
return false
49+
}
50+
let customKeyBindings
51+
try {
52+
customKeyBindings = closestSelectionEl?.closest("[data-custom-key-bindings-disabled='true']")
53+
} catch {
54+
customKeyBindings = closestSelectionEl?.parentElement.closest(
55+
"[data-custom-key-bindings-disabled='true']"
56+
)
57+
}
58+
if (customKeyBindings) {
59+
return true
60+
}
61+
62+
const isTextSelected = window.getSelection().type === 'Range'
63+
return isTextSelected
64+
}
65+
66+
export const useKeyboardActions = (): KeyboardActions => {
3667
const actions = ref<Array<KeyboardAction>>([])
3768
const selectionCursor = ref(0)
69+
3870
const listener = (event: KeyboardEvent): void => {
39-
event.preventDefault()
71+
if (areCustomKeyBindingsDisabled()) {
72+
return
73+
}
74+
4075
const { key, ctrlKey, metaKey, shiftKey } = event
4176
let modifier = null
4277
if (metaKey || ctrlKey) {
4378
modifier = ModifierKey.Ctrl
4479
} else if (shiftKey) {
4580
modifier = ModifierKey.Shift
4681
}
47-
const action = actions.value.find((action) => {
48-
return action.primary === key && action.modifier === modifier
49-
})
50-
if (action) {
51-
action.callback(event)
52-
}
82+
unref(actions)
83+
.filter((action) => {
84+
return action.primary === key && action.modifier === modifier
85+
})
86+
.forEach((action) => {
87+
event.preventDefault()
88+
action.callback(event)
89+
})
5390
}
5491
const bindKeyAction = (
5592
keys: { primary: Key; modifier?: ModifierKey },
@@ -73,25 +110,7 @@ export const useKeyboardActions = (keyBindOnElementId: string | null = null): Ke
73110
selectionCursor.value = 0
74111
}
75112

76-
onMounted(() => {
77-
let element = null
78-
if (keyBindOnElementId) {
79-
element = document.getElementById(keyBindOnElementId)
80-
}
81-
element
82-
? element.addEventListener('keydown', listener)
83-
: document.addEventListener('keydown', listener)
84-
})
85-
86-
onBeforeUnmount(() => {
87-
let element = null
88-
if (keyBindOnElementId) {
89-
element = document.getElementById(keyBindOnElementId)
90-
}
91-
element
92-
? element.removeEventListener('keydown', listener)
93-
: document.removeEventListener('keydown', listener)
94-
})
113+
useEventListener(document, 'keydown', listener)
95114

96115
return {
97116
actions,

pnpm-lock.yaml

Lines changed: 12 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)