Add focus-lock directive (#8141)
This commit is contained in:
@@ -241,6 +241,14 @@ function dependencies() {
|
||||
"name": "underscore",
|
||||
"src": ["node_modules/underscore/underscore-min.js"],
|
||||
"base": "./node_modules/underscore"
|
||||
},
|
||||
{
|
||||
"name": "wicg-inert",
|
||||
"src": [
|
||||
"./node_modules/wicg-inert/dist/inert.min.js",
|
||||
"./node_modules/wicg-inert/dist/inert.min.js.map"
|
||||
],
|
||||
"base": "./node_modules/wicg-inert"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
"spectrum-colorpicker": "1.8.0",
|
||||
"tinymce": "4.9.10",
|
||||
"typeahead.js": "0.11.1",
|
||||
"underscore": "1.9.1"
|
||||
"underscore": "1.9.1",
|
||||
"wicg-inert": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.6.4",
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function FocusLock($timeout) {
|
||||
|
||||
function getAutoFocusElement (elements) {
|
||||
var elmentWithAutoFocus = null;
|
||||
|
||||
elements.forEach((element) => {
|
||||
if(element.getAttribute('umb-auto-focus') === 'true') {
|
||||
elmentWithAutoFocus = element;
|
||||
}
|
||||
});
|
||||
|
||||
return elmentWithAutoFocus;
|
||||
}
|
||||
|
||||
function link(scope, element) {
|
||||
|
||||
function onInit() {
|
||||
// List of elements that can be focusable within the focus lock
|
||||
var focusableElementsSelector = 'a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled])';
|
||||
var bodyElement = document.querySelector('body');
|
||||
|
||||
$timeout(function() {
|
||||
var target = element[0];
|
||||
|
||||
var focusableElements = target.querySelectorAll(focusableElementsSelector);
|
||||
var defaultFocusedElement = getAutoFocusElement(focusableElements);
|
||||
var firstFocusableElement = focusableElements[0];
|
||||
var lastFocusableElement = focusableElements[focusableElements.length -1];
|
||||
|
||||
// We need to add the tabbing-active class in order to highlight the focused button since the default style is
|
||||
// outline: none; set in the stylesheet specifically
|
||||
bodyElement.classList.add('tabbing-active');
|
||||
|
||||
// If there is no default focused element put focus on the first focusable element in the nodelist
|
||||
if(defaultFocusedElement === null ){
|
||||
firstFocusableElement.focus();
|
||||
}
|
||||
|
||||
target.addEventListener('keydown', function(event){
|
||||
var isTabPressed = (event.key === 'Tab' || event.keyCode === 9);
|
||||
|
||||
if (!isTabPressed){
|
||||
return;
|
||||
}
|
||||
|
||||
// If shift + tab key
|
||||
if(event.shiftKey){
|
||||
// Set focus on the last focusable element if shift+tab are pressed meaning we go backwards
|
||||
if(document.activeElement === firstFocusableElement){
|
||||
lastFocusableElement.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
// Else only the tab key is pressed
|
||||
else{
|
||||
// Using only the tab key we set focus on the first focusable element mening we go forward
|
||||
if (document.activeElement === lastFocusableElement) {
|
||||
firstFocusableElement.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 250);
|
||||
}
|
||||
|
||||
onInit();
|
||||
}
|
||||
|
||||
var directive = {
|
||||
restrict: 'A',
|
||||
link: link
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbFocusLock', FocusLock);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,26 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function focusLockService() {
|
||||
var elementToInert = document.querySelector('#mainwrapper');
|
||||
|
||||
function addInertAttribute() {
|
||||
elementToInert.setAttribute('inert', true);
|
||||
}
|
||||
|
||||
function removeInertAttribute() {
|
||||
elementToInert.removeAttribute('inert');
|
||||
}
|
||||
|
||||
var service = {
|
||||
addInertAttribute: addInertAttribute,
|
||||
removeInertAttribute: removeInertAttribute
|
||||
}
|
||||
|
||||
return service;
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco.services").factory("focusLockService", focusLockService);
|
||||
|
||||
})();
|
||||
@@ -8,7 +8,7 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function overlayService(eventsService, backdropService) {
|
||||
function overlayService(eventsService, backdropService, focusLockService) {
|
||||
|
||||
var currentOverlay = null;
|
||||
|
||||
@@ -43,12 +43,14 @@
|
||||
}
|
||||
|
||||
overlay.show = true;
|
||||
focusLockService.addInertAttribute();
|
||||
backdropService.open(backdropOptions);
|
||||
currentOverlay = overlay;
|
||||
eventsService.emit("appState.overlay", overlay);
|
||||
}
|
||||
|
||||
function close() {
|
||||
focusLockService.removeInertAttribute();
|
||||
backdropService.close();
|
||||
currentOverlay = null;
|
||||
eventsService.emit("appState.overlay", null);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<umb-editor-container>
|
||||
<umb-box>
|
||||
<umb-box-content>
|
||||
<div class="form-search" ng-hide="model.filter === false" style="margin-bottom: 15px;">
|
||||
<div class="form-search" ng-if="model.filter" style="margin-bottom: 15px;">
|
||||
<i class="icon-search"></i>
|
||||
<input type="text"
|
||||
ng-model="searchTerm"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div ng-controller="Umbraco.Overlays.ItemPickerOverlay" class="umb-itempicker">
|
||||
|
||||
<div class="form-search" ng-hide="model.filter === false" style="margin-bottom: 15px;">
|
||||
<div class="form-search" ng-if="model.filter" style="margin-bottom: 15px;">
|
||||
<i class="icon-search" aria-hidden="true"></i>
|
||||
<input type="text"
|
||||
ng-model="searchTerm"
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-show="showPasswordFields">
|
||||
<div ng-if="showPasswordFields">
|
||||
|
||||
<h5>
|
||||
<localize key="general_changePassword">Change password</localize>
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
<div data-element="{{name}}" class="umb-overlay umb-overlay-{{position}} umb-overlay--{{size}}" on-outside-click="outSideClick()" role="dialog" aria-labelledby="umb-overlay-title" aria-describedby="umb-overlay-description">
|
||||
<div
|
||||
data-element="{{name}}"
|
||||
class="umb-overlay umb-overlay-{{position}} umb-overlay--{{size}}"
|
||||
on-outside-click="outSideClick()"
|
||||
umb-focus-lock
|
||||
role="dialog"
|
||||
aria-labelledby="umb-overlay-title"
|
||||
aria-describedby="umb-overlay-description">
|
||||
<ng-form class="umb-overlay__form" name="overlayForm" novalidate val-form-manager>
|
||||
<div data-element="overlay-header" class="umb-overlay-header" ng-show="!model.hideHeader">
|
||||
<h1 class="umb-overlay__title" id="umb-overlay-title">{{model.title}}</h1>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div ng-switch-when="true">
|
||||
|
||||
<ng-form name="changePasswordForm">
|
||||
<umb-control-group alias="resetPassword" label="@user_resetPassword" ng-show="vm.config.enableReset">
|
||||
<umb-control-group alias="resetPassword" label="@user_resetPassword" ng-if="vm.config.enableReset">
|
||||
<umb-checkbox model="vm.passwordValues.reset" server-validation-field="resetPassword"
|
||||
on-change="vm.showReset = !vm.showReset" />
|
||||
<span ng-messages="changePasswordForm.resetPassword.$error" show-validation-on-submit>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
'lib/chart.js/chart.min.js',
|
||||
'lib/angular-chart.js/angular-chart.min.js',
|
||||
'lib/wicg-inert/dist/inert.min.js',
|
||||
|
||||
'lib/umbraco/Extensions.js',
|
||||
|
||||
|
||||
Reference in New Issue
Block a user