mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-15 02:33:22 +00:00
Add proper delete button and fix item ordering
This commit is contained in:
parent
bb3f277ba5
commit
be5ce414ba
@ -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>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user