You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2017/11/07 13:57:35 UTC

ambari git commit: AMBARI-22374 Log Search UI: button with caret doesn't toggle dropdown is some cases. (Istvan Tobias via ababiichuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk ec3f1e4dc -> f74b2f873


AMBARI-22374 Log Search UI: button with caret doesn't toggle dropdown is some cases. (Istvan Tobias via ababiichuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/f74b2f87
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/f74b2f87
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/f74b2f87

Branch: refs/heads/trunk
Commit: f74b2f873b147e7c5639e2a342fc0627b8335f47
Parents: ec3f1e4
Author: Istvan Tobias <to...@gmail.com>
Authored: Tue Nov 7 15:27:38 2017 +0200
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Tue Nov 7 15:27:38 2017 +0200

----------------------------------------------------------------------
 .../menu-button/menu-button.component.html      |  17 +--
 .../menu-button/menu-button.component.less      |  22 ++-
 .../menu-button/menu-button.component.ts        | 144 ++++++++++++++++---
 3 files changed, 154 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/f74b2f87/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
index ca70927..5e2b15f 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
@@ -15,14 +15,15 @@
   limitations under the License.
 -->
 
-<div #dropdown [ngClass]="{'dropdown': hasSubItems, 'text-center': true}">
-  <a [ngClass]="iconClass + ' icon'" (mousedown)="onMouseDown($event)" (mouseup)="onMouseUp($event)"
-     (click)="$event.stopPropagation()"></a>
-  <a #dropdownToggle class="dropdown-toggle caret" data-toggle="dropdown" *ngIf="hasCaret"></a>
-  <br>
-  <a *ngIf="label" (mousedown)="onMouseDown($event)" [ngClass]="labelClass" (mouseup)="onMouseUp($event)"
-     (click)="$event.stopPropagation()">{{label}}</a>
-  <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems" (selectedItemChange)="updateValue($event)"
+<div #dropdown [ngClass]="{'dropdown': hasSubItems, 'text-center': true, 'open': dropdownIsOpen}">
+  <a class="dropdown-toggle" [ngClass]="(labelClass || '') + (hasCaret ? ' has-caret' : '')"
+    (click)="onMouseClick($event)"
+    (mousedown)="onMouseDown($event)">
+    <i *ngIf="iconClass" [ngClass]="['icon', iconClass]"></i>
+    <i *ngIf="hasCaret" [ngClass]="['fa ', caretClass ]"></i>
+    <span *ngIf="label" class="menu-button-label">{{label}}</span>
+  </a>
+  <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems" (selectedItemChange)="onDropdownItemChange($event)"
       [isMultipleChoice]="isMultipleChoice" [additionalLabelComponentSetter]="additionalLabelComponentSetter"
       [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': isRightAlign}"></ul>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/f74b2f87/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
index 615db24..0207561 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
@@ -21,14 +21,26 @@
   cursor: pointer;
   display: inline-block;
   position: relative;
-  a:hover, a:focus {
+  a {
+    text-align: center;
     text-decoration: none;
+    i {
+      color: @link-color;
+      display: inline-block;
+      position: relative;
+      &.fa-caret-down {
+        padding: 0 .25em;
+      }
+    }
+    .menu-button-label {
+      display: block;
+    }
   }
-
-  .icon {
-    padding: @icon-padding;
+  a:hover, a:focus {
+    i {
+      color: @link-hover-color;
+    }
   }
-
   .unstyled-link {
     color: inherit;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/f74b2f87/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
index 0aa7c7e..5932e1b 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
@@ -19,7 +19,6 @@
 import {Component, Input, ViewChild, ElementRef} from '@angular/core';
 import {ListItem} from '@app/classes/list-item';
 import {ComponentActionsService} from '@app/services/component-actions.service';
-import * as $ from 'jquery';
 
 @Component({
   selector: 'menu-button',
@@ -64,6 +63,37 @@ export class MenuButtonComponent {
   @Input()
   badge: string;
 
+  @Input()
+  caretClass: string = 'fa-caret-down';
+
+  /**
+   * The minimum time to handle a mousedown as a longclick. Default is 500 ms (0.5sec)
+   * @default 500
+   * @type {number}
+   */
+  @Input()
+  minLongClickDelay: number = 500;
+
+  /**
+   * The maximum milliseconds to wait for longclick ends. The default is 0 which means no upper limit.
+   * @default 0
+   * @type {number}
+   */
+  @Input()
+  maxLongClickDelay: number = 0;
+
+  /**
+   * This is a private property to indicate the mousedown timestamp, so that we can check it when teh click event
+   * has been triggered.
+   */
+  private mouseDownTimestamp: number;
+
+  /**
+   * Indicates if the dropdown list is open or not. So that we use internal state to display or hide the dropdown.
+   * @type {boolean}
+   */
+  private dropdownIsOpen: boolean = false;
+
   get hasSubItems(): boolean {
     return Boolean(this.subItems && this.subItems.length);
   }
@@ -72,26 +102,108 @@ export class MenuButtonComponent {
     return this.hasSubItems && !this.hideCaret;
   }
 
-  private clickStartTime: number;
-
-  private readonly longClickInterval = 1000;
-
-  onMouseDown(event: MouseEvent): void {
-    if (this.action && event.button === 0) {
-      this.clickStartTime = (new Date()).getTime();
+  /**
+   * Handling the click event on the component element.
+   * Two goal:
+   * - check if we have a 'longclick' event and open the dropdown (if any) when longclick event happened
+   * - trigger the action or the dropdown open depending on the target element (caret will open the dropdown otherwise
+   * trigger the action.
+   * @param {MouseEvent} event
+   */
+  onMouseClick(event: MouseEvent): void {
+    let el = <HTMLElement>event.target;
+    let now = Date.now();
+    let mdt = this.mouseDownTimestamp; // mousedown time
+    let isLongClick = mdt && mdt + this.minLongClickDelay <= now && (
+      !this.maxLongClickDelay || mdt + this.maxLongClickDelay >= now
+    );
+    let openDropdown = this.hasSubItems && (
+      el.classList.contains(this.caretClass) || isLongClick || !this.actions[this.action]
+    );
+    if (openDropdown && this.dropdown) {
+      if (this.toggleDropdown()) {
+        this.listenToClickOut();
+      }
+    } else if (this.action) {
+      this.actions[this.action]();
     }
+    this.mouseDownTimestamp = 0;
+    event.preventDefault();
   }
 
-  onMouseUp(event: MouseEvent): void {
-    if (event.button === 0) {
-      const clickEndTime = (new Date()).getTime();
-      if (this.hasSubItems && (!this.action || clickEndTime - this.clickStartTime >= this.longClickInterval)) {
-        $(this.dropdown.nativeElement).toggleClass('open');
-      } else if (this.action) {
-        this.actions[this.action]();
+  /**
+   * Listening the click event on the document so that we can hide our dropdown list if the event source is not the
+   * component.
+   */
+  private listenToClickOut = (): void => {
+    this.dropdownIsOpen && document.addEventListener('click', this.onDocumentMouseClick);
+  };
+
+  /**
+   * Handling the click event on the document to hide the dropdown list if it needs.
+   * @param {MouseEvent} event
+   */
+  private onDocumentMouseClick = (event: MouseEvent): void => {
+    let el = <HTMLElement>event.target;
+    if (!this.dropdown.nativeElement.contains(el)) {
+      this.closeDropdown();
+      this.removeDocumentClickListener()
+    }
+  };
+
+  /**
+   * Handling the mousedown event, so that we can check the long clicks and open the dropdown if any.
+   * @param {MouseEvent} event
+   */
+  onMouseDown = (event: MouseEvent): void => {
+    if (this.hasSubItems) {
+      let el = <HTMLElement>event.target;
+      if (!el.classList.contains(this.caretClass)) {
+        this.mouseDownTimestamp = Date.now();
       }
-      event.stopPropagation();
     }
+  };
+
+  /**
+   * The goal is to have one and only one place where we open the dropdown. So that later if we need to change the way
+   * how we do, it will be easier.
+   */
+  private openDropdown():void {
+    this.dropdownIsOpen = true;
+  }
+
+  /**
+   * The goal is to have one and only one place where we close the dropdown. So that later if we need to change the way
+   * how we do, it will be easier.
+   */
+  private closeDropdown():void {
+    this.dropdownIsOpen = false;
+  }
+
+  /**
+   * Just a simple helper method to make the dropdown toggle more easy.
+   * @returns {boolean} It will return the open state of the dropdown;
+   */
+  private toggleDropdown(): boolean {
+    this[this.dropdownIsOpen ? 'closeDropdown' : 'openDropdown']();
+    return this.dropdownIsOpen;
+  }
+
+  /**
+   * The goal is to simply remove the click event listeners from the document.
+   */
+  private removeDocumentClickListener(): void {
+    document.removeEventListener('click', this.onDocumentMouseClick);
+  }
+
+  /**
+   * The main goal if this function is tho handle the item change event on the child dropdown list.
+   * Should update the value and close the dropdown if it is not multiple choice type.
+   * @param {ListItem} options The selected item(s) from the dropdown list.
+   */
+  onDropdownItemChange(options: ListItem) {
+    this.updateValue(options);
+    !this.isMultipleChoice && this.closeDropdown();
   }
 
   updateValue(options: ListItem) {