|
1 | 1 | { |
2 | 2 | class WoltlabCoreLabelPickerElement extends HTMLElement { |
| 3 | + #button: HTMLButtonElement | undefined = undefined; |
| 4 | + #input: HTMLInputElement | undefined = undefined; |
3 | 5 | #labels: Map<number, string> | undefined = undefined; |
4 | | - #name: string = ""; |
5 | | - #selected: number = 0; |
6 | 6 |
|
7 | 7 | connectedCallback() { |
8 | 8 | this.#setupLabels(); |
9 | 9 |
|
10 | 10 | this.innerHTML = ""; |
11 | 11 |
|
12 | | - const selected = parseInt(this.getAttribute("selected")!); |
13 | | - this.removeAttribute("selected"); |
14 | | - if (!Number.isNaN(selected)) { |
15 | | - this.selected = selected; |
| 12 | + if (!this.value) { |
| 13 | + this.value = 0; |
16 | 14 | } |
17 | 15 |
|
18 | | - const name = this.getAttribute("name")!; |
19 | | - this.removeAttribute("name"); |
20 | | - if (this.#name === "") { |
21 | | - this.#name = name || "labelIDs[]"; |
| 16 | + if (!this.name) { |
| 17 | + this.name = "labelIDs"; |
22 | 18 | } |
23 | 19 |
|
24 | 20 | this.classList.add("dropdown"); |
25 | 21 |
|
26 | | - const button = document.createElement("button"); |
27 | | - button.type = "button"; |
28 | | - button.classList.add("dropdownToggle"); |
29 | | - button.append(this.#getLabel(this.selected)); |
30 | | - |
31 | | - const dropdownMenu = document.createElement("ol"); |
32 | | - dropdownMenu.classList.add("dropdownMenu"); |
33 | | - |
34 | | - const input = document.createElement("input"); |
35 | | - input.type = "hidden"; |
36 | | - input.name = this.name; |
37 | | - input.value = "0"; |
38 | | - |
39 | | - for (const [labelId, html] of this.#labels!) { |
40 | | - const button2 = document.createElement("button"); |
41 | | - button2.type = "button"; |
42 | | - button2.innerHTML = html; |
43 | | - button2.addEventListener("click", () => { |
44 | | - this.selected = labelId; |
45 | | - |
46 | | - button.innerHTML = ""; |
47 | | - button.append(this.#getLabel(this.selected)); |
48 | | - |
49 | | - input.value = this.#selected.toString(); |
50 | | - }); |
| 22 | + if (this.#button === undefined) { |
| 23 | + this.#button = document.createElement("button"); |
| 24 | + this.#button.type = "button"; |
| 25 | + this.#button.classList.add("dropdownToggle"); |
| 26 | + this.#button.append(this.#getLabel(this.value)); |
| 27 | + } |
51 | 28 |
|
52 | | - const listItem = document.createElement("li"); |
53 | | - listItem.append(button2); |
54 | | - dropdownMenu.append(listItem); |
| 29 | + if (this.#input === undefined) { |
| 30 | + this.#input = document.createElement("input"); |
| 31 | + this.#input.type = "hidden"; |
| 32 | + this.#input.name = this.name; |
| 33 | + this.#input.value = this.value.toString(); |
55 | 34 | } |
56 | 35 |
|
57 | | - this.append(button, dropdownMenu, input); |
| 36 | + this.append(this.#button, this.#buildSelection(), this.#input); |
58 | 37 | } |
59 | 38 |
|
60 | 39 | #setupLabels(): void { |
|
88 | 67 | return template.content.cloneNode(true); |
89 | 68 | } |
90 | 69 |
|
91 | | - get selected(): number { |
92 | | - return this.#selected; |
| 70 | + #buildSelection(): HTMLElement { |
| 71 | + const dropdownMenu = document.createElement("ol"); |
| 72 | + dropdownMenu.classList.add("dropdownMenu"); |
| 73 | + |
| 74 | + for (const [labelId, html] of this.#labels!) { |
| 75 | + dropdownMenu.append(this.#createListItem(labelId, html)); |
| 76 | + } |
| 77 | + |
| 78 | + if (!this.required) { |
| 79 | + const divider = document.createElement("li"); |
| 80 | + divider.classList.add("dropdownDivider"); |
| 81 | + |
| 82 | + const emptySelection = this.#createListItem(0, this.#getEmptyLabel().outerHTML); |
| 83 | + dropdownMenu.append(divider, emptySelection); |
| 84 | + } |
| 85 | + |
| 86 | + return dropdownMenu; |
| 87 | + } |
| 88 | + |
| 89 | + #createListItem(labelId: number, html: string): HTMLLIElement { |
| 90 | + const button = document.createElement("button"); |
| 91 | + button.type = "button"; |
| 92 | + button.innerHTML = html; |
| 93 | + button.addEventListener("click", () => { |
| 94 | + this.value = labelId; |
| 95 | + }); |
| 96 | + |
| 97 | + const listItem = document.createElement("li"); |
| 98 | + listItem.append(button); |
| 99 | + |
| 100 | + return listItem; |
93 | 101 | } |
94 | 102 |
|
95 | | - set selected(selected: number) { |
96 | | - if (this.#labels?.has(selected)) { |
97 | | - this.#selected = selected; |
| 103 | + get value(): number { |
| 104 | + return parseInt(this.getAttribute("value") || ""); |
| 105 | + } |
98 | 106 |
|
99 | | - // TODO: update the button label |
| 107 | + set value(value: number) { |
| 108 | + if (value !== 0 && !this.#labels?.has(value)) { |
| 109 | + throw new Error(`There is no label with the id ${value}.`); |
| 110 | + } |
| 111 | + |
| 112 | + this.setAttribute("value", value.toString()); |
| 113 | + |
| 114 | + if (this.#button !== undefined && this.#input !== undefined) { |
| 115 | + this.#button.innerHTML = ""; |
| 116 | + this.#button.append(this.#getLabel(this.value)); |
| 117 | + |
| 118 | + this.#input.value = value.toString(); |
100 | 119 | } |
101 | 120 | } |
102 | 121 |
|
103 | 122 | get name(): string { |
104 | | - return this.#name; |
| 123 | + return this.getAttribute("name") || ""; |
| 124 | + } |
| 125 | + |
| 126 | + set name(name: string) { |
| 127 | + if (name === "") { |
| 128 | + throw new Error("The name cannot be empty."); |
| 129 | + } |
| 130 | + |
| 131 | + this.setAttribute("name", name); |
| 132 | + |
| 133 | + if (this.#input !== undefined) { |
| 134 | + this.#input.name = name; |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + get required(): boolean { |
| 139 | + return this.hasAttribute("required"); |
| 140 | + } |
| 141 | + |
| 142 | + set required(required: boolean) { |
| 143 | + if (required) { |
| 144 | + this.setAttribute("required", ""); |
| 145 | + } else { |
| 146 | + this.removeAttribute("required"); |
| 147 | + } |
105 | 148 | } |
106 | 149 | } |
107 | 150 |
|
|
0 commit comments