2024-10-15 20:06:22 +00:00
import "tom-select/dist/css/tom-select.css" ;
import TomSelect from "tom-select" ;
2024-10-16 00:22:27 +00:00
import type { TomItem , TomLoadCallback , TomOption } from "tom-select/dist/types/types" ;
2024-10-15 20:06:22 +00:00
import type { escape_html } from "tom-select/dist/types/utils" ;
import { type UserProfileSchema , userSearchUsers } from "#openapi" ;
export class AjaxSelect extends HTMLSelectElement {
widget : TomSelect ;
filter ? : < T > ( items : T [ ] ) = > T [ ] ;
constructor ( ) {
super ( ) ;
window . addEventListener ( "DOMContentLoaded" , ( ) = > {
this . loadTomSelect ( ) ;
} ) ;
}
loadTomSelect() {
2024-10-16 00:22:27 +00:00
const minCharNumberForsearch = 2 ;
2024-10-15 20:06:22 +00:00
let maxItems = 1 ;
if ( this . multiple ) {
maxItems = Number . parseInt ( this . dataset . max ) ? ? null ;
}
this . widget = new TomSelect ( this , {
hideSelected : true ,
2024-10-16 00:22:27 +00:00
diacritics : true ,
duplicates : false ,
2024-10-15 20:06:22 +00:00
maxItems : maxItems ,
loadThrottle : Number.parseInt ( this . dataset . delay ) ? ? null ,
valueField : "id" ,
labelField : "display_name" ,
searchField : [ "display_name" , "nick_name" , "first_name" , "last_name" ] ,
placeholder : this.dataset.placeholder ? ? "" ,
2024-10-16 00:22:27 +00:00
shouldLoad : ( query : string ) = > {
return query . length >= minCharNumberForsearch ; // Avoid launching search with less than 2 characters
} ,
2024-10-15 20:06:22 +00:00
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 ( [ ] , [ ] ) ;
} ) ;
} ,
render : {
option : ( item : UserProfileSchema , sanitize : typeof escape_html ) = > {
return ` <div class="select-item">
< img
src = "${sanitize(item.profile_pict)}"
alt = "${sanitize(item.display_name)}"
onerror = "this.src = '/static/core/img/unknown.jpg'"
/ >
< 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> ` ;
} ,
2024-10-16 00:22:27 +00:00
// biome-ignore lint/style/useNamingConvention: that's how it's defined
not_loading : ( data : TomOption , _sanitize : typeof escape_html ) = > {
return ` <div class="no-results"> ${ interpolate ( gettext ( "You need to type %(number)s more characters" ) , { number : minCharNumberForsearch - data . input . length } , true)}</div> ` ;
} ,
// biome-ignore lint/style/useNamingConvention: that's how it's defined
no_results : ( _data : TomOption , _sanitize : typeof escape_html ) = > {
return ` <div class="no-results"> ${ gettext ( "No results found" ) } </div> ` ;
} ,
2024-10-15 20:06:22 +00:00
} ,
} ) ;
this . widget . on ( "item_select" , ( item : TomItem ) = > {
this . widget . removeItem ( item ) ;
} ) ;
}
}
window . customElements . define ( "ajax-select" , AjaxSelect , { extends : "select" } ) ;