diff --git a/chip-list.js b/chip-list.js index fcd9153..9ea5409 100644 --- a/chip-list.js +++ b/chip-list.js @@ -52,11 +52,10 @@ class ChipList extends IgniteElement { input: null, search: true, searching: false, - showSearchResults: true, searchLoading: false, searchResults: null, searchResultConverter: (result) => new text(result), - searchDelay: 200, + searchDelay: 300, searchCallback: null, searchFunction: null, documentListener: null, @@ -101,46 +100,6 @@ class ChipList extends IgniteElement { .class("input") .attribute("contenteditable", "true") .ref(this.input) - .onEnter((e) => { - e.preventDefault(); - - //If we are read only don't do anything. - if (this.readOnly) { - return; - } - - //If this chip allows free form input then add a new item. - if (this.freeForm && this.input.textContent.trim().length >= 1) { - if (this.items == null) { - this.items = []; - } - - //Add a new item to the chip list. - this.items.push({ content: this.input.textContent.trim() }); - this.input.innerHTML = ""; - this.input.focus(); //Refocus to ensure we still have it. - this.searching = false; //Reset searching since we just added a item. - this.changed = true; //Make sure changed flag was set. - } - }) - .onBackspace((e) => { - //If we are read only don't do anything. - if (this.readOnly) { - return; - } - - //If the backspace key is pressed and there is no content, try to remove the last item from the list. - if (this.input.textContent.length == 0 || (this.input.textContent.length == 1 && this.input.textContent[0] == " ")) { - e.preventDefault(); - - if (this.items) { - this.items.pop(); - this.changed = true; //Make sure changed flag was set. - } - - this.searching = false; - } - }) .on("keydown", (e) => { //If we are read only don't do anything. if (this.readOnly) { @@ -149,16 +108,39 @@ class ChipList extends IgniteElement { //If the escape key is pressed stop searching until something else happens. if (e.key == "Escape") { - this.searching = false; + this.stopSearching(); e.preventDefault(); e.stopPropagation(); return; } + //If the enter key is pressed add a new item if free form allows. + else if (e.key == "Enter") { + e.preventDefault(); + e.stopPropagation(); + if (this.freeForm && this.input.textContent.trim().length >= 1) { + this.stopSearching(); + + //Add a new item to the chip list. + if (this.items) { + this.items.push(this.input.textContent.trim()); + } else { + this.items = [this.input.textContent.trim()]; + } + + this.input.innerHTML = ""; + this.input.focus(); //Refocus to ensure we still have it. + this.changed = true; //Make sure changed flag was set. + + return; + } else if (!this.searching) { + this.searching = true; + this.searchLoading = false; + } + } //Reset the searching and input if we get a tab, since the browser //will focus the next avaiable element. - if (e.key == "Tab") { - this.searching = false; + else if (e.key == "Tab") { this.editing = false; //Fire a change event if there was a change. @@ -167,32 +149,44 @@ class ChipList extends IgniteElement { this.dispatchEvent(new Event("change")); } + this.stopSearching(); + this.input.innerHTML = ""; return; } - //If we are not searching and a key was pressed, open the search box. - if (!this.searching && this.search && (e.key !== "Backspace" || (e.key == "Backspace" && e.target.textContent.length > 1)) && (this.items == null || this.items.length < this.itemsMax)) { + else if (!this.searching && this.search && (e.key !== "Backspace" || (e.key == "Backspace" && e.target.textContent.length > 1)) && (this.items == null || this.items.length < this.itemsMax)) { this.searching = true; - this.showSearchResults = true; - } else if (this.items != null && this.items.length >= this.itemsMax && e.key !== "Backspace") { - //Don't allow input if we reached the max number of items. + this.searchLoading = false; + } + //Don't allow input if we reached the max number of items. + else if (this.items != null && this.items.length >= this.itemsMax && e.key !== "Backspace") { e.preventDefault(); } + //Remove the last item and stop searching if the backspace was pressed and there is no search text + else if (e.key == "Backspace" && (this.input.textContent.length == 0 || (this.input.textContent.length == 1 && this.input.textContent[0] == " "))) { + e.preventDefault(); - //If we are searching and we have a on search function invoke it. + if (this.items) { + this.items.pop(); + this.changed = true; //Make sure changed flag was set. + } + + this.stopSearching(); + } + + //If we are searching and we have a search function invoke it. if (this.searching && this.searchFunction) { - //Clear a pending search + //Clear a pending search if there is one if (this.searchCallback) { clearTimeout(this.searchCallback); + this.searchCallback = null; } //Schedule a new search this.searchCallback = setTimeout(async () => { this.searchLoading = true; - this.searchResults = await this.searchFunction(this.input.textContent.trim()); - this.searchLoading = false; }, this.searchDelay); } @@ -203,16 +197,18 @@ class ChipList extends IgniteElement { //Clear button new button() .class("btn btn-link text-muted fs-4") + .attribute("tabindex", "-1") .title("Clear") .show([this.showClearButton, this.items], (show, items) => show && items && items.length > 0) .child(new i().class('fa fa-times')) .onClick(() => { this.items = null; this.editing = false; - this.searching = false; - this.changed = true; + this.stopSearching(); this.input.blur(); this.input.innerHTML = ""; + this.changed = false; + this.dispatchEvent(new Event("change")); }), //Search results popper @@ -232,7 +228,7 @@ class ChipList extends IgniteElement { new span().class("text-muted").hide(this.searchLoading).innerText(this.searchResults, results => results && results.length > 0 ? `Showing ${results.length} ${results.length == 1 ? "result" : "results"}` : "No results"), //Results - new div().class("d-flex flex-column gap-2").hide([this.showSearchResults, this.searchLoading], (showSearchResults, searchLoading) => !showSearchResults || searchLoading).child( + new div().class("d-flex flex-column gap-2").hide(this.searchLoading).child( new list( this.searchResults, result => new div() @@ -299,6 +295,17 @@ class ChipList extends IgniteElement { this.input.innerHTML = ""; this.input.focus(); } + + stopSearching() { + this.searching = false; + + this.searchLoading = false; + + if (this.searchCallback) { + clearTimeout(this.searchCallback); + this.searchCallback = null; + } + } } class ChipListTemplate extends IgniteTemplate {