tobago-dropdown: custom elements, remove jQuery
Using custom elements for dropdown menus and remove jQuery
issue: TOBAGO-1633: TS refactoring
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/CommandRendererBase.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/CommandRendererBase.java
index 2e6b5bc..6a9e0ac 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/CommandRendererBase.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/CommandRendererBase.java
@@ -174,6 +174,7 @@
BootstrapClass.DROPDOWN_MENU,
getDropdownCssItems(facesContext, command));
writer.writeAttribute(Arias.LABELLEDBY, "dropdownMenuButton", false);
+ writer.writeAttribute(HtmlAttributes.NAME, command.getClientId(facesContext), false);
for (final UIComponent child : component.getChildren()) {
if (child.isRendered()
@@ -238,7 +239,7 @@
final TobagoResponseWriter writer = getResponseWriter(facesContext);
if (parentOfCommands) {
- writer.startElement(HtmlElements.SPAN);
+ writer.startElement(HtmlElements.TOBAGO_DROPDOWN);
writer.writeIdAttribute(clientId);
writer.writeClassAttribute(
@@ -250,7 +251,7 @@
protected void encodeEndOuter(final FacesContext facesContext, final AbstractUICommand command) throws IOException {
final TobagoResponseWriter writer = getResponseWriter(facesContext);
if (command.isParentOfCommands()) {
- writer.endElement(HtmlElements.SPAN);
+ writer.endElement(HtmlElements.TOBAGO_DROPDOWN);
}
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/LinkInsideLinksRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/LinkInsideLinksRenderer.java
index 7e1d62d..0345145 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/LinkInsideLinksRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/LinkInsideLinksRenderer.java
@@ -37,7 +37,7 @@
final TobagoResponseWriter writer = getResponseWriter(facesContext);
- writer.startElement(HtmlElements.LI);
+ writer.startElement(HtmlElements.TOBAGO_DROPDOWN);
if (parentOfCommands) {
writer.writeIdAttribute(clientId);
}
@@ -50,7 +50,7 @@
@Override
protected void encodeEndOuter(final FacesContext facesContext, final AbstractUICommand command) throws IOException {
final TobagoResponseWriter writer = getResponseWriter(facesContext);
- writer.endElement(HtmlElements.LI);
+ writer.endElement(HtmlElements.TOBAGO_DROPDOWN);
}
@Override
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
index 82b99a5..3993af1 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlElements.java
@@ -135,6 +135,7 @@
TOBAGO_BEHAVIOR("tobago-behavior"),
TOBAGO_DATE("tobago-date"),
+ TOBAGO_DROPDOWN("tobago-dropdown"),
TOBAGO_FILE("tobago-file"),
TOBAGO_LABEL("tobago-label"),
TOBAGO_IN("tobago-in"),
diff --git a/tobago-core/src/main/resources/scss/_tobago.scss b/tobago-core/src/main/resources/scss/_tobago.scss
index 36f5902..a56b6a5 100644
--- a/tobago-core/src/main/resources/scss/_tobago.scss
+++ b/tobago-core/src/main/resources/scss/_tobago.scss
@@ -581,6 +581,15 @@
display: block;
}
+/* dropdown ------------------------------------------------------- */
+tobago-dropdown {
+ display: inline-block;
+}
+
+ul > tobago-dropdown {
+ display: list-item;
+}
+
/* dropdown-submenu ------------------------------------------------------- */
.tobago-dropdown-submenu {
cursor: pointer;
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 7041506..86d18d2 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
@@ -15,61 +15,80 @@
* limitations under the License.
*/
-import {Listener, Order, Phase} from "./tobago-listener";
+import "/webjars/popper.js/1.14.3/umd/popper.js";
+import Popper from "popper.js";
-class Dropdown {
+class Dropdown extends HTMLElement {
- static init(element: HTMLElement): void {
- const $dropdownMenus = jQuery(":not(.tobago-page-menuStore) > .dropdown-menu");
- const $tobagoPageMenuStore = jQuery(".tobago-page-menuStore");
+ private blurFlag: boolean = false;
- $dropdownMenus.each(function (): void {
- const $dropdownMenu = jQuery(this);
- const $parent = $dropdownMenu.parent();
+ constructor() {
+ super();
+ }
- if (!$parent.hasClass("tobago-dropdown-submenu")
- && $parent.closest(".navbar").length === 0) {
+ connectedCallback(): void {
+ this.toggle.addEventListener("click", this.toggleDropdown.bind(this));
+ this.toggle.addEventListener("blur", this.deselectComponent.bind(this));
+ window.addEventListener("mouseup", this.deselectComponent.bind(this));
+ }
- // remove duplicated dropdown menus from menu store
- // this could happen if the dropdown component is updated by ajax
- removeDuplicates($dropdownMenu);
+ toggleDropdown(event: Event): void {
+ const visible: boolean = this.dropdownMenu.classList.contains("show");
+ if (visible) {
+ this.closeDropdown();
+ } else {
+ this.openDropdown();
+ }
+ }
- $parent.on("shown.bs.dropdown", function (event: Event): void {
- $tobagoPageMenuStore.append($dropdownMenu.detach());
- }).on("hidden.bs.dropdown", function (event: Event): void {
- $parent.append($dropdownMenu.detach());
- });
- }
- });
+ deselectComponent(event: Event): void {
+ if (event.type === "blur") {
+ this.blurFlag = true;
+ } else if (this.blurFlag) {
+ this.blurFlag = false;
+
+ const target: HTMLElement = event.target as HTMLElement;
+ this.closeDropdown();
+ target.dispatchEvent(new MouseEvent("click", {bubbles: true}));
+ }
+ }
+
+ openDropdown(): void {
+ if (!this.inStickyHeader()) {
+ this.menuStore.appendChild(this.dropdownMenu);
+ new Popper(this.toggle, this.dropdownMenu, {
+ placement: "bottom-start"
+ });
+ }
+
+ this.dropdownMenu.classList.add("show");
+ }
+
+ closeDropdown(): void {
+ this.dropdownMenu.classList.remove("show");
+ this.appendChild(this.dropdownMenu);
+ }
+
+ private get toggle(): HTMLElement {
+ return this.querySelector(":scope > [data-toggle='dropdown']");
+ }
+
+ private inStickyHeader(): boolean {
+ const root = this.getRootNode() as ShadowRoot | Document;
+ return root.querySelector("header.tobago-header.sticky-top tobago-dropdown[id='" + this.id + "']") !== null;
+ }
+
+ private get dropdownMenu(): HTMLDivElement {
+ const root = this.getRootNode() as ShadowRoot | Document;
+ return root.querySelector(".dropdown-menu[name='" + this.id + "']");
+ }
+
+ private get menuStore(): HTMLDivElement {
+ const root = this.getRootNode() as ShadowRoot | Document;
+ return root.querySelector(".tobago-page-menuStore");
}
}
-function removeDuplicates($dropdownMenu): void {
- const $menuStoreDropdowns = jQuery(".tobago-page-menuStore .dropdown-menu");
- // XXX todo: remove ts-ignore
- // @ts-ignore
- $menuStoreDropdowns.each(function (): boolean {
- const $menuStoreDropdown = jQuery(this);
-
- const dropdownIds = getIds($dropdownMenu);
- const menuStoreIds = getIds($menuStoreDropdown);
-
- for (let i = 0; i < dropdownIds.length; i++) {
- if (jQuery.inArray(dropdownIds[i], menuStoreIds) >= 0) {
- $menuStoreDropdown.remove();
- return false;
- }
- }
- });
-}
-
-// XXX todo: remove tslint
-// tslint:disable-next-line:typedef
-function getIds($dropdownMenu) {
- return $dropdownMenu.find("[id]").map(function (): string {
- return this.id;
- });
-}
-
-Listener.register(Dropdown.init, Phase.DOCUMENT_READY, Order.NORMAL);
-Listener.register(Dropdown.init, Phase.AFTER_UPDATE, Order.NORMAL);
+document.addEventListener("DOMContentLoaded", function (event: Event): void {
+ window.customElements.define("tobago-dropdown", Dropdown);
+});