mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 00:53:08 +00:00 
			
		
		
		
	Use tomselect instead of jquery autoselect for counter clicks
This commit is contained in:
		| @@ -0,0 +1,61 @@ | ||||
| import { AutoCompleteSelectBase } from "#core:core/components/ajax-select-base"; | ||||
| import { registerComponent } from "#core:utils/web-components"; | ||||
| import type { RecursivePartial, TomSettings } from "tom-select/dist/types/types"; | ||||
|  | ||||
| const productParsingRegex = /^(\d+x)?(.*)/i; | ||||
|  | ||||
| function parseProduct(query: string): [number, string] { | ||||
|   const parsed = productParsingRegex.exec(query); | ||||
|   return [Number.parseInt(parsed[1] || "1"), parsed[2]]; | ||||
| } | ||||
|  | ||||
| @registerComponent("counter-product-select") | ||||
| export class CounterProductSelect extends AutoCompleteSelectBase { | ||||
|   public getOperationCodes(): string[] { | ||||
|     return ["FIN", "ANN"]; | ||||
|   } | ||||
|  | ||||
|   protected attachBehaviors(): void { | ||||
|     this.allowMultipleProducts(); | ||||
|   } | ||||
|  | ||||
|   private allowMultipleProducts(): void { | ||||
|     const search = this.widget.search; | ||||
|     const onOptionSelect = this.widget.onOptionSelect; | ||||
|     this.widget.hook("instead", "search", (query: string) => { | ||||
|       return search.call(this.widget, parseProduct(query)[1]); | ||||
|     }); | ||||
|     this.widget.hook( | ||||
|       "instead", | ||||
|       "onOptionSelect", | ||||
|       (evt: MouseEvent | KeyboardEvent, option: HTMLElement) => { | ||||
|         const [quantity, _] = parseProduct(this.widget.inputValue()); | ||||
|         const originalValue = option.getAttribute("data-value") ?? option.innerText; | ||||
|  | ||||
|         if (quantity === 1 || this.getOperationCodes().includes(originalValue)) { | ||||
|           return onOptionSelect.call(this.widget, evt, option); | ||||
|         } | ||||
|  | ||||
|         const value = `${quantity}x${originalValue}`; | ||||
|         const label = `${quantity}x${option.innerText}`; | ||||
|         this.widget.addOption({ value: value, text: label }, true); | ||||
|         return onOptionSelect.call( | ||||
|           this.widget, | ||||
|           evt, | ||||
|           this.widget.getOption(value, true), | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|  | ||||
|     this.widget.hook("after", "onOptionSelect", () => { | ||||
|       /* Focus the next element if it's an input */ | ||||
|       if (this.nextElementSibling.nodeName === "INPUT") { | ||||
|         (this.nextElementSibling as HTMLInputElement).focus(); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|   protected tomSelectSettings(): RecursivePartial<TomSettings> { | ||||
|     /* We disable the dropdown on focus because we're going to always autofocus the widget */ | ||||
|     return { ...super.tomSelectSettings(), openOnFocus: false }; | ||||
|   } | ||||
| } | ||||
| @@ -1,4 +1,5 @@ | ||||
| import { exportToHtml } from "#core:utils/globals"; | ||||
| import type TomSelect from "tom-select"; | ||||
|  | ||||
| interface CounterConfig { | ||||
|   csrfToken: string; | ||||
| @@ -20,6 +21,12 @@ exportToHtml("loadCounter", (config: CounterConfig) => { | ||||
|       basket: config.sessionBasket, | ||||
|       errors: [], | ||||
|       customerBalance: config.customerBalance, | ||||
|       codeField: undefined, | ||||
|  | ||||
|       init() { | ||||
|         this.codeField = this.$refs.codeField; | ||||
|         this.codeField.widget.focus(); | ||||
|       }, | ||||
|  | ||||
|       sumBasket() { | ||||
|         if (!this.basket || Object.keys(this.basket).length === 0) { | ||||
| @@ -40,17 +47,19 @@ exportToHtml("loadCounter", (config: CounterConfig) => { | ||||
|           (event.detail.target.querySelector("#id_amount") as HTMLInputElement).value, | ||||
|         ); | ||||
|         document.getElementById("selling-accordion").click(); | ||||
|         this.codeField.widget.focus(); | ||||
|       }, | ||||
|  | ||||
|       async handleCode(event: SubmitEvent) { | ||||
|         const code = ( | ||||
|           $(event.target).find("#code_field").val() as string | ||||
|         ).toUpperCase(); | ||||
|         if (["FIN", "ANN"].includes(code)) { | ||||
|         const widget: TomSelect = this.codeField.widget; | ||||
|         const code = (widget.getValue() as string).toUpperCase(); | ||||
|         if (this.codeField.getOperationCodes().includes(code)) { | ||||
|           $(event.target).submit(); | ||||
|         } else { | ||||
|           await this.handleAction(event); | ||||
|         } | ||||
|         widget.clear(); | ||||
|         widget.focus(); | ||||
|       }, | ||||
|  | ||||
|       async handleAction(event: SubmitEvent) { | ||||
| @@ -68,54 +77,12 @@ exportToHtml("loadCounter", (config: CounterConfig) => { | ||||
|         const json = await response.json(); | ||||
|         this.basket = json.basket; | ||||
|         this.errors = json.errors; | ||||
|         $("form.code_form #code_field").val("").focus(); | ||||
|       }, | ||||
|     })); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| interface Product { | ||||
|   value: string; | ||||
|   label: string; | ||||
|   tags: string; | ||||
| } | ||||
| declare global { | ||||
|   const productsAutocomplete: Product[]; | ||||
| } | ||||
|  | ||||
| $(() => { | ||||
|   /* Autocompletion in the code field */ | ||||
|   // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery | ||||
|   const codeField: any = $("#code_field"); | ||||
|  | ||||
|   let quantity = ""; | ||||
|   codeField.autocomplete({ | ||||
|     // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery | ||||
|     select: (event: any, ui: any) => { | ||||
|       event.preventDefault(); | ||||
|       codeField.val(quantity + ui.item.value); | ||||
|     }, | ||||
|     // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery | ||||
|     focus: (event: any, ui: any) => { | ||||
|       event.preventDefault(); | ||||
|       codeField.val(quantity + ui.item.value); | ||||
|     }, | ||||
|     // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery | ||||
|     source: (request: any, response: any) => { | ||||
|       // biome-ignore lint/performance/useTopLevelRegex: performance impact is minimal | ||||
|       const res = /^(\d+x)?(.*)/i.exec(request.term); | ||||
|       quantity = res[1] || ""; | ||||
|       const search = res[2]; | ||||
|       // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery | ||||
|       const matcher = new RegExp(($ as any).ui.autocomplete.escapeRegex(search), "i"); | ||||
|       response( | ||||
|         $.grep(productsAutocomplete, (value: Product) => { | ||||
|           return matcher.test(value.tags); | ||||
|         }), | ||||
|       ); | ||||
|     }, | ||||
|   }); | ||||
|  | ||||
|   /* Accordion UI between basket and refills */ | ||||
|   // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery | ||||
|   ($("#click_form") as any).accordion({ | ||||
| @@ -124,6 +91,4 @@ $(() => { | ||||
|   }); | ||||
|   // biome-ignore lint/suspicious/noExplicitAny: dealing with legacy jquery | ||||
|   ($("#products") as any).tabs(); | ||||
|  | ||||
|   codeField.focus(); | ||||
| }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user