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 2023/02/14 16:05:17 UTC
[myfaces-tobago] 01/02: refactor(cleanup): select[One/Many]List
This is an automated email from the ASF dual-hosted git repository.
hnoeth pushed a commit to branch tobago-5.x
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
commit 452e16cfbb2c8f3595613da498db201b710edb33
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Fri Feb 10 22:27:31 2023 +0100
refactor(cleanup): select[One/Many]List
* naming, ordering, reduce code
---
.../src/main/ts/tobago-select-list-base.ts | 214 ++++++++++++++-------
.../src/main/ts/tobago-select-many-list.ts | 159 +++------------
.../src/main/ts/tobago-select-one-list.ts | 123 +++---------
3 files changed, 207 insertions(+), 289 deletions(-)
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts
index 21ca1bdfc8..28abdb16c8 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-list-base.ts
@@ -17,14 +17,32 @@
import {createPopper, Instance} from "@popperjs/core";
import {Css} from "./tobago-css";
+import {TobagoFilterRegistry} from "./tobago-filter-registry";
+import {Key} from "./tobago-key";
-export class SelectListBase extends HTMLElement {
+export abstract class SelectListBase extends HTMLElement {
private popper: Instance;
get disabled(): boolean {
return this.classList.contains(Css.TOBAGO_DISABLED);
}
+ get focused(): boolean {
+ return this.classList.contains(Css.TOBAGO_FOCUS);
+ }
+
+ set focused(focused: boolean) {
+ if (focused) {
+ this.classList.add(Css.TOBAGO_FOCUS);
+ } else {
+ this.classList.remove(Css.TOBAGO_FOCUS);
+ }
+ }
+
+ get filter(): string {
+ return this.getAttribute("filter");
+ }
+
get hiddenSelect(): HTMLSelectElement {
return this.querySelector("select");
}
@@ -37,14 +55,6 @@ export class SelectListBase extends HTMLElement {
return this.querySelector(".tobago-select-field");
}
- get filter(): string {
- return this.getAttribute("filter");
- }
-
- get filterEnabled(): boolean {
- return this.filter?.length > 0;
- }
-
get filterInput(): HTMLInputElement {
return this.querySelector(".tobago-filter");
}
@@ -54,11 +64,6 @@ export class SelectListBase extends HTMLElement {
return root.querySelector(`.dropdown-menu[name='${this.id}']`);
}
- get menuStore(): HTMLDivElement {
- const root = this.getRootNode() as ShadowRoot | Document;
- return root.querySelector(".tobago-page-menuStore");
- }
-
get tbody(): HTMLElement {
const root = this.getRootNode() as ShadowRoot | Document;
return root.querySelector(`.tobago-options[name='${this.id}'] tbody`);
@@ -76,30 +81,111 @@ export class SelectListBase extends HTMLElement {
return this.tbody.querySelector<HTMLTableRowElement>("." + Css.TOBAGO_PRESELECT);
}
+ get menuStore(): HTMLDivElement {
+ const root = this.getRootNode() as ShadowRoot | Document;
+ return root.querySelector(".tobago-page-menuStore");
+ }
+
connectedCallback(): void {
if (this.dropdownMenu) {
this.popper = createPopper(this.selectField, this.dropdownMenu, {});
- window.addEventListener("resize", this.resizeEvent.bind(this));
+ window.addEventListener("resize", () => this.updateDropdownMenuWidth());
+ }
+ document.addEventListener("click", this.globalClickEvent.bind(this));
+ this.selectField.addEventListener("keydown", this.keydownEventBase.bind(this));
+ this.filterInput.addEventListener("focus", this.focusEvent.bind(this));
+ this.filterInput.addEventListener("blur", this.blurEvent.bind(this));
+ if (this.filter) {
+ this.filterInput.addEventListener("input", this.filterEvent.bind(this));
+ }
+ this.tbody.addEventListener("click", this.tbodyClickEvent.bind(this));
+
+ // handle autofocus; trigger focus event
+ if (document.activeElement.id === this.filterInput.id) {
+ this.focusEvent();
+ }
+ }
+
+ protected abstract globalClickEvent(event: MouseEvent): void;
+
+ private keydownEventBase(event: KeyboardEvent) {
+ switch (event.key) {
+ case Key.ESCAPE:
+ this.hideDropdown();
+ this.removePreselection();
+ break;
+ case Key.ARROW_DOWN:
+ event.preventDefault();
+ this.showDropdown();
+ this.preselectNextRow();
+ break;
+ case Key.ARROW_UP:
+ event.preventDefault();
+ this.showDropdown();
+ this.preselectPreviousRow();
+ break;
+ case Key.ENTER:
+ case Key.SPACE:
+ if (this.preselectedRow) {
+ event.preventDefault();
+ const row = this.tbody.querySelector<HTMLTableRowElement>("." + Css.TOBAGO_PRESELECT);
+ this.select(row);
+ } else if (document.activeElement.id === this.filterInput.id) {
+ this.showDropdown();
+ }
+ break;
+ case Key.TAB:
+ this.removePreselection();
+ break;
}
}
protected focusEvent(): void {
- if (!this.hiddenSelect.disabled) {
- if (!this.classList.contains(Css.TOBAGO_FOCUS)) {
- this.setFocus(true);
+ if (!this.disabled) {
+ this.focused = true;
+ }
+ }
+
+ protected blurEvent(event: FocusEvent): void {
+ if (event.relatedTarget !== null) {
+ //relatedTarget is the new focused element; null indicate a mouseclick or an inactive browser window
+ if (!this.isPartOfSelectField(event.relatedTarget as Element)
+ && !this.isPartOfTobagoOptions(event.relatedTarget as Element)) {
+ this.leaveComponent();
}
}
}
- protected setFocus(focus: boolean): void {
- if (focus) {
- this.classList.add(Css.TOBAGO_FOCUS);
- } else {
- this.classList.remove(Css.TOBAGO_FOCUS);
+ private filterEvent(event: Event): void {
+ const input = event.currentTarget as HTMLInputElement;
+ const searchString = input.value;
+ this.showDropdown();
+ const filterFunction = TobagoFilterRegistry.get(this.filter);
+ // XXX todo: if filterFunction not found?
+ if (filterFunction != null) {
+ this.querySelectorAll("tr").forEach(row => {
+ const itemValue = row.dataset.tobagoValue;
+ if (filterFunction(itemValue, searchString)) {
+ row.classList.remove(Css.D_NONE);
+ } else {
+ row.classList.add(Css.D_NONE);
+ row.classList.remove(Css.TOBAGO_PRESELECT);
+ }
+ });
}
}
- protected preselectNextTableRow(): void {
+ private tbodyClickEvent(event: MouseEvent): void {
+ const target = <HTMLElement>event.target;
+ const row = target.closest("tr");
+ this.select(row);
+ }
+
+ protected abstract leaveComponent(): void;
+
+ protected abstract select(row: HTMLTableRowElement): void;
+
+ protected preselectNextRow(): void {
const rows = this.enabledRows;
const index = this.preselectIndex(rows);
if (index >= 0) {
@@ -115,7 +201,7 @@ export class SelectListBase extends HTMLElement {
}
}
- protected preselectPreviousTableRow(): void {
+ protected preselectPreviousRow(): void {
const rows = this.enabledRows;
const index = this.preselectIndex(rows);
if (index >= 0) {
@@ -131,33 +217,27 @@ export class SelectListBase extends HTMLElement {
}
}
- protected removePreselection(): void {
- this.preselectedRow?.classList.remove(Css.TOBAGO_PRESELECT);
- }
-
- protected isPartOfSelectField(element: Element): boolean {
- if (element) {
- if (this.selectField.id === element.id) {
- return true;
- } else {
- return element.parentElement ? this.isPartOfSelectField(element.parentElement) : false;
+ private preselectIndex(rows: NodeListOf<HTMLTableRowElement>): number {
+ for (let i = 0; i < rows.length; i++) {
+ if (rows.item(i).classList.contains(Css.TOBAGO_PRESELECT)) {
+ return i;
}
- } else {
- return false;
}
+ return -1;
}
- protected isPartOfTobagoOptions(element: Element): boolean {
- if (element) {
- if (element.classList.contains(Css.TOBAGO_OPTIONS)
- && this.id === element.getAttribute("name")) {
- return true;
- } else {
- return element.parentElement ? this.isPartOfTobagoOptions(element.parentElement) : false;
- }
- } else {
- return false;
+ private preselect(row: HTMLTableRowElement): void {
+ row.classList.add(Css.TOBAGO_PRESELECT);
+ if (!this.dropdownMenu) {
+ row.scrollIntoView({block: "center"});
}
+
+ this.filterInput.disabled = false;
+ this.filterInput.focus({preventScroll: true});
+ }
+
+ protected removePreselection(): void {
+ this.preselectedRow?.classList.remove(Css.TOBAGO_PRESELECT);
}
protected showDropdown(): void {
@@ -178,32 +258,34 @@ export class SelectListBase extends HTMLElement {
}
}
- private preselect(row: HTMLTableRowElement): void {
- row.classList.add(Css.TOBAGO_PRESELECT);
- if (!this.dropdownMenu) {
- row.scrollIntoView({block: "center"});
+ private updateDropdownMenuWidth(): void {
+ if (this.dropdownMenu) {
+ this.dropdownMenu.style.width = `${this.selectField.offsetWidth}px`;
}
-
- this.filterInput.disabled = false;
- this.filterInput.focus({preventScroll: true});
}
- private preselectIndex(rows: NodeListOf<HTMLTableRowElement>): number {
- for (let i = 0; i < rows.length; i++) {
- if (rows.item(i).classList.contains(Css.TOBAGO_PRESELECT)) {
- return i;
+ protected isPartOfSelectField(element: Element): boolean {
+ if (element) {
+ if (this.selectField.id === element.id) {
+ return true;
+ } else {
+ return element.parentElement ? this.isPartOfSelectField(element.parentElement) : false;
}
+ } else {
+ return false;
}
- return -1;
}
- private resizeEvent(event: UIEvent): void {
- this.updateDropdownMenuWidth();
- }
-
- private updateDropdownMenuWidth(): void {
- if (this.dropdownMenu) {
- this.dropdownMenu.style.width = `${this.selectField.offsetWidth}px`;
+ protected isPartOfTobagoOptions(element: Element): boolean {
+ if (element) {
+ if (element.classList.contains(Css.TOBAGO_OPTIONS)
+ && this.id === element.getAttribute("name")) {
+ return true;
+ } else {
+ return element.parentElement ? this.isPartOfTobagoOptions(element.parentElement) : false;
+ }
+ } else {
+ return false;
}
}
}
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
index a19e50dad9..8d72cce5ca 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-many-list.ts
@@ -15,9 +15,7 @@
* limitations under the License.
*/
-import {TobagoFilterRegistry} from "./tobago-filter-registry";
import {SelectListBase} from "./tobago-select-list-base";
-import {Key} from "./tobago-key";
import {Css} from "./tobago-css";
class SelectManyList extends SelectListBase {
@@ -31,51 +29,40 @@ class SelectManyList extends SelectListBase {
connectedCallback(): void {
super.connectedCallback();
- document.addEventListener("click", this.clickEvent.bind(this));
- this.filterInput.addEventListener("focus", this.focusEvent.bind(this));
- this.filterInput.addEventListener("blur", this.blurEvent.bind(this));
- this.selectField.addEventListener("keydown", this.keydownEvent.bind(this));
// init badges
this.querySelectorAll("option:checked").forEach(
option => this.sync(<HTMLOptionElement>option)
);
+ }
- this.initList();
+ protected globalClickEvent(event: MouseEvent): void {
+ if (!this.disabled) {
+ if (this.isDeleted(event.target as Element)) {
+ // do nothing, this is probably a removed badge
+ } else if (this.isPartOfSelectField(event.target as Element)
+ || this.isPartOfTobagoOptions(event.target as Element)) {
- if (this.filter) {
- this.filterInput.addEventListener("input", this.filterEvent.bind(this));
- }
+ if (!this.filterInput.disabled) {
+ this.filterInput.focus();
+ } else if (this.badgeCloseButtons.length > 0) {
+ this.badgeCloseButtons[0].focus();
+ }
+ this.showDropdown();
- // handle autofocus; trigger focus event
- if (document.activeElement.id === this.filterInput.id) {
- this.focusEvent();
+ } else {
+ this.leaveComponent();
+ }
}
}
- select(event: MouseEvent): void {
- const target = <HTMLElement>event.target;
- const row = target.closest("tr");
- this.selectRow(row);
- }
-
- selectRow(row: HTMLTableRowElement): void {
+ protected select(row: HTMLTableRowElement): void {
const itemValue = row.dataset.tobagoValue;
- console.info("itemValue", itemValue);
- const select = this.hiddenSelect;
- const option: HTMLOptionElement = select.querySelector(`[value="${itemValue}"]`);
+ const option: HTMLOptionElement = this.hiddenSelect.querySelector(`[value="${itemValue}"]`);
option.selected = !option.selected;
this.sync(option);
- }
-
- removeBadge(event: MouseEvent): void {
- const target = <HTMLElement>event.target;
- const group: HTMLElement = target.closest(".btn-group");
- const itemValue = group.dataset.tobagoValue;
- const select = this.hiddenSelect;
- const option: HTMLOptionElement = select.querySelector(`[value="${itemValue}"]`);
- option.selected = false;
- this.sync(option);
+ this.filterInput.disabled = false;
+ this.filterInput.focus({preventScroll: true});
}
sync(option: HTMLOptionElement) {
@@ -94,8 +81,7 @@ class SelectManyList extends SelectListBase {
closeButton?.addEventListener("focus", this.focusEvent.bind(this));
closeButton?.addEventListener("blur", this.blurEvent.bind(this));
- // highlight list row
- row.classList.add(Css.TABLE_PRIMARY);
+ row.classList.add(Css.TABLE_PRIMARY); // highlight list row
} else {
// remove badge
const badge = this.selectField.querySelector(`[data-tobago-value="${itemValue}"]`);
@@ -111,8 +97,7 @@ class SelectManyList extends SelectListBase {
this.filterInput.focus();
}
- // remove highlight list row
- row.classList.remove(Css.TABLE_PRIMARY);
+ row.classList.remove(Css.TABLE_PRIMARY); // remove highlight list row
}
if (!this.disabled && !this.filter) {
@@ -138,81 +123,17 @@ class SelectManyList extends SelectListBase {
</span>`;
}
- filterEvent(event: Event): void {
- const input = event.currentTarget as HTMLInputElement;
- const searchString = input.value;
- console.info("searchString", searchString);
- const filterFunction = TobagoFilterRegistry.get(this.filter);
- // XXX todo: if filterFunction not found?
- if (filterFunction != null) {
- this.querySelectorAll("tr").forEach(row => {
- const itemValue = row.dataset.tobagoValue;
- if (filterFunction(itemValue, searchString)) {
- row.classList.remove(Css.D_NONE);
- } else {
- row.classList.add(Css.D_NONE);
- row.classList.remove(Css.TOBAGO_PRESELECT);
- }
- });
- }
- }
-
- private clickEvent(event: MouseEvent): void {
- if (!this.disabled) {
- if (this.isDeleted(event.target as Element)) {
- // do nothing, this is probably a removed badge
- } else if (this.isPartOfSelectField(event.target as Element)
- || this.isPartOfTobagoOptions(event.target as Element)) {
-
- if (!this.filterInput.disabled) {
- this.filterInput.focus();
- } else if (this.badgeCloseButtons.length > 0) {
- this.badgeCloseButtons[0].focus();
- }
- this.showDropdown();
-
- } else {
- this.leaveComponent();
- }
- }
- }
-
- private keydownEvent(event: KeyboardEvent) {
- switch (event.key) {
- case Key.ESCAPE:
- this.hideDropdown();
- this.removePreselection();
- break;
- case Key.ARROW_DOWN:
- event.preventDefault();
- this.showDropdown();
- this.preselectNextTableRow();
- break;
- case Key.ARROW_UP:
- event.preventDefault();
- this.showDropdown();
- this.preselectPreviousTableRow();
- break;
- case Key.ENTER:
- case Key.SPACE:
- if (this.preselectedRow) {
- event.preventDefault();
- const row = this.tbody.querySelector<HTMLTableRowElement>("." + Css.TOBAGO_PRESELECT);
- this.selectRow(row);
- this.filterInput.disabled = false;
- this.filterInput.focus({preventScroll: true});
- } else if (document.activeElement.id === this.filterInput.id) {
- this.showDropdown();
- }
- break;
- case Key.TAB:
- this.removePreselection();
- break;
- }
+ removeBadge(event: MouseEvent): void {
+ const target = <HTMLElement>event.target;
+ const group: HTMLElement = target.closest(".btn-group");
+ const itemValue = group.dataset.tobagoValue;
+ const option: HTMLOptionElement = this.hiddenSelect.querySelector(`[value="${itemValue}"]`);
+ option.selected = false;
+ this.sync(option);
}
- private leaveComponent(): void {
- this.setFocus(false);
+ protected leaveComponent(): void {
+ this.focused = false;
this.filterInput.value = null;
this.filterInput.dispatchEvent(new Event("input"));
this.hideDropdown();
@@ -221,24 +142,6 @@ class SelectManyList extends SelectListBase {
private isDeleted(element: Element): boolean {
return element.closest("html") === null;
}
-
- private blurEvent(event: FocusEvent): void {
- if (event.relatedTarget !== null) {
- //relatedTarget is the new focused element; null indicate a mouseclick or an inactive browser window
- if (!this.isPartOfSelectField(event.relatedTarget as Element)
- && !this.isPartOfTobagoOptions(event.relatedTarget as Element)) {
- this.leaveComponent();
- }
- }
- }
-
- private initList() {
- const tbody = this.tbody;
- tbody.addEventListener("click", this.select.bind(this));
- tbody.querySelectorAll("tr").forEach((row: HTMLTableRowElement) => {
- // row stuff
- });
- }
}
document.addEventListener("tobago.init", function (event: Event): void {
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts
index 2039b338e0..4002e59f38 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-select-one-list.ts
@@ -15,7 +15,6 @@
* limitations under the License.
*/
-import {TobagoFilterRegistry} from "./tobago-filter-registry";
import {SelectListBase} from "./tobago-select-list-base";
import {Key} from "./tobago-key";
import {Css} from "./tobago-css";
@@ -35,72 +34,15 @@ class SelectOneList extends SelectListBase {
connectedCallback(): void {
super.connectedCallback();
- document.addEventListener("click", this.clickEvent.bind(this));
- this.filterInput.addEventListener("focus", this.focusEvent.bind(this));
- this.filterInput.addEventListener("blur", this.blurEvent.bind(this));
this.selectField.addEventListener("keydown", this.keydownEvent.bind(this));
- this.tbody.addEventListener("click", this.select.bind(this));
-
- this.sync();
-
if (this.filter) {
- this.filterInput.addEventListener("input", this.filterEvent.bind(this));
- }
-
- if (document.activeElement.id === this.filterInput.id) {
- this.focusEvent();
+ this.filterInput.addEventListener("input", this.clearSpan.bind(this));
}
- }
-
- select(event: MouseEvent): void {
- const target = <HTMLElement>event.target;
- const row = target.closest("tr");
- this.selectRow(row);
- }
- selectRow(row: HTMLTableRowElement): void {
- const itemValue = row.dataset.tobagoValue;
- const select = this.hiddenSelect;
- const option: HTMLOptionElement = select.querySelector(`[value="${itemValue}"]`);
- option.selected = true;
- this.filterInput.value = null;
this.sync();
}
- sync() {
- this.rows.forEach((row) => {
- if (row.dataset.tobagoValue === this.hiddenSelect.value) {
- this.spanText = this.hiddenSelect.value;
- row.classList.add(Css.TABLE_PRIMARY); // highlight list row
- } else {
- row.classList.remove(Css.TABLE_PRIMARY); // remove highlight list row
- }
- });
- }
-
- filterEvent(event: Event): void {
- const input = event.currentTarget as HTMLInputElement;
- const searchString = input.value;
- if (searchString !== null) {
- this.spanText = null;
- this.showDropdown();
- }
- const filterFunction = TobagoFilterRegistry.get(this.filter);
- // XXX todo: if filterFunction not found?
- if (filterFunction != null) {
- this.querySelectorAll("tr").forEach(row => {
- const itemValue = row.dataset.tobagoValue;
- if (filterFunction(itemValue, searchString)) {
- row.classList.remove(Css.D_NONE);
- } else {
- row.classList.add(Css.D_NONE);
- row.classList.remove(Css.TOBAGO_PRESELECT);
- }
- });
- }
- }
-
- private clickEvent(event: MouseEvent): void {
+ protected globalClickEvent(event: MouseEvent): void {
if (!this.disabled) {
if (this.isPartOfSelectField(event.target as Element) || this.isPartOfTobagoOptions(event.target as Element)) {
if (!this.filterInput.disabled) {
@@ -116,54 +58,45 @@ class SelectOneList extends SelectListBase {
private keydownEvent(event: KeyboardEvent) {
switch (event.key) {
case Key.ESCAPE:
- this.hideDropdown();
- this.removePreselection();
- break;
- case Key.ARROW_DOWN:
- event.preventDefault();
- this.showDropdown();
- this.preselectNextTableRow();
- break;
- case Key.ARROW_UP:
- event.preventDefault();
- this.showDropdown();
- this.preselectPreviousTableRow();
+ this.spanText = this.hiddenSelect.value;
break;
case Key.BACKSPACE:
if (this.filterInput.value.length === 0) {
- this.spanText = null;
this.filterInput.dispatchEvent(new Event("input"));
}
break;
- case Key.ENTER:
- case Key.SPACE:
- if (this.preselectedRow) {
- event.preventDefault();
- const row = this.tbody.querySelector<HTMLTableRowElement>("." + Css.TOBAGO_PRESELECT);
- this.selectRow(row);
- } else if (document.activeElement.id === this.filterInput.id) {
- this.showDropdown();
- }
- break;
}
}
- private leaveComponent(): void {
- this.setFocus(false);
+ private clearSpan(): void {
+ this.spanText = null;
+ }
+
+ protected select(row: HTMLTableRowElement): void {
+ const itemValue = row.dataset.tobagoValue;
+ const option: HTMLOptionElement = this.hiddenSelect.querySelector(`[value="${itemValue}"]`);
+ option.selected = true;
this.filterInput.value = null;
- this.filterInput.dispatchEvent(new Event("input"));
- this.spanText = this.hiddenSelect.value;
- this.hideDropdown();
+ this.sync();
}
- private blurEvent(event: FocusEvent): void {
- if (event.relatedTarget !== null) {
- //relatedTarget is the new focused element; null indicate a mouseclick or an inactive browser window
- if (!this.isPartOfSelectField(event.relatedTarget as Element)
- && !this.isPartOfTobagoOptions(event.relatedTarget as Element)) {
- this.leaveComponent();
+ sync() {
+ this.rows.forEach((row) => {
+ if (row.dataset.tobagoValue === this.hiddenSelect.value) {
+ this.spanText = this.hiddenSelect.value;
+ row.classList.add(Css.TABLE_PRIMARY); // highlight list row
+ } else {
+ row.classList.remove(Css.TABLE_PRIMARY); // remove highlight list row
}
- }
+ });
+ }
+
+ protected leaveComponent(): void {
+ this.focused = false;
+ this.filterInput.value = null;
+ this.filterInput.dispatchEvent(new Event("input"));
+ this.spanText = this.hiddenSelect.value;
+ this.hideDropdown();
}
}