forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			102 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { InputElement } from "./InputElement"
 | |
| import Translations from "../i18n/Translations"
 | |
| import { UIEventSource } from "../../Logic/UIEventSource"
 | |
| import BaseUIElement from "../BaseUIElement"
 | |
| 
 | |
| export class DropDown<T> extends InputElement<T> {
 | |
|     private static _nextDropdownId = 0
 | |
|     public IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false)
 | |
| 
 | |
|     private readonly _element: HTMLElement
 | |
| 
 | |
|     private readonly _value: UIEventSource<T>
 | |
|     private readonly _values: { value: T; shown: string | BaseUIElement }[]
 | |
| 
 | |
|     /**
 | |
|      *
 | |
|      * const dropdown = new DropDown<number>("test",[{value: 42, shown: "the answer"}])
 | |
|      * dropdown.GetValue().data // => 42
 | |
|      */
 | |
|     constructor(
 | |
|         label: string | BaseUIElement,
 | |
|         values: { value: T; shown: string | BaseUIElement }[],
 | |
|         value: UIEventSource<T> = undefined,
 | |
|         options?: {
 | |
|             select_class?: string
 | |
|         }
 | |
|     ) {
 | |
|         super()
 | |
|         value = value ?? new UIEventSource<T>(values[0].value)
 | |
|         this._value = value
 | |
|         this._values = values
 | |
|         if (values.length <= 1) {
 | |
|             return
 | |
|         }
 | |
| 
 | |
|         const id = DropDown._nextDropdownId
 | |
|         DropDown._nextDropdownId++
 | |
| 
 | |
|         const el = document.createElement("form")
 | |
|         this._element = el
 | |
|         el.id = "dropdown" + id
 | |
| 
 | |
|         {
 | |
|             const labelEl = Translations.W(label)?.ConstructElement()
 | |
|             if (labelEl !== undefined) {
 | |
|                 const labelHtml = document.createElement("label")
 | |
|                 labelHtml.appendChild(labelEl)
 | |
|                 labelHtml.htmlFor = el.id
 | |
|                 el.appendChild(labelHtml)
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         options = options ?? {}
 | |
|         options.select_class =
 | |
|             options.select_class ?? "w-full bg-indigo-100 p-1 rounded hover:bg-indigo-200"
 | |
| 
 | |
|         {
 | |
|             const select = document.createElement("select")
 | |
|             select.classList.add(...(options.select_class.split(" ") ?? []))
 | |
|             for (let i = 0; i < values.length; i++) {
 | |
|                 const option = document.createElement("option")
 | |
|                 option.value = "" + i
 | |
|                 option.appendChild(Translations.W(values[i].shown).ConstructElement())
 | |
|                 select.appendChild(option)
 | |
|             }
 | |
|             el.appendChild(select)
 | |
| 
 | |
|             select.onchange = () => {
 | |
|                 const index = select.selectedIndex
 | |
|                 value.setData(values[index].value)
 | |
|             }
 | |
| 
 | |
|             value.addCallbackAndRun((selected) => {
 | |
|                 for (let i = 0; i < values.length; i++) {
 | |
|                     const value = values[i].value
 | |
|                     if (value === selected) {
 | |
|                         select.selectedIndex = i
 | |
|                     }
 | |
|                 }
 | |
|             })
 | |
|         }
 | |
| 
 | |
|         this.onClick(() => {}) // by registering a click, the click event is consumed and doesn't bubble further to other elements, e.g. checkboxes
 | |
|     }
 | |
| 
 | |
|     GetValue(): UIEventSource<T> {
 | |
|         return this._value
 | |
|     }
 | |
| 
 | |
|     IsValid(t: T): boolean {
 | |
|         for (const value of this._values) {
 | |
|             if (value.value === t) {
 | |
|                 return true
 | |
|             }
 | |
|         }
 | |
|         return false
 | |
|     }
 | |
| 
 | |
|     protected InnerConstructElement(): HTMLElement {
 | |
|         return this._element
 | |
|     }
 | |
| }
 |