diff options
Diffstat (limited to 'core/src/jquery/ocdialog.js')
-rw-r--r-- | core/src/jquery/ocdialog.js | 95 |
1 files changed, 54 insertions, 41 deletions
diff --git a/core/src/jquery/ocdialog.js b/core/src/jquery/ocdialog.js index e08db2530e3..a5f588ec659 100644 --- a/core/src/jquery/ocdialog.js +++ b/core/src/jquery/ocdialog.js @@ -1,25 +1,11 @@ -/* - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import $ from 'jquery' +import { createFocusTrap } from 'focus-trap' +import { isA11yActivation } from '../Util/a11y.js' $.widget('oc.ocdialog', { options: { @@ -42,16 +28,32 @@ $.widget('oc.ocdialog', { this.originalTitle = this.element.attr('title') this.options.title = this.options.title || this.originalTitle - this.$dialog = $('<div class="oc-dialog" />') + this.$dialog = $('<div class="oc-dialog"></div>') .attr({ // Setting tabIndex makes the div focusable tabIndex: -1, role: 'dialog', + 'aria-modal': true, }) .insertBefore(this.element) this.$dialog.append(this.element.detach()) this.element.removeAttr('title').addClass('oc-dialog-content').appendTo(this.$dialog) + // Activate the primary button on enter if there is a single input + if (self.element.find('input').length === 1) { + const $input = self.element.find('input') + $input.on('keydown', function(event) { + if (isA11yActivation(event)) { + if (self.$buttonrow) { + const $button = self.$buttonrow.find('button.primary') + if ($button && !$button.prop('disabled')) { + $button.click() + } + } + } + }) + } + this.$dialog.css({ display: 'inline-block', position: 'fixed', @@ -88,27 +90,15 @@ $.widget('oc.ocdialog', { event.preventDefault() return false } - // If no button is selected we trigger the primary - if ( - self.$buttonrow - && self.$buttonrow.find($(event.target)).length === 0 - ) { - const $button = self.$buttonrow.find('button.primary') - if ($button && !$button.prop('disabled')) { - $button.trigger('click') - } - } else if (self.$buttonrow) { - $(event.target).trigger('click') - } return false } }) this._setOptions(this.options) this._createOverlay() + this._useFocusTrap() }, _init() { - this.$dialog.focus() this._trigger('open') }, _setOption(key, value) { @@ -129,7 +119,7 @@ $.widget('oc.ocdialog', { if (this.$buttonrow) { this.$buttonrow.empty() } else { - const $buttonrow = $('<div class="oc-dialog-buttonrow" />') + const $buttonrow = $('<div class="oc-dialog-buttonrow"></div>') this.$buttonrow = $buttonrow.appendTo(this.$dialog) } if (value.length === 1) { @@ -149,8 +139,10 @@ $.widget('oc.ocdialog', { self.$defaultButton = $button } self.$buttonrow.append($button) - $button.click(function() { - val.click.apply(self.element[0], arguments) + $button.on('click keydown', function(event) { + if (isA11yActivation(event)) { + val.click.apply(self.element[0], arguments) + } }) }) this.$buttonrow.find('button') @@ -167,11 +159,14 @@ $.widget('oc.ocdialog', { break case 'closeButton': if (value) { - const $closeButton = $('<a class="oc-dialog-close"></a>') + const $closeButton = $('<button class="oc-dialog-close"></button>') + $closeButton.attr('aria-label', t('core', 'Close "{dialogTitle}" dialog', { dialogTitle: this.$title || this.options.title })) this.$dialog.prepend($closeButton) - $closeButton.on('click', function() { - self.options.closeCallback && self.options.closeCallback() - self.close() + $closeButton.on('click keydown', function(event) { + if (isA11yActivation(event)) { + self.options.closeCallback && self.options.closeCallback() + self.close() + } }) } else { this.$dialog.find('.oc-dialog-close').remove() @@ -219,7 +214,7 @@ $.widget('oc.ocdialog', { } this.overlay = $('<div>') .addClass('oc-dialog-dim') - .appendTo(contentDiv) + .insertBefore(this.$dialog) this.overlay.on('click keydown keyup', function(event) { if (event.target !== self.$dialog.get(0) && self.$dialog.find($(event.target)).length === 0) { event.preventDefault() @@ -239,6 +234,23 @@ $.widget('oc.ocdialog', { this.overlay = null } }, + _useFocusTrap() { + // Create global stack if undefined + Object.assign(window, { _nc_focus_trap: window._nc_focus_trap || [] }) + + const dialogElement = this.$dialog[0] + this.focusTrap = createFocusTrap(dialogElement, { + allowOutsideClick: true, + trapStack: window._nc_focus_trap, + fallbackFocus: dialogElement, + }) + + this.focusTrap.activate() + }, + _clearFocusTrap() { + this.focusTrap?.deactivate() + this.focusTrap = null + }, widget() { return this.$dialog }, @@ -249,6 +261,7 @@ $.widget('oc.ocdialog', { this.enterCallback = null }, close() { + this._clearFocusTrap() this._destroyOverlay() const self = this // Ugly hack to catch remaining keyup events. |