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") abstract class AjaxSelect extends AutocompleteSelect {
export class UserAjaxSelect extends AutocompleteSelect { protected filter?: (items: TomOption[]) => TomOption[] = null;
public filter?: <T>(items: T[]) => T[];
protected minCharNumberForSearch = 2; 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> { protected tomSelectSettings(): RecursivePartial<TomSettings> {
return { return {
...super.tomSelectSettings(), ...super.tomSelectSettings(),
hideSelected: true, hideSelected: true,
diacritics: true, diacritics: true,
duplicates: false, duplicates: false,
valueField: "id", valueField: this.valueField,
labelField: "display_name", labelField: this.labelField,
searchField: [], // Disable local search filter and rely on tested backend searchField: [], // Disable local search filter and rely on tested backend
load: (query: string, callback: TomLoadCallback) => { load: this.getLoadFunction(),
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([], []);
});
},
render: { render: {
...super.tomSelectSettings().render, ...super.tomSelectSettings().render,
option: (item: UserProfileSchema, sanitize: typeof escape_html) => { option: this.renderOption,
return `<div class="select-item"> 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 <img
src="${sanitize(item.profile_pict)}" src="${sanitize(item.profile_pict)}"
alt="${sanitize(item.display_name)}" alt="${sanitize(item.display_name)}"
@ -155,11 +185,9 @@ export class UserAjaxSelect extends AutocompleteSelect {
/> />
<span class="select-item-text">${sanitize(item.display_name)}</span> <span class="select-item-text">${sanitize(item.display_name)}</span>
</div>`; </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 type { Client, Options, RequestResult } from "@hey-api/client-fetch";
import { client } from "#openapi"; import { client } from "#openapi";
interface PaginatedResponse<T> { export interface PaginatedResponse<T> {
count: number; count: number;
next: string | null; next: string | null;
previous: string | null; previous: string | null;
results: T[]; results: T[];
} }
interface PaginatedRequest { export interface PaginatedRequest {
query?: { query?: {
page?: number; page?: number;
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case

View File

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