mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 00:53:08 +00:00 
			
		
		
		
	Add proper delete button and fix item ordering
This commit is contained in:
		| @@ -25,10 +25,6 @@ | |||||||
| .ts-control { | .ts-control { | ||||||
|  |  | ||||||
|   .item { |   .item { | ||||||
|     .fa-times { |  | ||||||
|       margin-left: 5px; |  | ||||||
|       margin-right: 5px; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     cursor: pointer; |     cursor: pointer; | ||||||
|     background-color: #e4e4e4; |     background-color: #e4e4e4; | ||||||
| @@ -39,6 +35,7 @@ | |||||||
|     margin-top: 5px; |     margin-top: 5px; | ||||||
|     margin-bottom: 5px; |     margin-bottom: 5px; | ||||||
|     padding-right: 10px; |     padding-right: 10px; | ||||||
|  |     padding-left: 10px; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
| @@ -3,7 +3,6 @@ import { inheritHtmlElement, registerComponent } from "#core:utils/web-component | |||||||
| import TomSelect from "tom-select"; | import TomSelect from "tom-select"; | ||||||
| import type { | import type { | ||||||
|   RecursivePartial, |   RecursivePartial, | ||||||
|   TomItem, |  | ||||||
|   TomLoadCallback, |   TomLoadCallback, | ||||||
|   TomOption, |   TomOption, | ||||||
|   TomSettings, |   TomSettings, | ||||||
| @@ -71,14 +70,25 @@ class AutocompleteSelect extends inheritHtmlElement("select") { | |||||||
|     this.attachBehaviors(); |     this.attachBehaviors(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   protected shouldLoad(query: string) { | ||||||
|  |     console.log(this); | ||||||
|  |     return query.length >= this.minCharNumberForSearch; // Avoid launching search with less than setup number of characters | ||||||
|  |   } | ||||||
|  |  | ||||||
|   protected tomSelectSettings(): RecursivePartial<TomSettings> { |   protected tomSelectSettings(): RecursivePartial<TomSettings> { | ||||||
|     return { |     return { | ||||||
|  |       plugins: { | ||||||
|  |         // biome-ignore lint/style/useNamingConvention: this is required by the api | ||||||
|  |         remove_button: { | ||||||
|  |           title: gettext("Remove"), | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       persist: false, | ||||||
|       maxItems: this.node.multiple ? this.max : 1, |       maxItems: this.node.multiple ? this.max : 1, | ||||||
|  |       closeAfterSelect: true, | ||||||
|       loadThrottle: this.delay, |       loadThrottle: this.delay, | ||||||
|       placeholder: this.placeholder, |       placeholder: this.placeholder, | ||||||
|       shouldLoad: (query: string) => { |       shouldLoad: (query: string) => this.shouldLoad(query), // wraps the method to avoid shadowing `this` by the one from tom-select | ||||||
|         return query.length >= this.minCharNumberForSearch; // Avoid launching search with less than 2 characters |  | ||||||
|       }, |  | ||||||
|       render: { |       render: { | ||||||
|         option: (item: TomOption, sanitize: typeof escape_html) => { |         option: (item: TomOption, sanitize: typeof escape_html) => { | ||||||
|           return `<div class="select-item"> |           return `<div class="select-item"> | ||||||
| @@ -86,7 +96,7 @@ class AutocompleteSelect extends inheritHtmlElement("select") { | |||||||
|           </div>`; |           </div>`; | ||||||
|         }, |         }, | ||||||
|         item: (item: TomOption, sanitize: typeof escape_html) => { |         item: (item: TomOption, sanitize: typeof escape_html) => { | ||||||
|           return `<span><i class="fa fa-times"></i>${sanitize(item.text)}</span>`; |           return `<span>${sanitize(item.text)}</span>`; | ||||||
|         }, |         }, | ||||||
|         // biome-ignore lint/style/useNamingConvention: that's how it's defined |         // biome-ignore lint/style/useNamingConvention: that's how it's defined | ||||||
|         not_loading: (data: TomOption, _sanitize: typeof escape_html) => { |         not_loading: (data: TomOption, _sanitize: typeof escape_html) => { | ||||||
| @@ -101,14 +111,7 @@ class AutocompleteSelect extends inheritHtmlElement("select") { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected attachBehaviors() { |   protected attachBehaviors() { | ||||||
|     // Allow removing selected items by clicking on them |     /* Called once the widget has been initialized */ | ||||||
|     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(""); |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -118,6 +121,8 @@ abstract class AjaxSelect extends AutocompleteSelect { | |||||||
|  |  | ||||||
|   protected abstract valueField: string; |   protected abstract valueField: string; | ||||||
|   protected abstract labelField: string; |   protected abstract labelField: string; | ||||||
|  |   protected abstract searchField: string[]; | ||||||
|  |  | ||||||
|   protected abstract renderOption( |   protected abstract renderOption( | ||||||
|     item: TomOption, |     item: TomOption, | ||||||
|     sanitize: typeof escape_html, |     sanitize: typeof escape_html, | ||||||
| @@ -129,16 +134,28 @@ abstract class AjaxSelect extends AutocompleteSelect { | |||||||
|     this.filter = filter; |     this.filter = filter; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected getLoadFunction() { |   protected shouldLoad(query: string) { | ||||||
|     // this will be replaced by TomSelect if we don't wrap it that way |     const resp = super.shouldLoad(query); | ||||||
|     return async (query: string, callback: TomLoadCallback) => { |     /* Force order sync with backend if no client side filtering is set */ | ||||||
|  |     if (!resp && this.searchField.length === 0) { | ||||||
|  |       this.widget.clearOptions(); | ||||||
|  |     } | ||||||
|  |     return resp; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   protected async loadFunction(query: string, callback: TomLoadCallback) { | ||||||
|  |     /* Force order sync with backend if no client side filtering is set */ | ||||||
|  |     if (this.searchField.length === 0) { | ||||||
|  |       this.widget.clearOptions(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const resp = await this.search(query); |     const resp = await this.search(query); | ||||||
|  |  | ||||||
|     if (this.filter) { |     if (this.filter) { | ||||||
|       callback(this.filter(resp), []); |       callback(this.filter(resp), []); | ||||||
|     } else { |     } else { | ||||||
|       callback(resp, []); |       callback(resp, []); | ||||||
|     } |     } | ||||||
|     }; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected tomSelectSettings(): RecursivePartial<TomSettings> { |   protected tomSelectSettings(): RecursivePartial<TomSettings> { | ||||||
| @@ -149,8 +166,9 @@ abstract class AjaxSelect extends AutocompleteSelect { | |||||||
|       duplicates: false, |       duplicates: false, | ||||||
|       valueField: this.valueField, |       valueField: this.valueField, | ||||||
|       labelField: this.labelField, |       labelField: this.labelField, | ||||||
|       searchField: [], // Disable local search filter and rely on tested backend |       searchField: this.searchField, | ||||||
|       load: this.getLoadFunction(), |       load: (query: string, callback: TomLoadCallback) => | ||||||
|  |         this.loadFunction(query, callback), // wraps the method to avoid shadowing `this` by the one from tom-select | ||||||
|       render: { |       render: { | ||||||
|         ...super.tomSelectSettings().render, |         ...super.tomSelectSettings().render, | ||||||
|         option: this.renderOption, |         option: this.renderOption, | ||||||
| @@ -166,7 +184,7 @@ abstract class AjaxSelect extends AutocompleteSelect { | |||||||
|     for (const value of Array.from(this.children) |     for (const value of Array.from(this.children) | ||||||
|       .filter((child) => child.tagName.toLowerCase() === "slot") |       .filter((child) => child.tagName.toLowerCase() === "slot") | ||||||
|       .map((slot) => JSON.parse(slot.innerHTML))) { |       .map((slot) => JSON.parse(slot.innerHTML))) { | ||||||
|       this.widget.addOption(value, true); |       this.widget.addOption(value, false); | ||||||
|       this.widget.addItem(value[this.valueField]); |       this.widget.addItem(value[this.valueField]); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -176,6 +194,7 @@ abstract class AjaxSelect extends AutocompleteSelect { | |||||||
| export class UserAjaxSelect extends AjaxSelect { | export class UserAjaxSelect extends AjaxSelect { | ||||||
|   protected valueField = "id"; |   protected valueField = "id"; | ||||||
|   protected labelField = "display_name"; |   protected labelField = "display_name"; | ||||||
|  |   protected searchField: string[] = []; // Disable local search filter and rely on tested backend | ||||||
|  |  | ||||||
|   protected async search(query: string): Promise<TomOption[]> { |   protected async search(query: string): Promise<TomOption[]> { | ||||||
|     const resp = await userSearchUsers({ query: { search: query } }); |     const resp = await userSearchUsers({ query: { search: query } }); | ||||||
| @@ -197,7 +216,7 @@ export class UserAjaxSelect extends AjaxSelect { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected renderItem(item: UserProfileSchema, sanitize: typeof escape_html) { |   protected renderItem(item: UserProfileSchema, sanitize: typeof escape_html) { | ||||||
|     return `<span><i class="fa fa-times"></i>${sanitize(item.display_name)}</span>`; |     return `<span>${sanitize(item.display_name)}</span>`; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -205,6 +224,7 @@ export class UserAjaxSelect extends AjaxSelect { | |||||||
| export class GroupsAjaxSelect extends AjaxSelect { | export class GroupsAjaxSelect extends AjaxSelect { | ||||||
|   protected valueField = "id"; |   protected valueField = "id"; | ||||||
|   protected labelField = "name"; |   protected labelField = "name"; | ||||||
|  |   protected searchField = ["name"]; | ||||||
|  |  | ||||||
|   protected async search(query: string): Promise<TomOption[]> { |   protected async search(query: string): Promise<TomOption[]> { | ||||||
|     const resp = await groupSearchGroup({ query: { search: query } }); |     const resp = await groupSearchGroup({ query: { search: query } }); | ||||||
| @@ -221,6 +241,6 @@ export class GroupsAjaxSelect extends AjaxSelect { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   protected renderItem(item: GroupSchema, sanitize: typeof escape_html) { |   protected renderItem(item: GroupSchema, sanitize: typeof escape_html) { | ||||||
|     return `<span><i class="fa fa-times"></i>${sanitize(item.name)}</span>`; |     return `<span>${sanitize(item.name)}</span>`; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user