You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2020/03/02 08:23:21 UTC

[myfaces-tobago] branch master updated (13d5516 -> 9f1918c)

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

hnoeth pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git.


    from 13d5516  release Tobago 2.4.2
     new 9f6eb87  tobago-dropdown: custom elements, remove jQuery
     new fadb659  tobago-dropdown: custom elements, remove jQuery
     new 7e88b3e  tobago-dropdown: custom elements, remove jQuery
     new ccb0661  tobago-dropdown: custom elements, remove jQuery
     new 9330503  tobago-dropdown: custom elements, remove jQuery
     new 9f1918c  tobago-dropdown: add support for 'tab' key

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../myfaces/tobago/renderkit/css/TobagoClass.java  |   2 +
 tobago-core/src/main/resources/scss/_tobago.scss   |  19 +-
 .../src/main/npm/ts/tobago-dropdown.ts             | 267 +++++++++++++++++++--
 3 files changed, 264 insertions(+), 24 deletions(-)


[myfaces-tobago] 04/06: tobago-dropdown: custom elements, remove jQuery

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit ccb06612b0161993c218f2c12cbffff6e4bf2895
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Wed Feb 26 18:52:34 2020 +0100

    tobago-dropdown: custom elements, remove jQuery
    
    * activate/deactivate entries with mouse
    * focus() manage CSS classes
    
    issue: TOBAGO-1633: TS refactoring
