Merge pull request #2078 from umbraco/temp-U4-10107

fixes: U4-10107 Weird UI bugs with filter dropdowns
This commit is contained in:
Shannon Deminick
2017-07-28 16:43:21 +10:00
committed by GitHub
8 changed files with 325 additions and 26 deletions

View File

@@ -155,7 +155,6 @@ angular.module('umbraco.directives')
var els = ["INPUT","A","BUTTON"];
if(els.indexOf(el) >= 0){return;}
// ignore children of links and buttons
// ignore clicks on new overlay
var parents = $(event.target).parents("a,button,.umb-overlay");
if(parents.length > 0){

View File

@@ -0,0 +1,133 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbDropdown
@restrict E
@scope
@description
<b>Added in versions 7.7.0</b>: Use this component to render a dropdown menu.
<h3>Markup example</h3>
<pre>
<div ng-controller="MyDropdown.Controller as vm">
<div style="position: relative;">
<umb-button
type="button"
label="Toggle dropdown"
action="vm.toggle()">
</umb-button>
<umb-dropdown ng-if="vm.dropdownOpen" on-close="vm.close()" umb-keyboard-list>
<umb-dropdown-item
ng-repeat="item in vm.items">
<a href="" ng-click="vm.select(item)">{{ item.name }}</a>
</umb-dropdown-item>
</umb-dropdown>
</div>
</div>
</pre>
<h3>Controller example</h3>
<pre>
(function () {
"use strict";
function Controller() {
var vm = this;
vm.dropdownOpen = false;
vm.items = [
{ "name": "Item 1" },
{ "name": "Item 2" },
{ "name": "Item 3" }
];
vm.toggle = toggle;
vm.close = close;
vm.select = select;
function toggle() {
vm.dropdownOpen = true;
}
function close() {
vm.dropdownOpen = false;
}
function select(item) {
// Do your magic here
}
}
angular.module("umbraco").controller("MyDropdown.Controller", Controller);
})();
</pre>
<h3>Use in combination with</h3>
<ul>
<li>{@link umbraco.directives.directive:umbDropdownItem umbDropdownItem}</li>
<li>{@link umbraco.directives.directive:umbKeyboardList umbKeyboardList}</li>
</ul>
@param {callback} onClose Callback when the dropdown menu closes. When you click outside or press esc.
**/
(function() {
'use strict';
function umbDropdown($document) {
function link(scope, element, attr, ctrl) {
scope.close = function() {
if (scope.onClose) {
scope.onClose();
}
};
// Handle keydown events
function keydown(event) {
// press escape
if(event.keyCode === 27) {
scope.onClose();
}
}
// Stop to listen typing.
function stopListening() {
$document.off('keydown', keydown);
}
// Start listening to key typing.
$document.on('keydown', keydown);
// Stop listening when scope is destroyed.
scope.$on('$destroy', stopListening);
}
var directive = {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: 'views/components/umb-dropdown.html',
scope: {
onClose: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbDropdown', umbDropdown);
})();

View File

@@ -0,0 +1,29 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbDropdownItem
@restrict E
@description
<b>Added in versions 7.7.0</b>: Use this directive to construct a dropdown item. See documentation for {@link umbraco.directives.directive:umbDropdown umbDropdown}.
**/
(function() {
'use strict';
function umbDropdownItem() {
var directive = {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: 'views/components/umb-dropdown-item.html',
};
return directive;
}
angular.module('umbraco.directives').directive('umbDropdownItem', umbDropdownItem);
})();

View File

@@ -0,0 +1,115 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbKeyboardList
@restrict E
@description
<b>Added in versions 7.7.0</b>: Use this directive to add arrow up and down keyboard shortcuts to a list. Use this together with the {@link umbraco.directives.directive:umbDropdown umbDropdown} component to make easy accessible dropdown menus.
<h3>Markup example</h3>
<pre>
<div>
<ul umb-keyboard-list>
<li><a href="">Item 1</a></li>
<li><a href="">Item 2</a></li>
<li><a href="">Item 3</a></li>
<li><a href="">Item 4</a></li>
<li><a href="">Item 5</a></li>
<li><a href="">Item 6</a></li>
</ul>
</div>
</pre>
<h3>Use in combination with</h3>
<ul>
<li>{@link umbraco.directives.directive:umbDropdown umbDropdown}</li>
</ul>
**/
angular.module('umbraco.directives')
.directive('umbKeyboardList', ['$document', '$timeout', function ($document, $timeout) {
return {
restrict: 'A',
link: function (scope, element, attr) {
var listItems = [];
var currentIndex = 0;
var focusSet = false;
$timeout(function(){
// get list of all links in the list
listItems = element.find("li a");
});
// Handle keydown events
function keydown(event) {
$timeout(function(){
checkFocus();
// arrow down
if (event.keyCode === 40) {
arrowDown();
}
// arrow up
if (event.keyCode === 38) {
arrowUp();
}
});
}
function checkFocus() {
var found = false;
// check if any element has focus
angular.forEach(listItems, function (item, index) {
if ($(item).is(":focus")) {
// if an element already has focus set the
// currentIndex so we navigate from that element
currentIndex = index;
focusSet = true;
found = true;
}
});
// If we don't find an element with focus we reset the currentIndex and the focusSet flag
// we do this because you can have navigated away from the list with tab and we want to reset it if you navigate back
if (!found) {
currentIndex = 0;
focusSet = false;
}
}
function arrowDown() {
if (currentIndex < listItems.length - 1) {
// only bump the current index if the focus is already
// set else we just want to focus the first element
if (focusSet) {
currentIndex++;
}
listItems[currentIndex].focus();
focusSet = true;
}
}
function arrowUp() {
if (currentIndex > 0) {
currentIndex--;
listItems[currentIndex].focus();
}
}
// Stop to listen typing.
function stopListening() {
$document.off('keydown', keydown);
}
// Start listening to key typing.
$document.on('keydown', keydown);
// Stop listening when scope is destroyed.
scope.$on('$destroy', stopListening);
}
};
}]);

View File

@@ -0,0 +1 @@
<li ng-transclude></li>

View File

@@ -0,0 +1 @@
<ul class="dropdown-menu db" on-outside-click="close()" ng-transclude></ul>

View File

@@ -68,6 +68,7 @@
}
];
vm.toggleFilter = toggleFilter;
vm.setUsersViewState = setUsersViewState;
vm.selectLayout = selectLayout;
vm.selectUser = selectUser;
@@ -117,6 +118,28 @@
return found ? found.label : sortKey;
}
function toggleFilter(type) {
// hack: on-outside-click prevents us from closing the dropdown when clicking on another link
// so I had to do this manually
switch (type) {
case "state":
vm.page.showStatusFilter = !vm.page.showStatusFilter;
vm.page.showGroupFilter = false;
vm.page.showOrderByFilter = false;
break;
case "group":
vm.page.showGroupFilter = !vm.page.showGroupFilter;
vm.page.showStatusFilter = false;
vm.page.showOrderByFilter = false;
break;
case "orderBy":
vm.page.showOrderByFilter = !vm.page.showOrderByFilter;
vm.page.showStatusFilter = false;
vm.page.showGroupFilter = false;
break;
}
}
function setUsersViewState(state) {
if (state === "createUser") {

View File

@@ -1,7 +1,5 @@
<div ng-controller="Umbraco.Editors.Users.UsersController as vm" class="clearfix">
<umb-load-indicator ng-show="vm.loading"></umb-load-indicator>
<!-- Users Overview -->
@@ -102,51 +100,51 @@
<div class="flex" style="margin-left: auto;">
<!-- State filter -->
<div class="dropdown pull-right" ng-if="vm.userStatesFilter.length > 0">
<a class="btn btn-link dropdown-toggle flex" href="" ng-click="vm.showStatusFilter = !vm.showStatusFilter">
<div style="position: relative;" ng-if="vm.userStatesFilter.length > 0">
<a class="btn btn-link dropdown-toggle flex" href="" ng-click="vm.toggleFilter('state')">
<span>Status:</span>
<span class="bold truncate dib" style="margin-left: 5px; margin-right: 3px; max-width: 150px;">{{ vm.getFilterName(vm.userStatesFilter) }}</span>
<span class="caret"></span>
</a>
<ul ng-if="vm.showStatusFilter" on-outside-click="vm.showStatusFilter = false;" class="dropdown-menu db" role="menu" aria-labelledby="dropdownMenu">
<li ng-repeat="userState in vm.userStatesFilter" style="padding: 5px 10px;">
<umb-dropdown class="pull-right" ng-if="vm.page.showStatusFilter" on-close="vm.page.showStatusFilter = false;">
<umb-dropdown-item ng-repeat="userState in vm.userStatesFilter | filter:{ count: '!0', key: '!All'}" style="padding: 8px 20px 8px 16px;">
<div class="flex items-center">
<input style="margin-right: 7px; margin-top: 2px;" type="checkbox" ng-model="userState.selected" ng-change="vm.setUserStatesFilter(userState)" />
{{ userState.name }} ({{userState.count}})
<input id="state-{{$index}}" type="checkbox" ng-model="userState.selected" ng-change="vm.setUserStatesFilter(userState)" style="margin-right: 10px; margin-top: -3px;" />
<label for="state-{{$index}}">{{ userState.name }} ({{userState.count}})</label>
</div>
</li>
</ul>
</umb-dropdown-item>
</umb-dropdown>
</div>
<!-- Groups filter -->
<div class="dropdown pull-right">
<a class="btn btn-link dropdown-toggle flex" href="" ng-click="vm.showGroupFilter = !vm.showGroupFilter">
<div style="position: relative;">
<a class="btn btn-link dropdown-toggle flex" href="" ng-click="vm.toggleFilter('group')">
<span><localize key="general_groups"></localize>:</span>
<span class="bold truncate dib" style="margin-left: 5px; margin-right: 3px; max-width: 150px;">{{ vm.getFilterName(vm.userGroups) }}</span>
<span class="caret"></span>
</a>
<ul ng-if="vm.showGroupFilter" on-outside-click="vm.showGroupFilter = false;" class="dropdown-menu db" role="menu" aria-labelledby="dropdownMenu">
<li ng-repeat="userGroup in vm.userGroups" style="padding: 5px 10px;">
<umb-dropdown class="pull-right" ng-if="vm.page.showGroupFilter" on-close="vm.page.showGroupFilter = false;">
<umb-dropdown-item ng-repeat="userGroup in vm.userGroups" style="padding: 8px 20px 8px 16px;">
<div class="flex items-center">
<input style="margin-right: 7px; margin-top: 2px;" type="checkbox" ng-model="userGroup.selected" ng-change="vm.setUserGroupFilter(userGroup)" />
{{ userGroup.name }}
<input id="group-{{$index}}" type="checkbox" ng-model="userGroup.selected" ng-change="vm.setUserGroupFilter(userGroup)" style="margin-right: 10px; margin-top: -3px;" />
<label for="group-{{$index}}">{{ userGroup.name }}</label>
</div>
</li>
</ul>
</umb-dropdown-item>
</umb-dropdown>
</div>
<!-- Order By -->
<div class="dropdown pull-right">
<a class="btn btn-link dropdown-toggle flex" href="" data-toggle="dropdown">
<div style="position: relative;">
<a class="btn btn-link dropdown-toggle flex" href="" ng-click="vm.toggleFilter('orderBy')">
<span>Order by:</span>
<span class="bold" style="margin-left: 2px;">{{ vm.getSortLabel(vm.usersOptions.orderBy, vm.usersOptions.orderDirection) }} </span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu">
<li ng-repeat="sortData in vm.userSortData">
<a tabindex="-1" href="#" ng-click="vm.setOrderByFilter(sortData.key, sortData.direction)" prevent-default>{{sortData.label}}</a>
</li>
</ul>
<umb-dropdown class="pull-right" ng-if="vm.page.showOrderByFilter" on-close="vm.page.showOrderByFilter = false;" umb-keyboard-list>
<umb-dropdown-item ng-repeat="sortData in vm.userSortData">
<a href="#" ng-click="vm.setOrderByFilter(sortData.key, sortData.direction)" prevent-default>{{sortData.label}}</a>
</umb-dropdown-item>
</umb-dropdown>
</div>
</div>