From 3e396576421c20aa7d3e7a0ee349ecf40c4b9fff Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Sun, 21 Dec 2025 19:55:49 +0000 Subject: [PATCH] ui: add RadioBasedTabs does the "tabs" seen on the invite tab (invite expiry/user expiry), and will be used in all other similar cases. --- html/admin.html | 11 +---- ts/modules/accounts.ts | 12 +++++- ts/modules/invites.ts | 42 +++++++------------ ts/modules/ui.ts | 91 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 38 deletions(-) diff --git a/html/admin.html b/html/admin.html index 93f23d3..a21c34b 100644 --- a/html/admin.html +++ b/html/admin.html @@ -597,16 +597,7 @@ {{ .strings.create }}
-
- - -
+
diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index b5200ca..baba53a 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -2872,7 +2872,17 @@ class UserInfo extends PaginatedList {
-

${window.lang.strings("activity")}

+
+ + +
+
diff --git a/ts/modules/invites.ts b/ts/modules/invites.ts index 335bd7c..bd4b0e8 100644 --- a/ts/modules/invites.ts +++ b/ts/modules/invites.ts @@ -13,7 +13,7 @@ import { } from "../modules/common.js"; import { DiscordSearch, DiscordUser, newDiscordSearch } from "../modules/discord.js"; import { reloadProfileNames } from "../modules/profiles.js"; -import { HiddenInputField } from "./ui.js"; +import { HiddenInputField, RadioBasedTabSelector } from "./ui.js"; declare var window: GlobalWindow; @@ -945,8 +945,8 @@ export class createInvite { private _userHours = document.getElementById("user-hours") as HTMLSelectElement; private _userMinutes = document.getElementById("user-minutes") as HTMLSelectElement; - private _invDurationButton = document.getElementById("radio-inv-duration") as HTMLInputElement; - private _userExpiryButton = document.getElementById("radio-user-expiry") as HTMLInputElement; + // private _invDurationButton = document.getElementById("radio-inv-duration") as HTMLInputElement; + // private _userExpiryButton = document.getElementById("radio-user-expiry") as HTMLInputElement; private _invDuration = document.getElementById("inv-duration"); private _userExpiry = document.getElementById("user-expiry"); @@ -1191,30 +1191,18 @@ export class createInvite { this.uses = 1; this.label = ""; - const checkDuration = () => { - const invSpan = this._invDurationButton.nextElementSibling as HTMLSpanElement; - const userSpan = this._userExpiryButton.nextElementSibling as HTMLSpanElement; - if (this._invDurationButton.checked) { - this._invDuration.classList.remove("unfocused"); - this._userExpiry.classList.add("unfocused"); - invSpan.classList.add("@high"); - invSpan.classList.remove("@low"); - userSpan.classList.add("@low"); - userSpan.classList.remove("@high"); - } else if (this._userExpiryButton.checked) { - this._userExpiry.classList.remove("unfocused"); - this._invDuration.classList.add("unfocused"); - invSpan.classList.add("@low"); - invSpan.classList.remove("@high"); - userSpan.classList.add("@high"); - userSpan.classList.remove("@low"); - } - }; - - this._userExpiryButton.checked = false; - this._invDurationButton.checked = true; - this._userExpiryButton.onchange = checkDuration; - this._invDurationButton.onchange = checkDuration; + new RadioBasedTabSelector( + document.getElementById("invites-duration-type-tabs"), + "invites-duration-type", + { + name: window.lang.strings("inviteDuration"), + content: this._invDuration, + }, + { + name: window.lang.strings("userExpiry"), + content: this._userExpiry, + }, + ); this._days.onchange = this._checkDurationValidity; this._months.onchange = this._checkDurationValidity; diff --git a/ts/modules/ui.ts b/ts/modules/ui.ts index a158ed6..80a48f8 100644 --- a/ts/modules/ui.ts +++ b/ts/modules/ui.ts @@ -1,3 +1,5 @@ +declare var window: GlobalWindow; + export interface HiddenInputConf { container: HTMLElement; onSet: () => void; @@ -109,3 +111,92 @@ export class HiddenInputField { this.setEditing(!this.editing, false, noSave); } } + + +export interface RadioBasedTab { + name: string; + content: HTMLElement; + onShow?: () => void; + onHide?: () => void; +} + +interface RadioBasedTabItem { + tab: RadioBasedTab; + input: HTMLInputElement; + button: HTMLElement; +} + +export class RadioBasedTabSelector { + private _id: string; + private _container: HTMLElement; + private _tabs: RadioBasedTabItem[]; + private _selected: string; + constructor(container: HTMLElement, id: string, ...tabs: RadioBasedTab[]) { + this._container = container; + this._container.classList.add("flex", "flex-row", "gap-2"); + this._tabs = []; + this._id = id; + let i = 0; + const frag = document.createDocumentFragment(); + for (let tab of tabs) { + const label = document.createElement("label"); + label.classList.add("grow"); + label.innerHTML = ` + + ${tab.name} + `; + let ft: RadioBasedTabItem = { + tab: tab, + input: label.getElementsByTagName("input")[0] as HTMLInputElement, + button: label.getElementsByClassName("radio-tab-button")[0] as HTMLElement + }; + ft.input.onclick = () => { + ft.input.checked = true; + this.checkSource(); + }; + frag.appendChild(label); + this._tabs.push(ft); + + i++; + } + this._container.replaceChildren(frag); + this.selected = this._tabs[0].tab.name; + } + + checkSource = () => { + for (let tab of this._tabs) { + if (tab.input.checked) { + this._selected = tab.tab.name; + tab.tab.content.classList.remove("unfocused"); + tab.button.classList.add("@high"); + tab.button.classList.remove("@low"); + if (tab.tab.onShow) tab.tab.onShow(); + } else { + tab.tab.content.classList.add("unfocused"); + tab.button.classList.add("@low"); + tab.button.classList.remove("@high"); + if (tab.tab.onHide) tab.tab.onHide(); + } + } + }; + + get selected(): string { return this._selected; } + set selected(name: string) { + for (let tab of this._tabs) { + if (tab.tab.name == name) { + this._selected = tab.tab.name; + tab.input.checked = true; + tab.tab.content.classList.remove("unfocused"); + tab.button.classList.add("@high"); + tab.button.classList.remove("@low"); + if (tab.tab.onShow) tab.tab.onShow(); + } else { + tab.input.checked = false; + tab.tab.content.classList.add("unfocused"); + tab.button.classList.add("@low"); + tab.button.classList.remove("@high"); + if (tab.tab.onHide) tab.tab.onHide(); + } + } + } +}