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 2022/11/02 16:01:00 UTC
[myfaces-tobago] 02/02: feat(selectMany): focus
This is an automated email from the ASF dual-hosted git repository.
hnoeth pushed a commit to branch t5_selectMany
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
commit ad1a0aa7d30f5b1ab6df4c55f43e9032c5e91d85
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Wed Nov 2 16:37:29 2022 +0100
feat(selectMany): focus
Issue: TOBAGO-2159
---
.../myfaces/tobago/renderkit/css/TobagoClass.java | 1 +
tobago-theme/src/main/scss/_tobago.scss | 31 +++++++
.../src/main/ts/tobago-select-many.ts | 95 ++++++++++++++++++----
3 files changed, 113 insertions(+), 14 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 8d503c4eee..1d9d4b4fb3 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
@@ -54,6 +54,7 @@ public enum TobagoClass implements CssItem {
EXPANDED("tobago-expanded"),
// FILE("tobago-file"),
// FIGURE("tobago-figure"),
+ FOCUS("tobago-focus"),
FOLDER("tobago-folder"),
FILTER("tobago-filter"),
FILTER__WRAPPER("tobago-filter-wrapper"),
diff --git a/tobago-theme/src/main/scss/_tobago.scss b/tobago-theme/src/main/scss/_tobago.scss
index 882bc1337e..8d66f11cd9 100644
--- a/tobago-theme/src/main/scss/_tobago.scss
+++ b/tobago-theme/src/main/scss/_tobago.scss
@@ -118,6 +118,20 @@ $tobago-flex-layout-spacing: 0.5rem;
}
}
+@mixin formControlFocus() {
+ //_form-control:focus from bootstrap
+ color: $input-focus-color;
+ background-color: $input-focus-bg;
+ border-color: $input-focus-border-color;
+ outline: 0;
+ @if $enable-shadows {
+ @include box-shadow($input-box-shadow, $input-focus-box-shadow);
+ } @else {
+ // Avoid using mixin so we can pass custom focus shadow properly
+ box-shadow: $input-focus-box-shadow;
+ }
+}
+
@mixin formControlSelectListDisabled() {
&:disabled option, option:disabled {
color: rgba($input-color, $tobago-form-disabled-alpha);
@@ -1177,6 +1191,23 @@ tobago-select-one-radio {
tobago-select-many {
display: block;
+ &.list-group {
+ .tobago-options {
+ border-top: ($table-border-width * 2) solid $table-group-separator-color;
+ overflow: auto;
+ }
+ }
+
+ &.tobago-focus {
+ &.dropdown .tobago-filter-wrapper {
+ @include formControlFocus();
+ }
+
+ &.list-group {
+ @include formControlFocus();
+ }
+ }
+
.tobago-filter-wrapper {
display: flex;
flex-wrap: wrap;
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many.ts
index bbea491888..cd2fef5fd8 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many.ts
@@ -23,6 +23,7 @@ class SelectMany extends HTMLElement {
private readonly CssClass = {
DROPDOWN_MENU: "dropdown-menu",
SHOW: "show",
+ TOBAGO_FOCUS: "tobago-focus",
TOBAGO_OPTIONS: "tobago-options"
};
@@ -42,6 +43,10 @@ class SelectMany extends HTMLElement {
return this.querySelector(".tobago-filter-wrapper");
}
+ get badgeCloseButtons(): NodeListOf<HTMLButtonElement> {
+ return this.selectField.querySelectorAll("button.btn.badge");
+ }
+
get filter(): string {
return this.getAttribute("filter");
}
@@ -72,13 +77,21 @@ class SelectMany extends HTMLElement {
if (this.dropdownMenu) {
window.addEventListener("resize", this.resizeEvent.bind(this));
- document.addEventListener("click", this.clickEvent.bind(this));
document.addEventListener("keydown", this.keydownEvent.bind(this));
this.addEventListener(BootstrapEvents.DROPDOWN_SHOW, this.showDropdown.bind(this));
this.addEventListener(BootstrapEvents.DROPDOWN_SHOWN, this.shownDropdown.bind(this));
this.addEventListener(BootstrapEvents.DROPDOWN_HIDE, this.preventBootstrapHide.bind(this));
this.addEventListener(BootstrapEvents.DROPDOWN_HIDDEN, this.hiddenDropdown.bind(this));
}
+ document.addEventListener("click", this.clickEvent.bind(this));
+ this.filterInput.addEventListener("focus", this.focusEvent.bind(this));
+ this.filterInput.addEventListener("blur", this.blurEvent.bind(this));
+ this.badgeCloseButtons.forEach(
+ (closeButton) => {
+ closeButton.addEventListener("focus", this.focusEvent.bind(this));
+ closeButton.addEventListener("blur", this.blurEvent.bind(this));
+ }
+ );
// init badges
this.querySelectorAll("option:checked").forEach(
@@ -130,6 +143,8 @@ class SelectMany extends HTMLElement {
// todo: nicer adding the @click with lit-html
const current = this.filterInput.parentElement.querySelector(".btn-group[data-tobago-value='" + itemValue + "']");
current.addEventListener("click", this.removeBadge.bind(this));
+ this.selectField.querySelector("button.btn.badge").addEventListener("focus", this.focusEvent.bind(this));
+ this.selectField.querySelector("button.btn.badge").addEventListener("blur", this.blurEvent.bind(this));
// highlight list row
row.classList.add("table-active");
@@ -137,7 +152,14 @@ class SelectMany extends HTMLElement {
// remove badge
const selectField1 = this.selectField;
console.log("selectField1", selectField1);
- selectField1.querySelector(`[data-tobago-value="${itemValue}"]`).remove();
+ const badge = selectField1.querySelector(`[data-tobago-value="${itemValue}"]`);
+ const previousElementSibling = badge.previousElementSibling;
+ badge.remove();
+ if (previousElementSibling) {
+ previousElementSibling.querySelector<HTMLButtonElement>("button.btn.badge").focus();
+ } else {
+ this.filterInput.focus();
+ }
// remove highlight list row
row.classList.remove("table-active");
@@ -185,26 +207,45 @@ class SelectMany extends HTMLElement {
}
private clickEvent(event: MouseEvent): void {
- let hide = true;
+ if (this.isPartOfFilterWrapper(event.target as Element)) {
+ this.filterInput.focus();
+ } else if (this.isPartOfComponent(event.target as Element)) {
+ this.filterInput.focus();
+ } else {
+ this.hideDropdown();
+ this.setFocus(false);
+ }
+ }
- for (const element of event.composedPath() as HTMLElement[]) {
+ private keydownEvent(event: KeyboardEvent) {
+ if (event.key === this.Key.ESCAPE) {
+ this.hideDropdown();
+ }
+ }
+
+ private isPartOfComponent(element: Element): boolean {
+ if (element) {
if (this.id === element.id
|| (element.classList?.contains(this.CssClass.DROPDOWN_MENU)
&& this.id === element.getAttribute("name"))) {
- hide = false;
- break;
+ return true;
+ } else {
+ return element.parentElement ? this.isPartOfComponent(element.parentElement) : false;
}
- }
-
- if (hide) {
- this.hideDropdown();
+ } else {
+ return false;
}
}
- private keydownEvent(event: KeyboardEvent) {
- console.log("### keydownEvent");
- if (event.key === this.Key.ESCAPE) {
- this.hideDropdown();
+ private isPartOfFilterWrapper(element: Element): boolean {
+ if (element) {
+ if (this.selectField.id === element.id) {
+ return true;
+ } else {
+ return element.parentElement ? this.isPartOfFilterWrapper(element.parentElement) : false;
+ }
+ } else {
+ return false;
}
}
@@ -231,6 +272,32 @@ class SelectMany extends HTMLElement {
}
}
+ private focusEvent(event: FocusEvent): void {
+ this.setFocus(true);
+ }
+
+ private blurEvent(event: FocusEvent): void {
+ if (event.relatedTarget === null) {
+ //this must be a mouse click; if tabbed out the relatedTarget is the new focused element
+ } else {
+ if (this.isPartOfFilterWrapper(event.relatedTarget as Element)) {
+ //to nothing
+ } else if (this.isPartOfComponent(event.relatedTarget as Element)) {
+ this.filterInput.focus();
+ } else {
+ this.setFocus(false);
+ }
+ }
+ }
+
+ private setFocus(focus: boolean): void {
+ if (focus) {
+ this.classList.add(this.CssClass.TOBAGO_FOCUS);
+ } else {
+ this.classList.remove(this.CssClass.TOBAGO_FOCUS);
+ }
+ }
+
private focusFilter(event: MouseEvent): void {
// console.log("### focusFilter");
}