* slim-select-pagination-bug-fix : fixed several bugs related to slim select search box gui element 1. If user enters a search text in the filter text box, the user will not be able to scroll to the next page. That is the user will only be able to see the first page of returned item with a none empty search string. 2. User will not be able to select an item returned from search query if user clicks reload after a dynami search. When the user is able to load a second page, the user will be able to select an item from the third+ page if previous bug is fixed. * Recompile static assets --------- Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
This commit is contained in:
parent
53e1ab5fc5
commit
a544b55e9e
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -140,6 +140,10 @@ export class APISelect {
|
|||
*/
|
||||
private queryUrl: string = '';
|
||||
|
||||
/**
|
||||
* Interal state variable used to remember search key entered by user for "Filter" search box
|
||||
*/
|
||||
private searchKey: Nullable<string> = null;
|
||||
/**
|
||||
* Scroll position of options is at the bottom of the list, or not. Used to determine if
|
||||
* additional options should be fetched from the API.
|
||||
|
@ -359,30 +363,41 @@ export class APISelect {
|
|||
this.slim.enable();
|
||||
}
|
||||
|
||||
private setSearchKey(event: Event) {
|
||||
const { value: q } = event.target as HTMLInputElement;
|
||||
this.searchKey = q
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listeners to this element and its dependencies so that when dependencies change
|
||||
* this element's options are updated.
|
||||
*/
|
||||
private addEventListeners(): void {
|
||||
// Create a debounced function to fetch options based on the search input value.
|
||||
const fetcher = debounce((event: Event) => this.handleSearch(event), 300, false);
|
||||
const fetcher = debounce((action:ApplyMethod, url: Nullable<string>) => this.handleSearch(action, url), 300, false);
|
||||
|
||||
// Query the API when the input value changes or a value is pasted.
|
||||
this.slim.slim.search.input.addEventListener('keyup', event => {
|
||||
// Only search when necessary keys are pressed.
|
||||
if (!event.key.match(/^(Arrow|Enter|Tab).*/)) {
|
||||
return fetcher(event);
|
||||
this.setSearchKey(event);
|
||||
return fetcher('replace', null);
|
||||
}
|
||||
});
|
||||
this.slim.slim.search.input.addEventListener('paste', event => fetcher(event));
|
||||
this.slim.slim.search.input.addEventListener('paste', event => {
|
||||
this.setSearchKey(event);
|
||||
return fetcher('replace', null);;
|
||||
});
|
||||
|
||||
// Watch every scroll event to determine if the scroll position is at bottom.
|
||||
this.slim.slim.list.addEventListener('scroll', () => this.handleScroll());
|
||||
|
||||
// When the scroll position is at bottom, fetch additional options.
|
||||
this.base.addEventListener(`netbox.select.atbottom.${this.name}`, () =>
|
||||
this.fetchOptions(this.more, 'merge'),
|
||||
);
|
||||
this.base.addEventListener(`netbox.select.atbottom.${this.name}`, () => {
|
||||
if (this.more!=null) {
|
||||
return fetcher('merge', this.more, )
|
||||
}
|
||||
});
|
||||
|
||||
// When the base select element is disabled or enabled, properly disable/enable this instance.
|
||||
this.base.addEventListener(`netbox.select.disabled.${this.name}`, event =>
|
||||
|
@ -551,6 +566,14 @@ export class APISelect {
|
|||
}
|
||||
}
|
||||
|
||||
private getUrl() {
|
||||
var url = this.queryUrl
|
||||
if (this.searchKey!=null) {
|
||||
url = queryString.stringifyUrl({ url: this.queryUrl, query: { q : this.searchKey } })
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the NetBox API for this element's options.
|
||||
*/
|
||||
|
@ -559,21 +582,25 @@ export class APISelect {
|
|||
this.resetOptions();
|
||||
return;
|
||||
}
|
||||
await this.fetchOptions(this.queryUrl, action);
|
||||
const url = this.getUrl()
|
||||
await this.fetchOptions(url, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the API for a specific search pattern and add the results to the available options.
|
||||
*/
|
||||
private async handleSearch(event: Event) {
|
||||
const { value: q } = event.target as HTMLInputElement;
|
||||
const url = queryString.stringifyUrl({ url: this.queryUrl, query: { q } });
|
||||
if (!url.includes(`{{`)) {
|
||||
await this.fetchOptions(url, 'merge');
|
||||
this.slim.data.search(q);
|
||||
this.slim.render();
|
||||
private async handleSearch(action: ApplyMethod = 'merge', url: Nullable<string> ) {
|
||||
if (url==null) {
|
||||
url = this.getUrl()
|
||||
}
|
||||
return;
|
||||
if (url.includes(`{{`)) {
|
||||
return
|
||||
}
|
||||
await this.fetchOptions(url, action);
|
||||
if (this.searchKey!=null) {
|
||||
this.slim.data.search(this.searchKey);
|
||||
}
|
||||
this.slim.render();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -586,13 +613,11 @@ export class APISelect {
|
|||
Math.floor(this.slim.slim.list.scrollTop) + this.slim.slim.list.offsetHeight ===
|
||||
this.slim.slim.list.scrollHeight;
|
||||
|
||||
if (this.atBottom && !atBottom) {
|
||||
this.atBottom = false;
|
||||
this.atBottom = atBottom
|
||||
|
||||
if (this.atBottom) {
|
||||
this.base.dispatchEvent(this.bottomEvent);
|
||||
} else if (!this.atBottom && atBottom) {
|
||||
this.atBottom = true;
|
||||
this.base.dispatchEvent(this.bottomEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -994,7 +1019,9 @@ export class APISelect {
|
|||
['btn', 'btn-sm', 'btn-ghost-dark'],
|
||||
[createElement('i', null, ['mdi', 'mdi-reload'])],
|
||||
);
|
||||
refreshButton.addEventListener('click', () => this.loadData());
|
||||
// calling this.loadData() will prevent first page of returned items
|
||||
// with non-null search key inplace not selectable
|
||||
refreshButton.addEventListener('click', () => this.handleSearch('replace', null));
|
||||
refreshButton.type = 'button';
|
||||
this.slim.slim.search.container.appendChild(refreshButton);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue