Create base class for ajax-select

This commit is contained in:
Antoine Bartuccio 2024-10-18 23:26:04 +02:00
parent b9cbba2309
commit 56cc4776a6
2 changed files with 68 additions and 29 deletions

View File

@ -1,41 +1,89 @@
import "tom-select/dist/css/tom-select.css";
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components";
import TomSelect from "tom-select";
import type { TomItem, TomLoadCallback, TomOption } from "tom-select/dist/types/types";
import type {
RecursivePartial,
TomItem,
TomLoadCallback,
TomOption,
TomSettings,
} from "tom-select/dist/types/types";
import type { escape_html } from "tom-select/dist/types/utils";
import { type UserProfileSchema, userSearchUsers } from "#openapi";
@registerComponent("ajax-select")
export class AjaxSelect extends inheritHtmlElement("select") {
abstract class AjaxSelectBase extends inheritHtmlElement("select") {
static observedAttributes = ["delay", "placeholder", "max"];
public widget: TomSelect;
public filter?: <T>(items: T[]) => T[];
private delay: number | null = null;
private placeholder = "";
private max: number | null = null;
attributeChangedCallback(name: string, _oldValue?: string, newValue?: string) {
switch (name) {
case "delay": {
this.delay = Number.parseInt(newValue) ?? null;
break;
}
case "placeholder": {
this.placeholder = newValue ?? "";
break;
}
case "max": {
this.max = Number.parseInt(newValue) ?? null;
break;
}
default: {
return;
}
}
}
constructor() {
super();
window.addEventListener("DOMContentLoaded", () => {
this.loadTomSelect();
this.configureTomSelect(this.defaultTomSelectSettings());
this.setDefaultTomSelectBehaviors();
});
}
loadTomSelect() {
const minCharNumberForSearch = 2;
let maxItems = 1;
if (this.node.multiple) {
maxItems = Number.parseInt(this.node.dataset.max) ?? null;
private defaultTomSelectSettings(): RecursivePartial<TomSettings> {
return {
maxItems: this.max,
loadThrottle: this.delay,
placeholder: this.placeholder,
};
}
private setDefaultTomSelectBehaviors() {
// Allow removing selected items by clicking on them
this.widget.on("item_select", (item: TomItem) => {
this.widget.removeItem(item);
});
// Remove typed text once an item has been selected
this.widget.on("item_add", () => {
this.widget.setTextboxValue("");
});
}
abstract configureTomSelect(defaultSettings: RecursivePartial<TomSettings>): void;
}
@registerComponent("user-ajax-select")
export class UserAjaxSelect extends AjaxSelectBase {
public filter?: <T>(items: T[]) => T[];
configureTomSelect(defaultSettings: RecursivePartial<TomSettings>) {
const minCharNumberForSearch = 2;
this.widget = new TomSelect(this.node, {
...defaultSettings,
hideSelected: true,
diacritics: true,
duplicates: false,
maxItems: maxItems,
loadThrottle: Number.parseInt(this.node.dataset.delay) ?? null,
valueField: "id",
labelField: "display_name",
searchField: ["display_name", "nick_name", "first_name", "last_name"],
placeholder: this.node.dataset.placeholder ?? "",
searchField: [], // Disable local search filter and rely on tested backend
shouldLoad: (query: string) => {
return query.length >= minCharNumberForSearch; // Avoid launching search with less than 2 characters
},
@ -80,14 +128,5 @@ export class AjaxSelect extends inheritHtmlElement("select") {
},
},
});
// Allow removing selected items by clicking on them
this.widget.on("item_select", (item: TomItem) => {
this.widget.removeItem(item);
});
// Remove typed text once an item has been selected
this.widget.on("item_add", () => {
this.widget.setTextboxValue("");
});
}
}

View File

@ -157,12 +157,12 @@
<h5>{% trans %}People{% endtrans %}</h5>
{% if user.was_subscribed %}
<form @submit.prevent="submitIdentification" x-show="!!selector">
<ajax-select
<user-ajax-select
x-ref="search"
multiple
data-delay="300"
data-placeholder="{%- trans -%}Identify users on pictures{%- endtrans -%}"
></ajax-select>
delay="300"
placeholder="{%- trans -%}Identify users on pictures{%- endtrans -%}"
></user-ajax-select>
<input type="submit" value="{% trans %}Go{% endtrans %}"/>
</form>
{% endif %}