Make a generic AjaxSelect abstract class

This commit is contained in:
Antoine Bartuccio 2024-10-20 02:26:32 +02:00
parent f78b968075
commit 0af3505c2a
3 changed files with 63 additions and 35 deletions

View File

@ -112,42 +112,72 @@ class AutocompleteSelect extends inheritHtmlElement("select") {
}
}
@registerComponent("user-ajax-select")
export class UserAjaxSelect extends AutocompleteSelect {
public filter?: <T>(items: T[]) => T[];
abstract class AjaxSelect extends AutocompleteSelect {
protected filter?: (items: TomOption[]) => TomOption[] = null;
protected minCharNumberForSearch = 2;
protected abstract valueField: string;
protected abstract labelField: string;
protected abstract renderOption(
item: TomOption,
sanitize: typeof escape_html,
): string;
protected abstract renderItem(item: TomOption, sanitize: typeof escape_html): string;
protected abstract search(query: string): Promise<TomOption[]>;
public setFilter(filter?: (items: TomOption[]) => TomOption[]) {
this.filter = filter;
}
protected getLoadFunction() {
// this will be replaced by TomSelect if we don't wrap it that way
return async (query: string, callback: TomLoadCallback) => {
const resp = await this.search(query);
if (this.filter) {
callback(this.filter(resp), []);
} else {
callback(resp, []);
}
};
}
protected tomSelectSettings(): RecursivePartial<TomSettings> {
return {
...super.tomSelectSettings(),
hideSelected: true,
diacritics: true,
duplicates: false,
valueField: "id",
labelField: "display_name",
valueField: this.valueField,
labelField: this.labelField,
searchField: [], // Disable local search filter and rely on tested backend
load: (query: string, callback: TomLoadCallback) => {
userSearchUsers({
query: {
search: query,
},
}).then((response) => {
if (response.data) {
if (this.filter) {
callback(this.filter(response.data.results), []);
} else {
callback(response.data.results, []);
}
return;
}
callback([], []);
});
},
load: this.getLoadFunction(),
render: {
...super.tomSelectSettings().render,
option: (item: UserProfileSchema, sanitize: typeof escape_html) => {
return `<div class="select-item">
option: this.renderOption,
item: this.renderItem,
},
};
}
}
@registerComponent("user-ajax-select")
export class UserAjaxSelect extends AjaxSelect {
protected valueField = "id";
protected labelField = "display_name";
protected async search(query: string): Promise<TomOption[]> {
const resp = await userSearchUsers({ query: { search: query } });
if (resp.data) {
return resp.data.results;
}
return [];
}
protected tomSelectSettings(): RecursivePartial<TomSettings> {
return super.tomSelectSettings();
}
protected renderOption(item: UserProfileSchema, sanitize: typeof escape_html) {
return `<div class="select-item">
<img
src="${sanitize(item.profile_pict)}"
alt="${sanitize(item.display_name)}"
@ -155,11 +185,9 @@ export class UserAjaxSelect extends AutocompleteSelect {
/>
<span class="select-item-text">${sanitize(item.display_name)}</span>
</div>`;
},
item: (item: UserProfileSchema, sanitize: typeof escape_html) => {
return `<span><i class="fa fa-times"></i>${sanitize(item.display_name)}</span>`;
},
},
};
}
protected renderItem(item: UserProfileSchema, sanitize: typeof escape_html) {
return `<span><i class="fa fa-times"></i>${sanitize(item.display_name)}</span>`;
}
}

View File

@ -1,14 +1,14 @@
import type { Client, Options, RequestResult } from "@hey-api/client-fetch";
import { client } from "#openapi";
interface PaginatedResponse<T> {
export interface PaginatedResponse<T> {
count: number;
next: string | null;
previous: string | null;
results: T[];
}
interface PaginatedRequest {
export interface PaginatedRequest {
query?: {
page?: number;
// biome-ignore lint/style/useNamingConvention: api is in snake_case

View File

@ -177,7 +177,7 @@ exportToHtml("loadViewer", (config: ViewerConfig) => {
} as PicturesFetchPicturesData)
).map(PictureWithIdentifications.fromPicture);
this.selector = this.$refs.search;
this.selector.filter = (users: UserProfileSchema[]) => {
this.selector.setFilter((users: UserProfileSchema[]) => {
const resp: UserProfileSchema[] = [];
const ids = [
...(this.currentPicture.identifications || []).map(
@ -190,7 +190,7 @@ exportToHtml("loadViewer", (config: ViewerConfig) => {
}
}
return resp;
};
});
this.currentPicture = this.pictures.find(
(i: PictureSchema) => i.id === config.firstPictureId,
);