MDL-81702 core_message: Add focustrap and backdrop
* Add focustrap and backdrop to message drawer.
This commit is contained in:
@@ -36,8 +36,9 @@
|
||||
class="{{$drawerclasses}}{{/drawerclasses}} drawer bg-white {{^show}}hidden{{/show}}"
|
||||
{{^show}}aria-hidden="true"{{/show}}
|
||||
data-region="right-hand-drawer"
|
||||
role="region"
|
||||
tabindex="0"
|
||||
role="{{$role}}region{{/role}}"
|
||||
tabindex="{{$tabindex}}0{{/tabindex}}"
|
||||
{{$drawerattributes}}{{/drawerattributes}}
|
||||
>
|
||||
{{$drawercontent}}{{/drawercontent}}
|
||||
</div>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
@@ -5,6 +5,6 @@
|
||||
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define("core_message/message_popover",["jquery","core/custom_interaction_events","core/pubsub","core_message/message_drawer_events"],(function($,CustomEvents,PubSub,MessageDrawerEvents){var SELECTORS_COUNT_CONTAINER='[data-region="count-container"]',handleDecrementConversationCount=function(button){return function(){var countContainer=button.find(SELECTORS_COUNT_CONTAINER),count=parseInt(countContainer.text(),10);isNaN(count)||!count||count<2?countContainer.addClass("hidden"):(count-=1,countContainer.text(count))}},registerEventListeners=function(button){CustomEvents.define(button,[CustomEvents.events.activate]),button.on(CustomEvents.events.activate,(function(e,data){var buttonid;buttonid=button.attr("id"),PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY,buttonid),button.focus(),data.originalEvent.preventDefault()})),PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,handleDecrementConversationCount(button))};return{init:function(button){button=$(button),registerEventListeners(button)}}}));
|
||||
define("core_message/message_popover",["jquery","core/custom_interaction_events","core/pubsub","core_message/message_drawer_events"],(function($,CustomEvents,PubSub,MessageDrawerEvents){var SELECTORS_COUNT_CONTAINER='[data-region="count-container"]',handleDecrementConversationCount=function(button){return function(){var countContainer=button.find(SELECTORS_COUNT_CONTAINER),count=parseInt(countContainer.text(),10);isNaN(count)||!count||count<2?countContainer.addClass("hidden"):(count-=1,countContainer.text(count))}},registerEventListeners=function(button){CustomEvents.define(button,[CustomEvents.events.activate]),button.on(CustomEvents.events.activate,(function(e,data){var buttonid;buttonid=button.attr("id"),PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY,buttonid),data.originalEvent.preventDefault()})),PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED,handleDecrementConversationCount(button)),PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED,handleDecrementConversationCount(button))};return{init:function(button){button=$(button),registerEventListeners(button)}}}));
|
||||
|
||||
//# sourceMappingURL=message_popover.min.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"message_popover.min.js","sources":["../src/message_popover.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Controls the message popover in the nav bar.\n *\n * @module core_message/message_popover\n * @copyright 2018 Ryan Wyllie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/pubsub',\n 'core_message/message_drawer_events'\n],\nfunction(\n $,\n CustomEvents,\n PubSub,\n MessageDrawerEvents\n) {\n var SELECTORS = {\n COUNT_CONTAINER: '[data-region=\"count-container\"]'\n };\n\n /**\n * Toggle the message drawer visibility.\n *\n * @param {String} buttonid The button id for the popover.\n */\n var toggleMessageDrawerVisibility = function(buttonid) {\n PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY, buttonid);\n };\n\n /**\n * Decrement the unread conversation count in the nav bar if a conversation\n * is read. When there are no unread conversations then hide the counter.\n *\n * @param {Object} button The button element for the popover.\n * @return {Function}\n */\n var handleDecrementConversationCount = function(button) {\n return function() {\n var countContainer = button.find(SELECTORS.COUNT_CONTAINER);\n var count = parseInt(countContainer.text(), 10);\n\n if (isNaN(count)) {\n countContainer.addClass('hidden');\n } else if (!count || count < 2) {\n countContainer.addClass('hidden');\n } else {\n count = count - 1;\n countContainer.text(count);\n }\n };\n };\n\n /**\n * Add events listeners for when the popover icon is clicked and when conversations\n * are read.\n *\n * @param {Object} button The button element for the popover.\n */\n var registerEventListeners = function(button) {\n CustomEvents.define(button, [CustomEvents.events.activate]);\n\n button.on(CustomEvents.events.activate, function(e, data) {\n toggleMessageDrawerVisibility(button.attr('id'));\n button.focus();\n data.originalEvent.preventDefault();\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, handleDecrementConversationCount(button));\n };\n\n /**\n * Initialise the message popover.\n *\n * @param {Object} button The button element for the popover.\n */\n var init = function(button) {\n button = $(button);\n registerEventListeners(button);\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CustomEvents","PubSub","MessageDrawerEvents","SELECTORS","handleDecrementConversationCount","button","countContainer","find","count","parseInt","text","isNaN","addClass","registerEventListeners","events","activate","on","e","data","buttonid","attr","publish","TOGGLE_VISIBILITY","focus","originalEvent","preventDefault","subscribe","CONVERSATION_READ","CONTACT_REQUEST_ACCEPTED","CONTACT_REQUEST_DECLINED","init"],"mappings":";;;;;;;AAsBAA,sCACA,CACI,SACA,iCACA,cACA,uCAEJ,SACIC,EACAC,aACAC,OACAC,yBAEIC,0BACiB,kCAmBjBC,iCAAmC,SAASC,eACrC,eACCC,eAAiBD,OAAOE,KAAKJ,2BAC7BK,MAAQC,SAASH,eAAeI,OAAQ,IAExCC,MAAMH,SAEEA,OAASA,MAAQ,EADzBF,eAAeM,SAAS,WAIxBJ,OAAgB,EAChBF,eAAeI,KAAKF,UAW5BK,uBAAyB,SAASR,QAClCL,aAAaF,OAAOO,OAAQ,CAACL,aAAac,OAAOC,WAEjDV,OAAOW,GAAGhB,aAAac,OAAOC,UAAU,SAASE,EAAGC,MApCpB,IAASC,SAAAA,SAqCPd,OAAOe,KAAK,MApC9CnB,OAAOoB,QAAQnB,oBAAoBoB,kBAAmBH,UAqClDd,OAAOkB,QACPL,KAAKM,cAAcC,oBAGvBxB,OAAOyB,UAAUxB,oBAAoByB,kBAAmBvB,iCAAiCC,SACzFJ,OAAOyB,UAAUxB,oBAAoB0B,yBAA0BxB,iCAAiCC,SAChGJ,OAAOyB,UAAUxB,oBAAoB2B,yBAA0BzB,iCAAiCC,gBAa7F,CACHyB,KANO,SAASzB,QAChBA,OAASN,EAAEM,QACXQ,uBAAuBR"}
|
||||
{"version":3,"file":"message_popover.min.js","sources":["../src/message_popover.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Controls the message popover in the nav bar.\n *\n * @module core_message/message_popover\n * @copyright 2018 Ryan Wyllie <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n 'jquery',\n 'core/custom_interaction_events',\n 'core/pubsub',\n 'core_message/message_drawer_events'\n],\nfunction(\n $,\n CustomEvents,\n PubSub,\n MessageDrawerEvents\n) {\n var SELECTORS = {\n COUNT_CONTAINER: '[data-region=\"count-container\"]'\n };\n\n /**\n * Toggle the message drawer visibility.\n *\n * @param {String} buttonid The button id for the popover.\n */\n var toggleMessageDrawerVisibility = function(buttonid) {\n PubSub.publish(MessageDrawerEvents.TOGGLE_VISIBILITY, buttonid);\n };\n\n /**\n * Decrement the unread conversation count in the nav bar if a conversation\n * is read. When there are no unread conversations then hide the counter.\n *\n * @param {Object} button The button element for the popover.\n * @return {Function}\n */\n var handleDecrementConversationCount = function(button) {\n return function() {\n var countContainer = button.find(SELECTORS.COUNT_CONTAINER);\n var count = parseInt(countContainer.text(), 10);\n\n if (isNaN(count)) {\n countContainer.addClass('hidden');\n } else if (!count || count < 2) {\n countContainer.addClass('hidden');\n } else {\n count = count - 1;\n countContainer.text(count);\n }\n };\n };\n\n /**\n * Add events listeners for when the popover icon is clicked and when conversations\n * are read.\n *\n * @param {Object} button The button element for the popover.\n */\n var registerEventListeners = function(button) {\n CustomEvents.define(button, [CustomEvents.events.activate]);\n\n button.on(CustomEvents.events.activate, function(e, data) {\n toggleMessageDrawerVisibility(button.attr('id'));\n data.originalEvent.preventDefault();\n });\n\n PubSub.subscribe(MessageDrawerEvents.CONVERSATION_READ, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_ACCEPTED, handleDecrementConversationCount(button));\n PubSub.subscribe(MessageDrawerEvents.CONTACT_REQUEST_DECLINED, handleDecrementConversationCount(button));\n };\n\n /**\n * Initialise the message popover.\n *\n * @param {Object} button The button element for the popover.\n */\n var init = function(button) {\n button = $(button);\n registerEventListeners(button);\n };\n\n return {\n init: init,\n };\n});\n"],"names":["define","$","CustomEvents","PubSub","MessageDrawerEvents","SELECTORS","handleDecrementConversationCount","button","countContainer","find","count","parseInt","text","isNaN","addClass","registerEventListeners","events","activate","on","e","data","buttonid","attr","publish","TOGGLE_VISIBILITY","originalEvent","preventDefault","subscribe","CONVERSATION_READ","CONTACT_REQUEST_ACCEPTED","CONTACT_REQUEST_DECLINED","init"],"mappings":";;;;;;;AAsBAA,sCACA,CACI,SACA,iCACA,cACA,uCAEJ,SACIC,EACAC,aACAC,OACAC,yBAEIC,0BACiB,kCAmBjBC,iCAAmC,SAASC,eACrC,eACCC,eAAiBD,OAAOE,KAAKJ,2BAC7BK,MAAQC,SAASH,eAAeI,OAAQ,IAExCC,MAAMH,SAEEA,OAASA,MAAQ,EADzBF,eAAeM,SAAS,WAIxBJ,OAAgB,EAChBF,eAAeI,KAAKF,UAW5BK,uBAAyB,SAASR,QAClCL,aAAaF,OAAOO,OAAQ,CAACL,aAAac,OAAOC,WAEjDV,OAAOW,GAAGhB,aAAac,OAAOC,UAAU,SAASE,EAAGC,MApCpB,IAASC,SAAAA,SAqCPd,OAAOe,KAAK,MApC9CnB,OAAOoB,QAAQnB,oBAAoBoB,kBAAmBH,UAqClDD,KAAKK,cAAcC,oBAGvBvB,OAAOwB,UAAUvB,oBAAoBwB,kBAAmBtB,iCAAiCC,SACzFJ,OAAOwB,UAAUvB,oBAAoByB,yBAA0BvB,iCAAiCC,SAChGJ,OAAOwB,UAAUvB,oBAAoB0B,yBAA0BxB,iCAAiCC,gBAa7F,CACHwB,KANO,SAASxB,QAChBA,OAASN,EAAEM,QACXQ,uBAAuBR"}
|
||||
@@ -38,6 +38,13 @@ define(
|
||||
'core_message/message_drawer_helper',
|
||||
'core/pending',
|
||||
'core/drawer',
|
||||
'core/toast',
|
||||
'core/str',
|
||||
'core/config',
|
||||
'core/ajax',
|
||||
'core/local/aria/focuslock',
|
||||
'core/modal_backdrop',
|
||||
'core/templates',
|
||||
],
|
||||
function(
|
||||
$,
|
||||
@@ -55,7 +62,14 @@ function(
|
||||
Events,
|
||||
Helper,
|
||||
Pending,
|
||||
Drawer
|
||||
Drawer,
|
||||
Toast,
|
||||
Str,
|
||||
Config,
|
||||
Ajax,
|
||||
FocusLock,
|
||||
ModalBackdrop,
|
||||
Templates,
|
||||
) {
|
||||
|
||||
var SELECTORS = {
|
||||
@@ -129,6 +143,69 @@ function(
|
||||
});
|
||||
};
|
||||
|
||||
let backdropPromise = null;
|
||||
|
||||
/**
|
||||
* Set the focus on the drawer.
|
||||
*
|
||||
* This method also creates or destroy any necessary backdrop zone and focus trap.
|
||||
*
|
||||
* @param {Object} root The message drawer container.
|
||||
* @param {Boolean} hasFocus Whether the drawer has focus or not.
|
||||
*/
|
||||
var setFocus = function(root, hasFocus) {
|
||||
var drawerRoot = Drawer.getDrawerRoot(root);
|
||||
if (!drawerRoot.length) {
|
||||
return;
|
||||
}
|
||||
if (!backdropPromise) {
|
||||
backdropPromise = Templates.render('core/modal_backdrop', {})
|
||||
.then(html => new ModalBackdrop(html));
|
||||
}
|
||||
const backdropWithAdjustments = backdropPromise.then(modalBackdrop => {
|
||||
const messageDrawerZIndex = window.getComputedStyle(drawerRoot[0]).zIndex;
|
||||
if (messageDrawerZIndex) {
|
||||
modalBackdrop.setZIndex(messageDrawerZIndex - 1);
|
||||
}
|
||||
modalBackdrop.getAttachmentPoint().get(0).addEventListener('click', e => {
|
||||
PubSub.publish(Events.HIDE, {});
|
||||
e.preventDefault();
|
||||
});
|
||||
return modalBackdrop;
|
||||
});
|
||||
if (hasFocus) {
|
||||
FocusLock.trapFocus(root[0]);
|
||||
// eslint-disable-next-line promise/catch-or-return
|
||||
backdropWithAdjustments.then(modalBackdrop => {
|
||||
if (modalBackdrop) {
|
||||
modalBackdrop.show();
|
||||
const pageWrapper = document.getElementById('page');
|
||||
pageWrapper.style.overflow = 'hidden';
|
||||
// Set the focus on the close button so when we press enter, it closes the drawer as it did before
|
||||
var closeButton = root.find(SELECTORS.CLOSE_BUTTON);
|
||||
if (closeButton.length) {
|
||||
closeButton.focus();
|
||||
}
|
||||
}
|
||||
return modalBackdrop;
|
||||
});
|
||||
} else {
|
||||
// eslint-disable-next-line promise/catch-or-return
|
||||
backdropWithAdjustments.then(modalBackdrop => {
|
||||
if (modalBackdrop) {
|
||||
FocusLock.untrapFocus();
|
||||
var button = $(SELECTORS.DRAWER).attr('data-origin');
|
||||
if (button) {
|
||||
$('#' + button).focus();
|
||||
}
|
||||
modalBackdrop.hide();
|
||||
const pageWrapper = document.getElementById('page');
|
||||
pageWrapper.style.overflow = 'visible';
|
||||
}
|
||||
return modalBackdrop;
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Show the message drawer.
|
||||
*
|
||||
@@ -143,6 +220,7 @@ function(
|
||||
|
||||
var drawerRoot = Drawer.getDrawerRoot(root);
|
||||
if (drawerRoot.length) {
|
||||
setFocus(root, true);
|
||||
Drawer.show(drawerRoot);
|
||||
}
|
||||
};
|
||||
@@ -155,6 +233,7 @@ function(
|
||||
var hide = function(root) {
|
||||
var drawerRoot = Drawer.getDrawerRoot(root);
|
||||
if (drawerRoot.length) {
|
||||
setFocus(root, false);
|
||||
Drawer.hide(drawerRoot);
|
||||
}
|
||||
};
|
||||
@@ -190,7 +269,7 @@ function(
|
||||
* @param {bool} alwaysVisible Is this messaging app always shown?
|
||||
*/
|
||||
var registerEventListeners = function(namespace, root, alwaysVisible) {
|
||||
CustomEvents.define(root, [CustomEvents.events.activate]);
|
||||
CustomEvents.define(root, [CustomEvents.events.activate, CustomEvents.events.escape]);
|
||||
var paramRegex = /^data-route-param-?(\d*)$/;
|
||||
|
||||
root.on(CustomEvents.events.activate, SELECTORS.ROUTES, function(e, data) {
|
||||
@@ -293,6 +372,9 @@ function(
|
||||
$(SELECTORS.JUMPTO).attr('tabindex', 0);
|
||||
}
|
||||
});
|
||||
root.on(CustomEvents.events.escape, function() {
|
||||
PubSub.publish(Events.HIDE, {});
|
||||
});
|
||||
}
|
||||
|
||||
PubSub.subscribe(Events.SHOW_CONVERSATION, function(args) {
|
||||
@@ -304,7 +386,7 @@ function(
|
||||
var closebutton = root.find(SELECTORS.CLOSE_BUTTON);
|
||||
closebutton.on(CustomEvents.events.activate, function(e, data) {
|
||||
data.originalEvent.preventDefault();
|
||||
|
||||
setFocus(root, false);
|
||||
var button = $(SELECTORS.DRAWER).attr('data-origin');
|
||||
if (button) {
|
||||
$('#' + button).focus();
|
||||
|
||||
@@ -80,7 +80,6 @@ function(
|
||||
|
||||
button.on(CustomEvents.events.activate, function(e, data) {
|
||||
toggleMessageDrawerVisibility(button.attr('id'));
|
||||
button.focus();
|
||||
data.originalEvent.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
|
||||
}}
|
||||
{{< core/drawer}}
|
||||
{{$tabindex}}-1{{/tabindex}}
|
||||
{{$role}}dialog{{/role}}
|
||||
{{$drawerattributes}}
|
||||
aria-modal="true"
|
||||
aria-labelledby="message-drawer-header-{{uniqid}}"
|
||||
{{/drawerattributes}}
|
||||
{{$drawercontent}}
|
||||
<div id="message-drawer-{{uniqid}}" class="message-app" data-region="message-drawer" role="region">
|
||||
<h2 class="sr-only">{{#str}} messagingcategory,admin {{/str}}</h2>
|
||||
|
||||
Reference in New Issue
Block a user