This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-ui.git

commit 17bc3ee4e134a9a1ca0c27e940bf70fcbbedaf3f
Author: Alex Heneveld <[email protected]>
AuthorDate: Wed Oct 5 13:54:31 2022 +0100

    exact copy of uib dropdown.js, to allow for nested dropdowns
---
 .../inspect/activities/detail/dropdown-nested.js   | 440 +++++++++++++++++++++
 1 file changed, 440 insertions(+)

diff --git 
a/ui-modules/app-inspector/app/views/main/inspect/activities/detail/dropdown-nested.js
 
b/ui-modules/app-inspector/app/views/main/inspect/activities/detail/dropdown-nested.js
new file mode 100644
index 00000000..cbfe420f
--- /dev/null
+++ 
b/ui-modules/app-inspector/app/views/main/inspect/activities/detail/dropdown-nested.js
@@ -0,0 +1,440 @@
+angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 
'ui.bootstrap.position'])
+
+    .constant('uibDropdownConfig', {
+        appendToOpenClass: 'uib-dropdown-open',
+        openClass: 'open'
+    })
+
+    .service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', 
function($document, $rootScope, $$multiMap) {
+        var openScope = null;
+        var openedContainers = $$multiMap.createNew();
+
+        this.isOnlyOpen = function(dropdownScope, appendTo) {
+            var openedDropdowns = openedContainers.get(appendTo);
+            if (openedDropdowns) {
+                var openDropdown = openedDropdowns.reduce(function(toClose, 
dropdown) {
+                    if (dropdown.scope === dropdownScope) {
+                        return dropdown;
+                    }
+
+                    return toClose;
+                }, {});
+                if (openDropdown) {
+                    return openedDropdowns.length === 1;
+                }
+            }
+
+            return false;
+        };
+
+        this.open = function(dropdownScope, element, appendTo) {
+            if (!openScope) {
+                $document.on('click', closeDropdown);
+            }
+
+            if (openScope && openScope !== dropdownScope) {
+                openScope.isOpen = false;
+            }
+
+            openScope = dropdownScope;
+
+            if (!appendTo) {
+                return;
+            }
+
+            var openedDropdowns = openedContainers.get(appendTo);
+            if (openedDropdowns) {
+                var openedScopes = openedDropdowns.map(function(dropdown) {
+                    return dropdown.scope;
+                });
+                if (openedScopes.indexOf(dropdownScope) === -1) {
+                    openedContainers.put(appendTo, {
+                        scope: dropdownScope
+                    });
+                }
+            } else {
+                openedContainers.put(appendTo, {
+                    scope: dropdownScope
+                });
+            }
+        };
+
+        this.close = function(dropdownScope, element, appendTo) {
+            if (openScope === dropdownScope) {
+                $document.off('click', closeDropdown);
+                $document.off('keydown', this.keybindFilter);
+                openScope = null;
+            }
+
+            if (!appendTo) {
+                return;
+            }
+
+            var openedDropdowns = openedContainers.get(appendTo);
+            if (openedDropdowns) {
+                var dropdownToClose = openedDropdowns.reduce(function(toClose, 
dropdown) {
+                    if (dropdown.scope === dropdownScope) {
+                        return dropdown;
+                    }
+
+                    return toClose;
+                }, {});
+                if (dropdownToClose) {
+                    openedContainers.remove(appendTo, dropdownToClose);
+                }
+            }
+        };
+
+        var closeDropdown = function(evt) {
+            // This method may still be called during the same mouse event that
+            // unbound this event handler. So check openScope before 
proceeding.
+            if (!openScope || !openScope.isOpen) { return; }
+
+            if (evt && openScope.getAutoClose() === 'disabled') { return; }
+
+            if (evt && evt.which === 3) { return; }
+
+            var toggleElement = openScope.getToggleElement();
+            if (evt && toggleElement && toggleElement[0].contains(evt.target)) 
{
+                return;
+            }
+
+            var dropdownElement = openScope.getDropdownElement();
+            if (evt && openScope.getAutoClose() === 'outsideClick' &&
+                dropdownElement && dropdownElement[0].contains(evt.target)) {
+                return;
+            }
+
+            openScope.focusToggleElement();
+            openScope.isOpen = false;
+
+            if (!$rootScope.$$phase) {
+                openScope.$apply();
+            }
+        };
+
+        this.keybindFilter = function(evt) {
+            if (!openScope) {
+                // see this.close as ESC could have been pressed which kills 
the scope so we can not proceed
+                return;
+            }
+
+            var dropdownElement = openScope.getDropdownElement();
+            var toggleElement = openScope.getToggleElement();
+            var dropdownElementTargeted = dropdownElement && 
dropdownElement[0].contains(evt.target);
+            var toggleElementTargeted = toggleElement && 
toggleElement[0].contains(evt.target);
+            if (evt.which === 27) {
+                evt.stopPropagation();
+                openScope.focusToggleElement();
+                closeDropdown();
+            } else if (openScope.isKeynavEnabled() && [38, 
40].indexOf(evt.which) !== -1 && openScope.isOpen && (dropdownElementTargeted 
|| toggleElementTargeted)) {
+                evt.preventDefault();
+                evt.stopPropagation();
+                openScope.focusDropdownEntry(evt.which);
+            }
+        };
+    }])
+
+    .controller('UibDropdownController', ['$scope', '$element', '$attrs', 
'$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', 
'$uibPosition', '$document', '$compile', '$templateRequest', function($scope, 
$element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, 
$position, $document, $compile, $templateRequest) {
+        var self = this,
+            scope = $scope.$new(), // create a child scope so we are not 
polluting original one
+            templateScope,
+            appendToOpenClass = dropdownConfig.appendToOpenClass,
+            openClass = dropdownConfig.openClass,
+            getIsOpen,
+            setIsOpen = angular.noop,
+            toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : 
angular.noop,
+            keynavEnabled = false,
+            selectedOption = null,
+            body = $document.find('body');
+
+        $element.addClass('dropdown');
+
+        this.init = function() {
+            if ($attrs.isOpen) {
+                getIsOpen = $parse($attrs.isOpen);
+                setIsOpen = getIsOpen.assign;
+
+                $scope.$watch(getIsOpen, function(value) {
+                    scope.isOpen = !!value;
+                });
+            }
+
+            keynavEnabled = angular.isDefined($attrs.keyboardNav);
+        };
+
+        this.toggle = function(open) {
+            scope.isOpen = arguments.length ? !!open : !scope.isOpen;
+            if (angular.isFunction(setIsOpen)) {
+                setIsOpen(scope, scope.isOpen);
+            }
+
+            return scope.isOpen;
+        };
+
+        // Allow other directives to watch status
+        this.isOpen = function() {
+            return scope.isOpen;
+        };
+
+        scope.getToggleElement = function() {
+            return self.toggleElement;
+        };
+
+        scope.getAutoClose = function() {
+            return $attrs.autoClose || 'always'; //or 'outsideClick' or 
'disabled'
+        };
+
+        scope.getElement = function() {
+            return $element;
+        };
+
+        scope.isKeynavEnabled = function() {
+            return keynavEnabled;
+        };
+
+        scope.focusDropdownEntry = function(keyCode) {
+            var elems = self.dropdownMenu ? //If append to body is used.
+                angular.element(self.dropdownMenu).find('a') :
+                $element.find('ul').eq(0).find('a');
+
+            switch (keyCode) {
+                case 40: {
+                    if (!angular.isNumber(self.selectedOption)) {
+                        self.selectedOption = 0;
+                    } else {
+                        self.selectedOption = self.selectedOption === 
elems.length - 1 ?
+                            self.selectedOption :
+                            self.selectedOption + 1;
+                    }
+                    break;
+                }
+                case 38: {
+                    if (!angular.isNumber(self.selectedOption)) {
+                        self.selectedOption = elems.length - 1;
+                    } else {
+                        self.selectedOption = self.selectedOption === 0 ?
+                            0 : self.selectedOption - 1;
+                    }
+                    break;
+                }
+            }
+            elems[self.selectedOption].focus();
+        };
+
+        scope.getDropdownElement = function() {
+            return self.dropdownMenu;
+        };
+
+        scope.focusToggleElement = function() {
+            if (self.toggleElement) {
+                self.toggleElement[0].focus();
+            }
+        };
+
+        function removeDropdownMenu() {
+            $element.append(self.dropdownMenu);
+        }
+
+        scope.$watch('isOpen', function(isOpen, wasOpen) {
+            var appendTo = null,
+                appendToBody = false;
+
+            if (angular.isDefined($attrs.dropdownAppendTo)) {
+                var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
+                if (appendToEl) {
+                    appendTo = angular.element(appendToEl);
+                }
+            }
+
+            if (angular.isDefined($attrs.dropdownAppendToBody)) {
+                var appendToBodyValue = 
$parse($attrs.dropdownAppendToBody)(scope);
+                if (appendToBodyValue !== false) {
+                    appendToBody = true;
+                }
+            }
+
+            if (appendToBody && !appendTo) {
+                appendTo = body;
+            }
+
+            if (appendTo && self.dropdownMenu) {
+                if (isOpen) {
+                    appendTo.append(self.dropdownMenu);
+                    $element.on('$destroy', removeDropdownMenu);
+                } else {
+                    $element.off('$destroy', removeDropdownMenu);
+                    removeDropdownMenu();
+                }
+            }
+
+            if (appendTo && self.dropdownMenu) {
+                var pos = $position.positionElements($element, 
self.dropdownMenu, 'bottom-left', true),
+                    css,
+                    rightalign,
+                    scrollbarPadding,
+                    scrollbarWidth = 0;
+
+                css = {
+                    top: pos.top + 'px',
+                    display: isOpen ? 'block' : 'none'
+                };
+
+                rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
+                if (!rightalign) {
+                    css.left = pos.left + 'px';
+                    css.right = 'auto';
+                } else {
+                    css.left = 'auto';
+                    scrollbarPadding = $position.scrollbarPadding(appendTo);
+
+                    if (scrollbarPadding.heightOverflow && 
scrollbarPadding.scrollbarWidth) {
+                        scrollbarWidth = scrollbarPadding.scrollbarWidth;
+                    }
+
+                    css.right = window.innerWidth - scrollbarWidth -
+                        (pos.left + $element.prop('offsetWidth')) + 'px';
+                }
+
+                // Need to adjust our positioning to be relative to the 
appendTo container
+                // if it's not the body element
+                if (!appendToBody) {
+                    var appendOffset = $position.offset(appendTo);
+
+                    css.top = pos.top - appendOffset.top + 'px';
+
+                    if (!rightalign) {
+                        css.left = pos.left - appendOffset.left + 'px';
+                    } else {
+                        css.right = window.innerWidth -
+                            (pos.left - appendOffset.left + 
$element.prop('offsetWidth')) + 'px';
+                    }
+                }
+
+                self.dropdownMenu.css(css);
+            }
+
+            var openContainer = appendTo ? appendTo : $element;
+            var dropdownOpenClass = appendTo ? appendToOpenClass : openClass;
+            var hasOpenClass = openContainer.hasClass(dropdownOpenClass);
+            var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo);
+
+            if (hasOpenClass === !isOpen) {
+                var toggleClass;
+                if (appendTo) {
+                    toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass';
+                } else {
+                    toggleClass = isOpen ? 'addClass' : 'removeClass';
+                }
+                $animate[toggleClass](openContainer, 
dropdownOpenClass).then(function() {
+                    if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
+                        toggleInvoker($scope, { open: !!isOpen });
+                    }
+                });
+            }
+
+            if (isOpen) {
+                if (self.dropdownMenuTemplateUrl) {
+                    
$templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
+                        templateScope = scope.$new();
+                        $compile(tplContent.trim())(templateScope, 
function(dropdownElement) {
+                            var newEl = dropdownElement;
+                            self.dropdownMenu.replaceWith(newEl);
+                            self.dropdownMenu = newEl;
+                            $document.on('keydown', 
uibDropdownService.keybindFilter);
+                        });
+                    });
+                } else {
+                    $document.on('keydown', uibDropdownService.keybindFilter);
+                }
+
+                scope.focusToggleElement();
+                uibDropdownService.open(scope, $element, appendTo);
+            } else {
+                uibDropdownService.close(scope, $element, appendTo);
+                if (self.dropdownMenuTemplateUrl) {
+                    if (templateScope) {
+                        templateScope.$destroy();
+                    }
+                    var newEl = angular.element('<ul 
class="dropdown-menu"></ul>');
+                    self.dropdownMenu.replaceWith(newEl);
+                    self.dropdownMenu = newEl;
+                }
+
+                self.selectedOption = null;
+            }
+
+            if (angular.isFunction(setIsOpen)) {
+                setIsOpen($scope, isOpen);
+            }
+        });
+    }])
+
+    .directive('uibDropdown', function() {
+        return {
+            controller: 'UibDropdownController',
+            link: function(scope, element, attrs, dropdownCtrl) {
+                dropdownCtrl.init();
+            }
+        };
+    })
+
+    .directive('uibDropdownMenu', function() {
+        return {
+            restrict: 'A',
+            require: '?^uibDropdown',
+            link: function(scope, element, attrs, dropdownCtrl) {
+                if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
+                    return;
+                }
+
+                element.addClass('dropdown-menu');
+
+                var tplUrl = attrs.templateUrl;
+                if (tplUrl) {
+                    dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
+                }
+
+                if (!dropdownCtrl.dropdownMenu) {
+                    dropdownCtrl.dropdownMenu = element;
+                }
+            }
+        };
+    })
+
+    .directive('uibDropdownToggle', function() {
+        return {
+            require: '?^uibDropdown',
+            link: function(scope, element, attrs, dropdownCtrl) {
+                if (!dropdownCtrl) {
+                    return;
+                }
+
+                element.addClass('dropdown-toggle');
+
+                dropdownCtrl.toggleElement = element;
+
+                var toggleDropdown = function(event) {
+                    event.preventDefault();
+
+                    if (!element.hasClass('disabled') && !attrs.disabled) {
+                        scope.$apply(function() {
+                            dropdownCtrl.toggle();
+                        });
+                    }
+                };
+
+                element.on('click', toggleDropdown);
+
+                // WAI-ARIA
+                element.attr({ 'aria-haspopup': true, 'aria-expanded': false 
});
+                scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
+                    element.attr('aria-expanded', !!isOpen);
+                });
+
+                scope.$on('$destroy', function() {
+                    element.off('click', toggleDropdown);
+                });
+            }
+        };
+    });

Reply via email to