tobago-popover: custom elements, remove jQuery
A new custom element named tobago-popover is added. It replace the
message layout button and help button which were using bootstrap.js with
jQuery.
issue: TOBAGO-1633: TS refactoring
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/MessageLayoutRendererBase.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/MessageLayoutRendererBase.java
index 67e81ff..ca3d051 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/MessageLayoutRendererBase.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/MessageLayoutRendererBase.java
@@ -24,9 +24,9 @@
import org.apache.myfaces.tobago.component.SupportsLabelLayout;
import org.apache.myfaces.tobago.internal.util.StringUtils;
import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
+import org.apache.myfaces.tobago.renderkit.css.CssItem;
import org.apache.myfaces.tobago.renderkit.css.Icons;
import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
-import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
@@ -100,10 +100,10 @@
if (hasMessage || hasHelp) {
if (hasMessage) {
- encodeFacesMessagesButton(writer, messages);
+ encodeFacesMessagesButton(facesContext, component, writer, messages);
}
if (hasHelp) {
- encodeHelpButton(facesContext, writer, help);
+ encodeHelpButton(facesContext, component, writer, help);
}
writer.endElement(HtmlElements.DIV);
}
@@ -113,22 +113,12 @@
protected abstract void encodeEndField(FacesContext facesContext, UIComponent component) throws IOException;
- private void encodeFacesMessagesButton(
+ private void encodeFacesMessagesButton(FacesContext facesContext, final UIComponent component,
final TobagoResponseWriter writer, final List<FacesMessage> messages) throws IOException {
- writer.startElement(HtmlElements.A);
- writer.writeAttribute(HtmlAttributes.TABINDEX, "0", false);
- writer.writeAttribute(HtmlAttributes.ROLE, HtmlButtonTypes.BUTTON);
- writer.writeClassAttribute(
- TobagoClass.MESSAGES__BUTTON,
- BootstrapClass.BTN,
- BootstrapClass.buttonColor(ComponentUtils.getMaximumSeverity(messages)));
- writer.writeAttribute(DataAttributes.TOGGLE, "popover", false);
- writer.writeAttribute(DataAttributes.TITLE, getTitle(messages), true);
- writer.writeAttribute(DataAttributes.CONTENT, getMessage(messages), true);
- writer.startElement(HtmlElements.I);
- writer.writeClassAttribute(Icons.FA, Icons.EXCLAMATION);
- writer.endElement(HtmlElements.I);
- writer.endElement(HtmlElements.A);
+
+ encodePopover(writer, component.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "messages",
+ BootstrapClass.buttonColor(ComponentUtils.getMaximumSeverity(messages)),
+ Icons.EXCLAMATION, getTitle(messages), getMessage(messages));
}
private String getTitle(final List<FacesMessage> messages) {
@@ -217,21 +207,45 @@
return stringBuilder.toString();
}
- private void encodeHelpButton(final FacesContext facesContext, final TobagoResponseWriter writer, final String help)
+ private void encodeHelpButton(final FacesContext facesContext, final UIComponent component,
+ final TobagoResponseWriter writer, final String help) throws IOException {
+
+ encodePopover(writer, component.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "help",
+ BootstrapClass.BTN_OUTLINE_INFO, Icons.QUESTION,
+ ResourceUtils.getString(facesContext, "help.title"), help);
+ }
+
+ private void encodePopover(final TobagoResponseWriter writer, final String popoverId,
+ final CssItem buttonColor, final Icons buttonIcon, final String title, final String content)
throws IOException {
+ writer.startElement(HtmlElements.TOBAGO_POPOVER);
+ writer.writeIdAttribute(popoverId);
+
writer.startElement(HtmlElements.A);
writer.writeAttribute(HtmlAttributes.TABINDEX, "0", false);
writer.writeAttribute(HtmlAttributes.ROLE, HtmlButtonTypes.BUTTON);
- writer.writeClassAttribute(
- TobagoClass.HELP__BUTTON,
- BootstrapClass.BTN,
- BootstrapClass.BTN_OUTLINE_INFO);
- writer.writeAttribute(DataAttributes.TOGGLE, "popover", false);
- writer.writeAttribute(DataAttributes.TITLE, ResourceUtils.getString(facesContext, "help.title"), true);
- writer.writeAttribute(DataAttributes.CONTENT, help, true);
+ writer.writeClassAttribute(TobagoClass.POPOVER__BUTTON, BootstrapClass.BTN, buttonColor);
writer.startElement(HtmlElements.I);
- writer.writeClassAttribute(Icons.FA, Icons.QUESTION);
+ writer.writeClassAttribute(Icons.FA, buttonIcon);
writer.endElement(HtmlElements.I);
writer.endElement(HtmlElements.A);
+
+ writer.startElement(HtmlElements.DIV);
+ writer.writeClassAttribute(TobagoClass.POPOVER__BOX, BootstrapClass.POPOVER);
+ writer.writeNameAttribute(popoverId);
+ writer.startElement(HtmlElements.DIV);
+ writer.writeClassAttribute(BootstrapClass.ARROW);
+ writer.endElement(HtmlElements.DIV);
+ writer.startElement(HtmlElements.H3);
+ writer.writeClassAttribute(BootstrapClass.POPOVER_HEADER);
+ writer.writeText(title);
+ writer.endElement(HtmlElements.H3);
+ writer.startElement(HtmlElements.DIV);
+ writer.writeClassAttribute(BootstrapClass.POPOVER_BODY);
+ writer.writeText(content);
+ writer.endElement(HtmlElements.DIV);
+ writer.endElement(HtmlElements.DIV);
+
+ writer.endElement(HtmlElements.TOBAGO_POPOVER);
}
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
index f0f15b4..42df2e6 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/css/BootstrapClass.java
@@ -57,6 +57,7 @@
ALIGN_ITEMS_END("align-items-end"),
ALIGN_ITEMS_START("align-items-start"),
ALIGN_ITEMS_STRETCH("align-items-stretch"),
+ ARROW("arrow"),
BG_DARK("bg-dark"),
/**
* @deprecated since 4.0.0, please use {@link #BG_DARK}
@@ -471,6 +472,9 @@
PAGE_ITEM("page-item"),
PAGE_LINK("page-link"),
PAGINATION("pagination"),
+ POPOVER("popover"),
+ POPOVER_BODY("popover-body"),
+ POPOVER_HEADER("popover-header"),
PROGRESS("progress"),
PROGRESS_BAR("progress-bar"),
ROW("row"),
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 33abe3a..bc2ab9a 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
@@ -130,6 +130,10 @@
FORM("tobago-form"),
GRID_LAYOUT("tobago-gridLayout"),
HEADER("tobago-header"),
+ /**
+ * @deprecated Since 5.0.0. Please use {@link TobagoClass#POPOVER__BUTTON}
+ */
+ @Deprecated
HELP__BUTTON("tobago-help-button"),
IMAGE("tobago-image"),
IN("tobago-in"),
@@ -144,6 +148,10 @@
LINK("tobago-link"),
LINKS("tobago-links"),
MESSAGES("tobago-messages"),
+ /**
+ * @deprecated Since 5.0.0. Please use {@link TobagoClass#POPOVER__BUTTON}
+ */
+ @Deprecated
MESSAGES__BUTTON("tobago-messages-button"),
MESSAGES__CONTAINER("tobago-messages-container"),
OBJECT("tobago-object"),
@@ -152,6 +160,8 @@
PAGE__MENU_STORE("tobago-page-menuStore"),
PAGE__NOSCRIPT("tobago-page-noscript"),
PANEL("tobago-panel"),
+ POPOVER__BOX("tobago-popover-box"),
+ POPOVER__BUTTON("tobago-popover-button"),
PROGRESS("tobago-progress"),
SECTION("tobago-section"),
SECTION__HEADER("tobago-section-header"),
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 3993af1..e6d396e 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
@@ -141,6 +141,7 @@
TOBAGO_IN("tobago-in"),
TOBAGO_PAGE("tobago-page"),
TOBAGO_PANEL("tobago-panel"),
+ TOBAGO_POPOVER("tobago-popover"),
TOBAGO_POPUP("tobago-popup"),
TOBAGO_SPLIT_LAYOUT("tobago-split-layout"),
TOBAGO_SELECT_BOOLEAN_CHECKBOX("tobago-select-boolean-checkbox"),
diff --git a/tobago-core/src/main/resources/scss/_tobago.scss b/tobago-core/src/main/resources/scss/_tobago.scss
index a56b6a5..323c02d 100644
--- a/tobago-core/src/main/resources/scss/_tobago.scss
+++ b/tobago-core/src/main/resources/scss/_tobago.scss
@@ -500,7 +500,16 @@
}
}
-a.tobago-messages-button, a.tobago-help-button {
+.tobago-popover-box {
+ display: none;
+ width: max-content;
+
+ &.show {
+ display: block;
+ }
+}
+
+a.tobago-messages-button, a.tobago-help-button, a.tobago-popover-button {
padding-left: 0.4em;
padding-right: 0.4em;
}
diff --git a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-popover.ts b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-popover.ts
index 6a5c638..92202b9 100644
--- a/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-popover.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/npm/ts/tobago-popover.ts
@@ -15,23 +15,80 @@
* limitations under the License.
*/
-import {Listener, Phase} from "./tobago-listener";
+import Popper from "popper.js";
-class MessagePopover {
+class Popover extends HTMLElement {
- static init = function (element: HTMLElement): void {
- jQuery("[data-toggle=\"popover\"]").popover({
- constraints: [
- {
- to: "window",
- attachment: "together",
- pin: true
+ private popper;
+
+ constructor() {
+ super();
+ }
+
+ connectedCallback(): void {
+ this.button.addEventListener("click", this.showPopover.bind(this));
+ this.button.addEventListener("blur", this.hidePopover.bind(this));
+ }
+
+ showPopover(): void {
+ this.menuStore.appendChild(this.popover);
+ this.popper = new Popper(this.button, this.popover, {
+ placement: "right",
+ modifiers: {
+ arrow: {
+ element: ".arrow"
}
- ],
- trigger: "focus"
+ },
+ onCreate: this.updateBootstrapPopoverCss.bind(this),
+ onUpdate: this.updateBootstrapPopoverCss.bind(this)
});
- };
+ this.popover.classList.add("show");
+ }
+
+ hidePopover(): void {
+ this.popover.classList.remove("show");
+ this.appendChild(this.popover);
+
+ if (this.popper !== undefined && this.popper !== null) {
+ this.popper.destroy();
+ this.popper = null;
+ }
+ }
+
+ private updateBootstrapPopoverCss(): void {
+ const placement = this.popover.getAttribute("x-placement");
+ if (placement === "right" && !this.popover.classList.contains("bs-popover-right")) {
+ this.popover.classList.add("bs-popover-right");
+ this.popover.classList.remove("bs-popover-left");
+ this.updateAfterCssClassChange();
+ } else if (placement === "left" && !this.popover.classList.contains("bs-popover-left")) {
+ this.popover.classList.add("bs-popover-left");
+ this.popover.classList.remove("bs-popover-right");
+ this.updateAfterCssClassChange();
+ }
+ }
+
+ private updateAfterCssClassChange(): void {
+ if (this.popper !== undefined && this.popper !== null) {
+ this.popper.scheduleUpdate();
+ }
+ }
+
+ get button(): HTMLLinkElement {
+ return this.querySelector(":scope > .tobago-popover-button");
+ }
+
+ get popover(): HTMLDivElement {
+ const root = this.getRootNode() as ShadowRoot | Document;
+ return root.querySelector(".tobago-popover-box[name='" + this.id + "']");
+ }
+
+ private get menuStore(): HTMLDivElement {
+ const root = this.getRootNode() as ShadowRoot | Document;
+ return root.querySelector(".tobago-page-menuStore");
+ }
}
-Listener.register(MessagePopover.init, Phase.DOCUMENT_READY);
-Listener.register(MessagePopover.init, Phase.AFTER_UPDATE);
+document.addEventListener("DOMContentLoaded", function (event: Event): void {
+ window.customElements.define("tobago-popover", Popover);
+});