Merge pull request #5281 from nextcloud/fix/sidebar-focustrap

fix: Fix tab focus when other elements are displayed next to text that are within a focus trap
This commit is contained in:
Julius Härtl 2024-01-29 08:37:02 +01:00 коммит произвёл GitHub
Родитель c7b98a80a3 2c9e7a0fce
Коммит cd2f39c5d5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
6 изменённых файлов: 49 добавлений и 11 удалений

Просмотреть файл

@ -29,7 +29,7 @@ import { lowlight } from 'lowlight/lib/core.js'
import hljs from 'highlight.js/lib/core'
import { logger } from './helpers/logger.js'
import { Mention, PlainText, RichText } from './extensions/index.js'
import { FocusTrap, Mention, PlainText, RichText } from './extensions/index.js'
// eslint-disable-next-line import/no-named-as-default
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
@ -64,6 +64,7 @@ const createEditor = ({ language, onCreate, onUpdate = () => {}, extensions, ena
}),
],
}),
FocusTrap,
]
} else {
defaultExtensions = [PlainText, CodeBlockLowlight.configure({ lowlight, defaultLanguage: language })]

Просмотреть файл

@ -643,16 +643,10 @@ export default {
onFocus() {
this.emit('focus')
// Make sure that the focus trap doesn't catch tab keys while being in the contenteditable
window._nc_focus_trap?.[0]?.pause()
},
onBlur() {
this.emit('blur')
// Hand back focus to the previous trap
window._nc_focus_trap?.[0]?.unpause()
this.$el.focus()
},
onAddImageNode() {
@ -750,6 +744,7 @@ export default {
this.$editor.commands.insertContent('\t')
this.$editor.commands.focus()
event.preventDefault()
event.stopPropagation()
return
}

Просмотреть файл

@ -30,8 +30,7 @@
<EditorOutline />
</div>
<slot />
<EditorContent tabindex="0"
role="document"
<EditorContent role="document"
class="editor__content text-editor__content"
:editor="$editor" />
<div class="text-editor__content-wrapper__right" />

Просмотреть файл

@ -0,0 +1,41 @@
import { Extension } from '@tiptap/core'
const toggleFocusTrap = ({ editor }) => {
const trapStack = window._nc_focus_trap ?? []
const activeTrap = trapStack[trapStack.length - 1]
const possibleEditorTabCommand = editor.can().sinkListItem('listItem')
|| editor.can().goToNextCell()
|| editor.can().goToPreviousCell()
if (possibleEditorTabCommand) {
activeTrap?.pause()
} else {
activeTrap?.unpause()
}
}
const unpauseFocusTrap = ({ editor }) => {
const trapStack = window._nc_focus_trap ?? []
const activeTrap = trapStack[trapStack.length - 1]
activeTrap?.unpause()
}
/**
* The viewer focus trap needs to be paused on the fly in order to properly handle tab commands in the editor,
* as we have no control over if a tab key event is reaching the editor otherwise. This is because the focus trap
* registeres a capture listener (https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#capture), so whenever we reach a tab command in the editor the focus trap will already have captured the event.
*
* We also cannot work around this by pushing our own focus trap to the stack, as the focus trap package does not offer any reliable way to programmatically focus the next element of the parent trap if we allow tabbing out of the editor.
*/
const FocusTrap = Extension.create({
name: 'focustrap',
onFocus: toggleFocusTrap,
onBlur: unpauseFocusTrap,
onSelectionUpdate: toggleFocusTrap,
onTransaction: toggleFocusTrap,
onUpdate: toggleFocusTrap,
})
export default FocusTrap

Просмотреть файл

@ -22,6 +22,7 @@
import CollaborationCursor from './CollaborationCursor.js'
import Emoji from './Emoji.js'
import FocusTrap from './FocusTrap.js'
import Keymap from './Keymap.js'
import UserColor from './UserColor.js'
import Markdown from './Markdown.js'
@ -33,6 +34,7 @@ import Mention from './Mention.js'
export {
CollaborationCursor,
Emoji,
FocusTrap,
Keymap,
UserColor,
Markdown,

Просмотреть файл

@ -46,7 +46,7 @@
</div>
</div>
<div :class="{'split-view': showCode && showPreview }">
<pre v-show="showCode" class="split-view__code"><NodeViewContent as="code" :contenteditable="isEditable" /></pre>
<pre v-show="showCode" class="split-view__code"><NodeViewContent as="code" tabindex="-1" :contenteditable="isEditable" /></pre>
<div v-show="showPreview"
ref="preview"
class="split-view__preview"