---
 .../src/main/npm/ts/tobago-dropdown.ts             | 61 ++++++++++++++++------
 1 file changed, 44 insertions(+), 17 deletions(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
index 55960a5..3af1d0f 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
@@ -27,7 +27,6 @@ const Event = {
 class Dropdown extends HTMLElement {
 
   private dropdownEntries: DropdownEntry[] = [];
-  private activeDropdownEntry: DropdownEntry;
 
   constructor() {
     super();
@@ -83,7 +82,6 @@ class Dropdown extends HTMLElement {
       event.preventDefault();
       event.stopPropagation();
       this.openDropdown();
-      this.activeDropdownEntry = this.dropdownEntries[0];
 
       const interval = setInterval(() => {
         if (this.dropdownVisible()) {
@@ -93,21 +91,13 @@ class Dropdown extends HTMLElement {
       }, 0);
     } else if (this.activeDropdownEntry && this.dropdownVisible()) {
       if (event.code === "ArrowUp" && this.activeDropdownEntry.previous) {
-        this.activeDropdownEntry.clearCss();
-        this.activeDropdownEntry = this.activeDropdownEntry.previous;
-        this.activeDropdownEntry.focus();
+        this.activeDropdownEntry.previous.focus();
       } else if (event.code === "ArrowDown" && this.activeDropdownEntry.next) {
-        this.activeDropdownEntry.clearCss();
-        this.activeDropdownEntry = this.activeDropdownEntry.next;
-        this.activeDropdownEntry.focus();
+        this.activeDropdownEntry.next.focus();
       } else if (event.code === "ArrowRight" && this.activeDropdownEntry.children.length > 0) {
-        this.activeDropdownEntry = this.activeDropdownEntry.children[0];
-        this.activeDropdownEntry.focus();
+        this.activeDropdownEntry.children[0].focus();
       } else if (event.code === "ArrowLeft" && this.activeDropdownEntry.parent) {
-        this.activeDropdownEntry.clearCss();
-        this.activeDropdownEntry = this.activeDropdownEntry.parent;
-        this.activeDropdownEntry.clearCss();
-        this.activeDropdownEntry.focus();
+        this.activeDropdownEntry.parent.focus();
       }
     }
   }
@@ -123,7 +113,7 @@ class Dropdown extends HTMLElement {
     }
 
     for (const dropdownEntry of this.dropdownEntries) {
-      dropdownEntry.clearCss();
+      dropdownEntry.clear();
     }
 
     this.dropdownMenu.classList.add("show");
@@ -164,6 +154,15 @@ class Dropdown extends HTMLElement {
     return root.querySelector(".tobago-page-menuStore");
   }
 
+  private get activeDropdownEntry(): DropdownEntry {
+    for (const dropdownEntry of this.dropdownEntries) {
+      if (dropdownEntry.active) {
+        return dropdownEntry;
+      }
+    }
+    return this.dropdownEntries[0];
+  }
+
   private createDropdownEntries(dropdownMenu: HTMLDivElement, parent: DropdownEntry): void {
     let lastDropdownEntry: DropdownEntry = null;
 
@@ -215,6 +214,7 @@ class DropdownEntry {
   private _children: DropdownEntry[] = [];
   private readonly _baseElement: HTMLElement;
   private readonly focusElement: HTMLElement;
+  private _active: boolean;
 
   constructor(dropdownItem: HTMLElement) {
     this._baseElement = dropdownItem;
@@ -225,6 +225,17 @@ class DropdownEntry {
     } else {
       this.focusElement = dropdownItem;
     }
+
+    this._baseElement.addEventListener("mouseenter", this.activate.bind(this));
+    this._baseElement.addEventListener("mouseleave", this.deactivate.bind(this));
+  }
+
+  activate(event: MouseEvent): void {
+    this.active = true;
+  }
+
+  deactivate(event: MouseEvent): void {
+    this.active = false;
   }
 
   get previous(): DropdownEntry {
@@ -259,18 +270,34 @@ class DropdownEntry {
     this._children = value;
   }
 
+  get active(): boolean {
+    return this._active;
+  }
+
+  set active(value: boolean) {
+    this._active = value;
+  }
+
   public focus(): void {
+    this.previous?.clear();
+    this.next?.clear();
     if (this.parent) {
+      this.parent.active = false;
       this.parent._baseElement.classList.add("tobago-dropdown-open");
     }
-
+    for (const child of this.children) {
+      child.clear();
+    }
+    this._baseElement.classList.remove("tobago-dropdown-open");
     this._baseElement.classList.add("tobago-dropdown-selected");
+    this.active = true;
     this.focusElement.focus();
   }
 
-  public clearCss(): void {
+  public clear(): void {
     this._baseElement.classList.remove("tobago-dropdown-open");
     this._baseElement.classList.remove("tobago-dropdown-selected");
+    this.active = false;
   }
 }
 


[myfaces-tobago] 03/06: tobago-dropdown: custom elements, remove jQuery

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 7e88b3ef21508a0b4de6791766810ea04fc8864d
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Wed Feb 26 17:02:36 2020 +0100

    tobago-dropdown: custom elements, remove jQuery
    
    * improve toggleButtonSelected
    * fix: mouse click
    
    issue: TOBAGO-1633: TS refactoring
---
 .../src/main/npm/ts/tobago-dropdown.ts                     | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
index 9e4f1be..55960a5 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
@@ -57,7 +57,8 @@ class Dropdown extends HTMLElement {
   }
 
   mouseupOnDocument(event: MouseEvent): void {
-    if (!this.toggleButtonSelected() && this.dropdownVisible()) {
+    if (!this.toggleButtonSelected(event) && this.dropdownVisible()
+        && !this.dropdownMenu.contains(event.target as HTMLElement)) {
       this.closeDropdown();
     }
   }
@@ -67,7 +68,7 @@ class Dropdown extends HTMLElement {
       event.preventDefault();
       event.stopPropagation();
       this.closeDropdown();
-    } else if ((this.toggleButtonSelected() || this.dropdownVisible())
+    } else if ((this.toggleButtonSelected(event) || this.dropdownVisible())
         && (event.code === "ArrowUp" || event.code === "ArrowDown"
             || event.code === "ArrowLeft" || event.code === "ArrowRight")) {
       // prevent scrolling with arrow keys
@@ -77,9 +78,7 @@ class Dropdown extends HTMLElement {
   }
 
   keyupOnDocument(event: KeyboardEvent): void {
-    const root = this.getRootNode() as ShadowRoot | Document;
-
-    if (this.toggleButtonSelected() && !this.dropdownVisible()
+    if (this.toggleButtonSelected(event) && !this.dropdownVisible()
         && (event.code === "ArrowUp" || event.code === "ArrowDown")) {
       event.preventDefault();
       event.stopPropagation();
@@ -142,9 +141,8 @@ class Dropdown extends HTMLElement {
     return this.querySelector(":scope > button[data-toggle='dropdown']");
   }
 
-  private toggleButtonSelected(): boolean {
-    const root = this.getRootNode() as ShadowRoot | Document;
-    return root.activeElement === this.toggleButton;
+  private toggleButtonSelected(event: Event): boolean {
+    return this.toggleButton.contains(event.target as HTMLElement);
   }
 
   private inStickyHeader(): boolean {


[myfaces-tobago] 05/06: tobago-dropdown: custom elements, remove jQuery

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 9330503864278b8875788046f82d43fbabab445a
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Thu Feb 27 15:57:08 2020 +0100

    tobago-dropdown: custom elements, remove jQuery
    
    * use only keydown event
    
    issue: TOBAGO-1633: TS refactoring
---
 .../src/main/npm/ts/tobago-dropdown.ts             | 39 +++++++++++-----------
 1 file changed, 20 insertions(+), 19 deletions(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
index 3af1d0f..908a24f 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
@@ -38,7 +38,6 @@ class Dropdown extends HTMLElement {
       this.toggleButton.addEventListener("click", this.toggleDropdown.bind(this));
       root.addEventListener("mouseup", this.mouseupOnDocument.bind(this));
       root.addEventListener("keydown", this.keydownOnDocument.bind(this));
-      root.addEventListener("keyup", this.keyupOnDocument.bind(this));
     }
   }
 
@@ -63,20 +62,6 @@ class Dropdown extends HTMLElement {
   }
 
   keydownOnDocument(event: KeyboardEvent): void {
-    if (this.dropdownVisible() && event.code === "Escape") {
-      event.preventDefault();
-      event.stopPropagation();
-      this.closeDropdown();
-    } else if ((this.toggleButtonSelected(event) || this.dropdownVisible())
-        && (event.code === "ArrowUp" || event.code === "ArrowDown"
-            || event.code === "ArrowLeft" || event.code === "ArrowRight")) {
-      // prevent scrolling with arrow keys
-      event.preventDefault();
-      event.stopPropagation();
-    }
-  }
-
-  keyupOnDocument(event: KeyboardEvent): void {
     if (this.toggleButtonSelected(event) && !this.dropdownVisible()
         && (event.code === "ArrowUp" || event.code === "ArrowDown")) {
       event.preventDefault();
@@ -85,12 +70,24 @@ class Dropdown extends HTMLElement {
 
       const interval = setInterval(() => {
         if (this.dropdownVisible()) {
-          this.activeDropdownEntry.focus();
+
+          if (this.activeDropdownEntry) {
+            this.activeDropdownEntry.focus();
+          } else {
+            this.dropdownEntries[0].focus();
+          }
           clearInterval(interval);
         }
       }, 0);
-    } else if (this.activeDropdownEntry && this.dropdownVisible()) {
-      if (event.code === "ArrowUp" && this.activeDropdownEntry.previous) {
+    } else if (this.dropdownVisible()
+        && (event.code === "ArrowUp" || event.code === "ArrowDown"
+            || event.code === "ArrowLeft" || event.code === "ArrowRight")) {
+      event.preventDefault();
+      event.stopPropagation();
+
+      if (!this.activeDropdownEntry) {
+        this.dropdownEntries[0].focus();
+      } else if (event.code === "ArrowUp" && this.activeDropdownEntry.previous) {
         this.activeDropdownEntry.previous.focus();
       } else if (event.code === "ArrowDown" && this.activeDropdownEntry.next) {
         this.activeDropdownEntry.next.focus();
@@ -99,6 +96,10 @@ class Dropdown extends HTMLElement {
       } else if (event.code === "ArrowLeft" && this.activeDropdownEntry.parent) {
         this.activeDropdownEntry.parent.focus();
       }
+    } else if (this.dropdownVisible() && event.code === "Escape") {
+      event.preventDefault();
+      event.stopPropagation();
+      this.closeDropdown();
     }
   }
 
@@ -160,7 +161,7 @@ class Dropdown extends HTMLElement {
         return dropdownEntry;
       }
     }
-    return this.dropdownEntries[0];
+    return null;
   }
 
   private createDropdownEntries(dropdownMenu: HTMLDivElement, parent: DropdownEntry): void {


[myfaces-tobago] 06/06: tobago-dropdown: add support for 'tab' key

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 9f1918c8e5f3dd4aff173be02289c1c585a96f27
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Thu Feb 27 17:14:23 2020 +0100

    tobago-dropdown: add support for 'tab' key
    
    issue: TOBAGO-1633: TS refactoring
---
 .../src/main/npm/ts/tobago-dropdown.ts             | 26 +++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
index 908a24f..e0da2ab 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
@@ -81,7 +81,8 @@ class Dropdown extends HTMLElement {
       }, 0);
     } else if (this.dropdownVisible()
         && (event.code === "ArrowUp" || event.code === "ArrowDown"
-            || event.code === "ArrowLeft" || event.code === "ArrowRight")) {
+            || event.code === "ArrowLeft" || event.code === "ArrowRight"
+            || event.code === "Tab")) {
       event.preventDefault();
       event.stopPropagation();
 
@@ -95,6 +96,29 @@ class Dropdown extends HTMLElement {
         this.activeDropdownEntry.children[0].focus();
       } else if (event.code === "ArrowLeft" && this.activeDropdownEntry.parent) {
         this.activeDropdownEntry.parent.focus();
+      } else if (!event.shiftKey && event.code === "Tab") {
+        if (this.activeDropdownEntry.children.length > 0) {
+          this.activeDropdownEntry.children[0].focus();
+        } else if (this.activeDropdownEntry.next) {
+          this.activeDropdownEntry.next.focus();
+        } else {
+          let parent: DropdownEntry = this.activeDropdownEntry.parent;
+          while (parent) {
+            if (parent.next) {
+              this.activeDropdownEntry.clear();
+              parent.next.focus();
+              break;
+            } else {
+              parent = parent.parent;
+            }
+          }
+        }
+      } else if (event.shiftKey && event.code === "Tab") {
+        if (this.activeDropdownEntry.previous) {
+          this.activeDropdownEntry.previous.focus();
+        } else if (this.activeDropdownEntry.parent) {
+          this.activeDropdownEntry.parent.focus();
+        }
       }
     } else if (this.dropdownVisible() && event.code === "Escape") {
       event.preventDefault();


[myfaces-tobago] 01/06: tobago-dropdown: custom elements, remove jQuery

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit 9f6eb87e36722a9a7b08d719887b3fc59d2895a2
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Wed Feb 26 13:21:43 2020 +0100

    tobago-dropdown: custom elements, remove jQuery
    
    * implement key navigation
    
    issue: TOBAGO-1633: TS refactoring
---
 .../myfaces/tobago/renderkit/css/TobagoClass.java  |   2 +
 tobago-core/src/main/resources/scss/_tobago.scss   |  19 +-
 .../src/main/npm/ts/tobago-dropdown.ts             | 205 +++++++++++++++++++--
 3 files changed, 202 insertions(+), 24 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java
index bc2ab9a..b0b4d16 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/TobagoClass.java
@@ -121,6 +121,8 @@ public enum TobagoClass implements CssItem {
   BUTTONS("tobago-buttons"),
   COLLAPSED("tobago-collapsed"),
   DATE("tobago-date"),
+  DROPDOWN__OPEN("tobago-dropdown-open"),
+  DROPDOWN__SELECTED("tobago-dropdown-selected"),
   FILE("tobago-file"),
   FILE__PLACEHOLDER("tobago-file-placeholder"),
   FLEX_LAYOUT("tobago-flexLayout"),
diff --git a/tobago-core/src/main/resources/scss/_tobago.scss b/tobago-core/src/main/resources/scss/_tobago.scss
index ef9638a..dd11466 100644
--- a/tobago-core/src/main/resources/scss/_tobago.scss
+++ b/tobago-core/src/main/resources/scss/_tobago.scss
@@ -74,6 +74,7 @@ $input-focus-box-shadow: $input-btn-focus-box-shadow !default;
 $form-check-inline-input-margin-x: 0.75rem;
 $input-placeholder-color: $gray-600 !default;
 $grid-gutter-width: 30px !default;
+$dropdown-link-hover-color: darken($gray-900, 5%) !default;
 
 $page-padding-top: 1rem;
 
@@ -600,6 +601,14 @@ tobago-dropdown {
   display: inline-block;
 }
 
+tobago-dropdown, .tobago-page-menuStore {
+  .tobago-dropdown-selected {
+    color: $dropdown-link-hover-color;
+    text-decoration: none;
+    @include gradient-bg($dropdown-link-hover-bg);
+  }
+}
+
 ul > tobago-dropdown {
   display: list-item;
 }
@@ -647,11 +656,13 @@ ul > tobago-dropdown {
     margin-right: -10px;
   }
 
-  &:hover > .dropdown-menu {
-    display: block;
+  &:hover, &.tobago-dropdown-open {
+    > .dropdown-menu {
+      display: block;
 
-    > a:after {
-      border-left-color: $dropdown-bg;
+      > a:after {
+        border-left-color: $dropdown-bg;
+      }
     }
   }
 
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
index 61e0898..f026821 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
@@ -19,39 +19,90 @@ import Popper from "popper.js";
 
 class Dropdown extends HTMLElement {
 
-  private closeFlag: boolean = false;
+  private dropdownEntries: DropdownEntry[] = [];
+  private activeDropdownEntry: DropdownEntry;
 
   constructor() {
     super();
+    if (!this.classList.contains("tobago-dropdown-submenu")) { // ignore submenus
+      const root = this.getRootNode() as ShadowRoot | Document;
 
-    this.toggleButton.addEventListener("mouseup", this.toggleDropdown.bind(this));
-    this.toggleButton.addEventListener("blur", this.setCloseFlag.bind(this));
-    window.addEventListener("mouseup", this.deselectComponent.bind(this));
+      this.createDropdownEntries(this.dropdownMenu, null);
+
+      this.toggleButton.addEventListener("click", this.toggleDropdown.bind(this));
+      root.addEventListener("mouseup", this.mouseupOnDocument.bind(this));
+      root.addEventListener("keydown", this.keydownOnDocument.bind(this));
+      root.addEventListener("keyup", this.keyupOnDocument.bind(this));
+    }
   }
 
   connectedCallback(): void {
-    //TODO add keyboard support
   }
 
   toggleDropdown(event: Event): void {
-    this.resetCloseFlag();
-
-    const visible: boolean = this.dropdownMenu.classList.contains("show");
-    if (visible) {
+    event.preventDefault();
+    event.stopPropagation();
+    if (this.dropdownVisible()) {
       this.closeDropdown();
     } else {
       this.openDropdown();
     }
   }
 
-  deselectComponent(event: Event): void {
-    const visible: boolean = this.dropdownMenu.classList.contains("show");
-    if (this.closeFlag && visible) {
-      this.resetCloseFlag();
+  mouseupOnDocument(event: MouseEvent): void {
+    if (!this.toggleButtonSelected() && this.dropdownVisible()) {
+      this.closeDropdown();
+    }
+  }
 
-      const target: HTMLElement = event.target as HTMLElement;
+  keydownOnDocument(event: KeyboardEvent): void {
+    if (this.dropdownVisible() && event.code === "Escape") {
+      event.preventDefault();
+      event.stopPropagation();
       this.closeDropdown();
-      target.dispatchEvent(new MouseEvent("click", {bubbles: true}));
+    } else if ((this.toggleButtonSelected() || this.dropdownVisible())
+        && (event.code === "ArrowUp" || event.code === "ArrowDown"
+            || event.code === "ArrowLeft" || event.code === "ArrowRight")) {
+      // prevent scrolling with arrow keys
+      event.preventDefault();
+      event.stopPropagation();
+    }
+  }
+
+  keyupOnDocument(event: KeyboardEvent): void {
+    const root = this.getRootNode() as ShadowRoot | Document;
+
+    if (this.toggleButtonSelected() && !this.dropdownVisible()
+        && (event.code === "ArrowUp" || event.code === "ArrowDown")) {
+      event.preventDefault();
+      event.stopPropagation();
+      this.openDropdown();
+      this.activeDropdownEntry = this.dropdownEntries[0];
+
+      const interval = setInterval(() => {
+        if (this.dropdownVisible()) {
+          this.activeDropdownEntry.focus();
+          clearInterval(interval);
+        }
+      }, 0);
+    } else if (this.activeDropdownEntry && this.dropdownVisible()) {
+      if (event.code === "ArrowUp" && this.activeDropdownEntry.previous) {
+        this.activeDropdownEntry.clearCss();
+        this.activeDropdownEntry = this.activeDropdownEntry.previous;
+        this.activeDropdownEntry.focus();
+      } else if (event.code === "ArrowDown" && this.activeDropdownEntry.next) {
+        this.activeDropdownEntry.clearCss();
+        this.activeDropdownEntry = this.activeDropdownEntry.next;
+        this.activeDropdownEntry.focus();
+      } else if (event.code === "ArrowRight" && this.activeDropdownEntry.children.length > 0) {
+        this.activeDropdownEntry = this.activeDropdownEntry.children[0];
+        this.activeDropdownEntry.focus();
+      } else if (event.code === "ArrowLeft" && this.activeDropdownEntry.parent) {
+        this.activeDropdownEntry.clearCss();
+        this.activeDropdownEntry = this.activeDropdownEntry.parent;
+        this.activeDropdownEntry.clearCss();
+        this.activeDropdownEntry.focus();
+      }
     }
   }
 
@@ -63,6 +114,10 @@ class Dropdown extends HTMLElement {
       });
     }
 
+    for (const dropdownEntry of this.dropdownEntries) {
+      dropdownEntry.clearCss();
+    }
+
     this.dropdownMenu.classList.add("show");
   }
 
@@ -75,9 +130,14 @@ class Dropdown extends HTMLElement {
     return this.querySelector(":scope > button[data-toggle='dropdown']");
   }
 
+  private toggleButtonSelected(): boolean {
+    const root = this.getRootNode() as ShadowRoot | Document;
+    return root.activeElement === this.toggleButton;
+  }
+
   private inStickyHeader(): boolean {
     const root = this.getRootNode() as ShadowRoot | Document;
-    return root.querySelector("header.tobago-header.sticky-top tobago-dropdown[id='" + this.id + "']") !== null;
+    return Boolean(root.querySelector("header.tobago-header.sticky-top tobago-dropdown[id='" + this.id + "']"));
   }
 
   private get dropdownMenu(): HTMLDivElement {
@@ -85,17 +145,122 @@ class Dropdown extends HTMLElement {
     return root.querySelector(".dropdown-menu[name='" + this.id + "']");
   }
 
+  private dropdownVisible(): boolean {
+    return this.dropdownMenu.classList.contains("show");
+  }
+
   private get menuStore(): HTMLDivElement {
     const root = this.getRootNode() as ShadowRoot | Document;
     return root.querySelector(".tobago-page-menuStore");
   }
 
-  setCloseFlag(event: Event): void {
-    this.closeFlag = true;
+  private createDropdownEntries(dropdownMenu: HTMLDivElement, parent: DropdownEntry): void {
+    let lastDropdownEntry: DropdownEntry = null;
+
+    for (const dropdownItem of dropdownMenu.children) {
+      if (dropdownItem.classList.contains("dropdown-item")) {
+        const entry = this.createDropdownEntry(dropdownItem as HTMLElement, parent, lastDropdownEntry);
+
+        lastDropdownEntry = entry;
+        this.dropdownEntries.push(entry);
+
+        if (dropdownItem.classList.contains("tobago-dropdown-submenu")) {
+          this.createDropdownEntries(dropdownItem.querySelector(".dropdown-menu"), entry);
+        }
+      } else {
+        const dropdownItems: NodeListOf<HTMLElement> = dropdownItem.querySelectorAll(".dropdown-item");
+        for (const dropdownItem of dropdownItems) {
+          const entry = this.createDropdownEntry(dropdownItem, parent, lastDropdownEntry);
+
+          lastDropdownEntry = entry;
+          this.dropdownEntries.push(entry);
+        }
+      }
+    }
+  }
+
+  private createDropdownEntry(
+      dropdownItem: HTMLElement, parent: DropdownEntry, previous: DropdownEntry): DropdownEntry {
+
+    const entry = new DropdownEntry(dropdownItem);
+    if (parent) {
+      entry.parent = parent;
+      parent.children.push(entry);
+    }
+
+    if (previous) {
+      previous.next = entry;
+      entry.previous = previous;
+    }
+
+    return entry;
+  }
+}
+
+class DropdownEntry {
+
+  private _previous: DropdownEntry;
+  private _next: DropdownEntry;
+  private _parent: DropdownEntry;
+  private _children: DropdownEntry[] = [];
+  private readonly _baseElement: HTMLElement;
+  private readonly focusElement: HTMLElement;
+
+  constructor(dropdownItem: HTMLElement) {
+    this._baseElement = dropdownItem;
+    if (dropdownItem.classList.contains("tobago-dropdown-submenu")) {
+      this.focusElement = dropdownItem.querySelector(".tobago-link");
+    } else if (dropdownItem.tagName === "LABEL") {
+      this.focusElement = dropdownItem.querySelector("input");
+    } else {
+      this.focusElement = dropdownItem;
+    }
+  }
+
+  get previous(): DropdownEntry {
+    return this._previous;
+  }
+
+  set previous(value: DropdownEntry) {
+    this._previous = value;
+  }
+
+  get next(): DropdownEntry {
+    return this._next;
+  }
+
+  set next(value: DropdownEntry) {
+    this._next = value;
+  }
+
+  get parent(): DropdownEntry {
+    return this._parent;
+  }
+
+  set parent(value: DropdownEntry) {
+    this._parent = value;
+  }
+
+  get children(): DropdownEntry[] {
+    return this._children;
+  }
+
+  set children(value: DropdownEntry[]) {
+    this._children = value;
+  }
+
+  public focus(): void {
+    if (this.parent) {
+      this.parent._baseElement.classList.add("tobago-dropdown-open");
+    }
+
+    this._baseElement.classList.add("tobago-dropdown-selected");
+    this.focusElement.focus();
   }
 
-  resetCloseFlag(): void {
-    this.closeFlag = false;
+  public clearCss(): void {
+    this._baseElement.classList.remove("tobago-dropdown-open");
+    this._baseElement.classList.remove("tobago-dropdown-selected");
   }
 }
 


[myfaces-tobago] 02/06: tobago-dropdown: custom elements, remove jQuery

Posted by hn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git

commit fadb65946d6748fc430a0484cffd56478c613d0b
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Wed Feb 26 15:55:44 2020 +0100

    tobago-dropdown: custom elements, remove jQuery
    
    * add events
    
    issue: TOBAGO-1633: TS refactoring
---
 .../tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
index f026821..9e4f1be 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-dropdown.ts
@@ -17,6 +17,13 @@
 
 import Popper from "popper.js";
 
+const Event = {
+  HIDE: "tobago.dropdown.hide",
+  HIDDEN: "tobago.dropdown.hidden",
+  SHOW: "tobago.dropdown.show",
+  SHOWN: "tobago.dropdown.shown"
+};
+
 class Dropdown extends HTMLElement {
 
   private dropdownEntries: DropdownEntry[] = [];
@@ -107,6 +114,8 @@ class Dropdown extends HTMLElement {
   }
 
   openDropdown(): void {
+    this.dispatchEvent(new CustomEvent(Event.HIDE));
+
     if (!this.inStickyHeader()) {
       this.menuStore.appendChild(this.dropdownMenu);
       new Popper(this.toggleButton, this.dropdownMenu, {
@@ -119,11 +128,14 @@ class Dropdown extends HTMLElement {
     }
 
     this.dropdownMenu.classList.add("show");
+    this.dispatchEvent(new CustomEvent(Event.HIDDEN));
   }
 
   closeDropdown(): void {
+    this.dispatchEvent(new CustomEvent(Event.SHOW));
     this.dropdownMenu.classList.remove("show");
     this.appendChild(this.dropdownMenu);
+    this.dispatchEvent(new CustomEvent(Event.SHOWN));
   }
 
   private get toggleButton(): HTMLElement {