made sub menu buttons more obvious

This commit is contained in:
MathMan05
2026-06-24 17:02:14 -05:00
parent 5e6bf8868b
commit 4e757cf15c
3 changed files with 195 additions and 127 deletions
+126 -124
View File
@@ -2731,9 +2731,9 @@ class Localuser {
const genSecurity = () => {
security.removeAll();
if (this.mfa_enabled) {
security.addButtonInput("", I18n.localuser["2faDisable"](), () => {
const form = security.addSubForm(
I18n.localuser["2faDisable"](),
security.addSubButtonInput(I18n.localuser["2faDisable"](), (sub) => {
const form = sub.addForm(
"",
(_: any) => {
if (_.message) {
switch (_.code) {
@@ -2755,13 +2755,13 @@ class Localuser {
form.addTextInput(I18n.localuser["2faCode:"](), "code", {required: true});
});
} else {
security.addButtonInput("", I18n.localuser["2faEnable"](), async () => {
security.addSubButtonInput(I18n.localuser["2faEnable"](), async (sub) => {
let secret = "";
for (let i = 0; i < 18; i++) {
secret += "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[Math.floor(Math.random() * 32)];
}
const form = security.addSubForm(
I18n.localuser.setUp2fa(),
const form = sub.addForm(
"",
(_: any) => {
if (_.message) {
switch (_.code) {
@@ -2794,8 +2794,7 @@ class Localuser {
});
}
{
security.addButtonInput("", I18n.webauth.manage(), () => {
const keyMenu = security.addSubOptions("Manage Keys");
security.addSubButtonInput(I18n.webauth.manage(), (keyMenu) => {
const addKey = (key: {name: string; id: string}) => {
keyMenu.addButtonInput("", key.name, () => {
const opt = keyMenu.addSubOptions(key.name);
@@ -2882,9 +2881,9 @@ class Localuser {
});
});
}
security.addButtonInput("", I18n.localuser.changeDiscriminator(), () => {
const form = security.addSubForm(
I18n.localuser.changeDiscriminator(),
security.addSubButtonInput(I18n.localuser.changeDiscriminator(), (sub) => {
const form = sub.addForm(
"",
(_) => {
security.returnFromSub();
},
@@ -2896,9 +2895,9 @@ class Localuser {
);
form.addTextInput(I18n.localuser.newDiscriminator(), "discriminator");
});
security.addButtonInput("", I18n.localuser.changeEmail(), () => {
const form = security.addSubForm(
I18n.localuser.changeEmail(),
security.addSubButtonInput(I18n.localuser.changeEmail(), (sub) => {
const form = sub.addForm(
"",
(_) => {
security.returnFromSub();
},
@@ -2916,9 +2915,9 @@ class Localuser {
}
form.addTextInput(I18n.localuser["newEmail:"](), "email");
});
security.addButtonInput("", I18n.localuser.changeUsername(), () => {
const form = security.addSubForm(
I18n.localuser.changeUsername(),
security.addSubButtonInput(I18n.localuser.changeUsername(), (sub) => {
const form = sub.addForm(
"",
(_) => {
security.returnFromSub();
},
@@ -2936,9 +2935,9 @@ class Localuser {
}
form.addTextInput(I18n.localuser.newUsername(), "username");
});
security.addButtonInput("", I18n.localuser.changePassword(), () => {
const form = security.addSubForm(
I18n.localuser.changePassword(),
security.addSubButtonInput(I18n.localuser.changePassword(), (sub) => {
const form = sub.addForm(
"",
(_) => {
security.returnFromSub();
},
@@ -3196,9 +3195,9 @@ class Localuser {
}).then(async (teamsRes) => {
const teams = await teamsRes.json();
const button = devPortal.addButtonInput("", I18n.localuser.createApp(), () => {
const form = devPortal.addSubForm(
I18n.localuser.createApp(),
const button = devPortal.addSubButtonInput(I18n.localuser.createApp(), (sub) => {
const form = sub.addForm(
"",
(json: any) => {
if (json.message) form.error("name", json.message);
else {
@@ -3412,111 +3411,114 @@ class Localuser {
});
}
if (this.rights.hasPermission("CREATE_REGISTRATION_TOKENS")) {
manageInstance.addButtonInput("", I18n.manageInstance.createTokens(), () => {
const tokens = manageInstance.addSubOptions(I18n.manageInstance.createTokens(), {
noSubmit: true,
});
const count = tokens.addTextInput(I18n.manageInstance.count(), () => {}, {
initText: "1",
});
const length = tokens.addTextInput(I18n.manageInstance.length(), () => {}, {
initText: "32",
});
const format = tokens.addSelect(
I18n.manageInstance.format(),
() => {},
[
I18n.manageInstance.TokenFormats.JSON(),
I18n.manageInstance.TokenFormats.plain(),
I18n.manageInstance.TokenFormats.URLs(),
],
{
defaultIndex: 2,
},
);
format.watchForChange((e) => {
if (e !== 2) {
urlOption.removeAll();
} else {
makeURLMenu();
}
});
const urlOption = tokens.addOptions("");
const urlOptionsJSON = {
url: window.location.origin,
type: "Jank",
};
function makeURLMenu() {
urlOption
.addTextInput(I18n.manageInstance.clientURL(), () => {}, {
initText: urlOptionsJSON.url,
})
.watchForChange((str) => {
urlOptionsJSON.url = str;
});
urlOption
.addSelect(
I18n.manageInstance.regType(),
() => {},
["Jank", I18n.manageInstance.genericType()],
{
defaultIndex: ["Jank", "generic"].indexOf(urlOptionsJSON.type),
},
)
.watchForChange((i) => {
urlOptionsJSON.type = ["Jank", "generic"][i];
});
}
makeURLMenu();
tokens.addButtonInput("", I18n.manageInstance.create(), async () => {
const params = new URLSearchParams();
params.set("count", count.value);
params.set("length", length.value);
const json = (await (
await fetch(
this.info.api + "/auth/generate-registration-tokens?" + params.toString(),
{
headers: this.headers,
},
)
).json()) as {tokens: string[]};
if (format.index === 0) {
pre.textContent = JSON.stringify(json.tokens);
} else if (format.index === 1) {
pre.textContent = json.tokens.join("\n");
} else if (format.index === 2) {
if (urlOptionsJSON.type === "Jank") {
const options = new URLSearchParams();
options.set("instance", this.info.wellknown);
pre.textContent = json.tokens
.map((token) => {
options.set("token", token);
return `${urlOptionsJSON.url}/register?` + options.toString();
})
.join("\n");
manageInstance.addSubButtonInput(
I18n.manageInstance.createTokens(),
(tokens) => {
const count = tokens.addTextInput(I18n.manageInstance.count(), () => {}, {
initText: "1",
});
const length = tokens.addTextInput(I18n.manageInstance.length(), () => {}, {
initText: "32",
});
const format = tokens.addSelect(
I18n.manageInstance.format(),
() => {},
[
I18n.manageInstance.TokenFormats.JSON(),
I18n.manageInstance.TokenFormats.plain(),
I18n.manageInstance.TokenFormats.URLs(),
],
{
defaultIndex: 2,
},
);
format.watchForChange((e) => {
if (e !== 2) {
urlOption.removeAll();
} else {
const options = new URLSearchParams();
pre.textContent = json.tokens
.map((token) => {
options.set("token", token);
return `${urlOptionsJSON.url}/register?` + options.toString();
})
.join("\n");
makeURLMenu();
}
});
const urlOption = tokens.addOptions("");
const urlOptionsJSON = {
url: window.location.origin,
type: "Jank",
};
function makeURLMenu() {
urlOption
.addTextInput(I18n.manageInstance.clientURL(), () => {}, {
initText: urlOptionsJSON.url,
})
.watchForChange((str) => {
urlOptionsJSON.url = str;
});
urlOption
.addSelect(
I18n.manageInstance.regType(),
() => {},
["Jank", I18n.manageInstance.genericType()],
{
defaultIndex: ["Jank", "generic"].indexOf(urlOptionsJSON.type),
},
)
.watchForChange((i) => {
urlOptionsJSON.type = ["Jank", "generic"][i];
});
}
});
tokens.addButtonInput("", I18n.manageInstance.copy(), async () => {
try {
if (pre.textContent) {
await navigator.clipboard.writeText(pre.textContent);
makeURLMenu();
tokens.addButtonInput("", I18n.manageInstance.create(), async () => {
const params = new URLSearchParams();
params.set("count", count.value);
params.set("length", length.value);
const json = (await (
await fetch(
this.info.api + "/auth/generate-registration-tokens?" + params.toString(),
{
headers: this.headers,
},
)
).json()) as {tokens: string[]};
if (format.index === 0) {
pre.textContent = JSON.stringify(json.tokens);
} else if (format.index === 1) {
pre.textContent = json.tokens.join("\n");
} else if (format.index === 2) {
if (urlOptionsJSON.type === "Jank") {
const options = new URLSearchParams();
options.set("instance", this.info.wellknown);
pre.textContent = json.tokens
.map((token) => {
options.set("token", token);
return `${urlOptionsJSON.url}/register?` + options.toString();
})
.join("\n");
} else {
const options = new URLSearchParams();
pre.textContent = json.tokens
.map((token) => {
options.set("token", token);
return `${urlOptionsJSON.url}/register?` + options.toString();
})
.join("\n");
}
}
} catch (err) {
console.error(err);
}
});
const pre = document.createElement("pre");
tokens.addHTMLArea(pre);
});
});
tokens.addButtonInput("", I18n.manageInstance.copy(), async () => {
try {
if (pre.textContent) {
await navigator.clipboard.writeText(pre.textContent);
}
} catch (err) {
console.error(err);
}
});
const pre = document.createElement("pre");
tokens.addHTMLArea(pre);
},
{
noSubmit: true,
},
);
}
}
(async () => {
+46
View File
@@ -354,6 +354,42 @@ class CheckboxInput implements OptionsElement<boolean> {
}
}
class SubButtonInput implements OptionsElement<void> {
readonly owner: Options;
readonly onClick: (opt: Options) => void;
textContent: string;
value!: void;
opts?: {noSubmit?: boolean; ltr?: boolean};
constructor(
textContent: string,
onClick: (opt: Options) => void,
owner: Options,
{} = {},
opts?: SubButtonInput["opts"],
) {
this.owner = owner;
this.onClick = onClick;
this.textContent = textContent;
this.opts = opts;
}
buttonHtml?: HTMLButtonElement;
generateHTML(): HTMLDivElement {
const div = document.createElement("div");
const button = document.createElement("button");
button.classList.add("subButton");
button.textContent = this.textContent;
button.onclick = () => {
this.onClick(this.owner.addSubOptions(this.textContent, this.opts));
};
this.buttonHtml = button;
div.append(button);
return div;
}
watchForChange() {}
submit() {}
}
class ButtonInput implements OptionsElement<void> {
readonly label: string;
readonly owner: Options;
@@ -1516,6 +1552,16 @@ class Options implements OptionsElement<void> {
this.generate(button);
return button;
}
addSubButtonInput(
textContent: string,
onSubmit: (opt: Options) => void,
{ltr = false, noSubmit = false} = {},
) {
const button = new SubButtonInput(textContent, onSubmit, this, {ltr, noSubmit});
this.options.push(button);
this.generate(button);
return button;
}
addCheckboxInput(label: string, onSubmit: (str: boolean) => void, {initState = false} = {}) {
const box = new CheckboxInput(label, onSubmit, this, {initState});
this.options.push(box);
+23 -3
View File
@@ -755,6 +755,23 @@ select {
cursor: pointer;
transition: background 0.1s ease-in-out;
}
.subButton {
min-width: 350px;
padding: 10px;
position: relative;
&::after {
content: "";
mask: url(./icons/intoMenu.svg);
mask-size: contain !important;
width: 24px;
height: 24px;
background: var(--primary-text-soft);
display: block;
position: absolute;
right: 4px;
top: 8px;
}
}
input::file-selector-button {
margin-right: 6px;
}
@@ -2896,11 +2913,13 @@ input[type="search"]:read-only {
background: var(--spoiler-bg);
color: var(--spoiler-bg);
cursor: pointer;
samp, a {
samp,
a {
background: transparent;
color: inherit;
}
pre, .quote {
pre,
.quote {
background: var(--spoiler-bg);
color: var(--spoiler-bg);
}
@@ -2908,7 +2927,8 @@ input[type="search"]:read-only {
.spoiler:hover {
background: var(--spoiler-hover);
color: var(--spoiler-hover);
pre, .quote {
pre,
.quote {
background: var(--spoiler-hover);
color: var(--spoiler-hover);
}