Files
OpenFrontIO/src/client/components/baseComponents/setting/SettingKeybind.ts
T
Aotumuri 3478b3ab13 Fix: Prevent unbind button overflow and wrap long text in keybind settings (#2287)
## Description:
This PR fixes layout issues in the User Settings → Keybind section where
the “Unbind” button text would overflow in languages with long
translations (e.g., Italian).

before 
<img width="372" height="477" alt="スクリーンショット 2025-10-25 15 42 09"
src="https://github.com/user-attachments/assets/ce31e116-1848-4350-a6da-011b10a42668"
/>

after
<img width="383" height="168" alt="スクリーンショット 2025-10-25 15 41 47"
src="https://github.com/user-attachments/assets/8133ed56-f920-42a8-9477-78561fdd477a"
/>

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced

## Please put your Discord username so you can be contacted if a bug or
regression is found:

aotumuri

Co-authored-by: Evan <evanpelle@gmail.com>
2025-10-25 14:35:24 -07:00

123 lines
3.5 KiB
TypeScript

import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { translateText } from "../../../../client/Utils";
@customElement("setting-keybind")
export class SettingKeybind extends LitElement {
@property() label = "Setting";
@property() description = "";
@property({ type: String, reflect: true }) action = "";
@property({ type: String }) defaultKey = "";
@property({ type: String }) value = "";
@property({ type: Boolean }) easter = false;
createRenderRoot() {
return this;
}
private listening = false;
render() {
return html`
<div class="setting-item column${this.easter ? " easter-egg" : ""}">
<div class="setting-label-group">
<label class="setting-label block mb-1">${this.label}</label>
<div class="setting-keybind-box flex flex-wrap items-start gap-2">
<div
class="setting-keybind-description flex-1 min-w-[240px] max-w-full whitespace-normal break-words text-sm text-gray-300"
style="word-break: break-word;"
>
${this.description}
</div>
<div
class="flex flex-wrap items-center gap-2 gap-y-1 basis-full sm:basis-auto min-w-0"
>
<span
class="setting-key shrink-0"
tabindex="0"
@keydown=${this.handleKeydown}
@click=${this.startListening}
>
${this.displayKey(this.value || this.defaultKey)}
</span>
<button
class="text-xs text-gray-400 hover:text-white border border-gray-500 px-2 py-0.5 rounded transition whitespace-normal break-words max-w-full"
@click=${this.resetToDefault}
>
${translateText("user_setting.reset")}
</button>
<button
class="text-xs text-gray-400 hover:text-white border border-gray-500 px-2 py-0.5 rounded transition whitespace-normal break-words max-w-full"
@click=${this.unbindKey}
>
${translateText("user_setting.unbind")}
</button>
</div>
</div>
</div>
</div>
`;
}
private displayKey(key: string): string {
if (key === " ") return "Space";
if (key.startsWith("Key") && key.length === 4) {
return key.slice(3);
}
return key.length
? key.charAt(0).toUpperCase() + key.slice(1)
: "Press a key";
}
private startListening() {
this.listening = true;
this.requestUpdate();
}
private handleKeydown(e: KeyboardEvent) {
if (!this.listening) return;
e.preventDefault();
const code = e.code;
this.value = code;
this.dispatchEvent(
new CustomEvent("change", {
detail: { action: this.action, value: code, key: e.key },
bubbles: true,
composed: true,
}),
);
this.listening = false;
this.requestUpdate();
}
private resetToDefault() {
this.value = this.defaultKey;
this.dispatchEvent(
new CustomEvent("change", {
detail: { action: this.action, value: this.defaultKey },
bubbles: true,
composed: true,
}),
);
}
private unbindKey() {
this.value = "";
this.dispatchEvent(
new CustomEvent("change", {
detail: { action: this.action, value: "Null" },
bubbles: true,
composed: true,
}),
);
this.requestUpdate();
}
}