Host/Solo modal code cleanup (#2939)

## Description:

Refactor / Clean-up code inside Host/Solo modals.

## 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:

w.o.n
This commit is contained in:
Ryan
2026-01-18 02:08:23 +00:00
committed by GitHub
parent 1e629ca531
commit 8bc037e098
3 changed files with 322 additions and 497 deletions
+100 -301
View File
@@ -33,6 +33,10 @@ import { modalHeader } from "./components/ui/ModalHeader";
import { crazyGamesSDK } from "./CrazyGamesSDK";
import { JoinLobbyEvent } from "./Main";
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
import {
renderToggleInputCard,
renderToggleInputCardInput,
} from "./utilities/RenderToggleInputCard";
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
import randomMap from "/images/RandomMap.webp?url";
@customElement("host-lobby-modal")
@@ -127,6 +131,35 @@ export class HostLobbyModal extends BaseModal {
}
render() {
const maxTimerHandlers = this.createToggleHandlers(
() => this.maxTimer,
(val) => (this.maxTimer = val),
() => this.maxTimerValue,
(val) => (this.maxTimerValue = val),
30,
);
const spawnImmunityHandlers = this.createToggleHandlers(
() => this.spawnImmunity,
(val) => (this.spawnImmunity = val),
() => this.spawnImmunityDurationMinutes,
(val) => (this.spawnImmunityDurationMinutes = val),
5,
);
const goldMultiplierHandlers = this.createToggleHandlers(
() => this.goldMultiplier,
(val) => (this.goldMultiplier = val),
() => this.goldMultiplierValue,
(val) => (this.goldMultiplierValue = val),
2,
);
const startingGoldHandlers = this.createToggleHandlers(
() => this.startingGold,
(val) => (this.startingGold = val),
() => this.startingGoldValue,
(val) => (this.startingGoldValue = val),
5000000,
);
const content = html`
<div
class="h-full flex flex-col bg-black/60 backdrop-blur-md rounded-2xl border border-white/10 overflow-hidden select-none"
@@ -504,313 +537,79 @@ export class HostLobbyModal extends BaseModal {
)}
<!-- Max Timer -->
<div
role="button"
tabindex="0"
@click=${this.createToggleHandlers(
() => this.maxTimer,
(val) => (this.maxTimer = val),
() => this.maxTimerValue,
(val) => (this.maxTimerValue = val),
30,
).click}
@keydown=${this.createToggleHandlers(
() => this.maxTimer,
(val) => (this.maxTimer = val),
() => this.maxTimerValue,
(val) => (this.maxTimerValue = val),
30,
).keydown}
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${this
.maxTimer
? "bg-blue-500/20 border-blue-500/50 shadow-[0_0_15px_rgba(59,130,246,0.2)]"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20 opacity-80"}"
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${this
.maxTimer
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${this.maxTimer
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${this.maxTimer
? html`
<input
type="number"
min="0"
max="120"
.value=${String(this.maxTimerValue ?? 0)}
class="w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1"
@click=${(e: Event) => e.stopPropagation()}
@input=${this.handleMaxTimerValueChanges}
@keydown=${this.handleMaxTimerValueKeyDown}
placeholder=${translateText(
"host_modal.mins_placeholder",
)}
/>
`
: html`<div
class="h-[2px] w-4 bg-white/10 rounded my-3"
></div>`}
<div
class="text-[10px] uppercase font-bold tracking-wider text-center w-full leading-tight ${this
.maxTimer
? "text-white"
: "text-white/60"}"
>
${translateText("host_modal.max_timer")}
</div>
</div>
${renderToggleInputCard({
labelKey: "host_modal.max_timer",
checked: this.maxTimer,
onClick: maxTimerHandlers.click,
input: renderToggleInputCardInput({
min: 0,
max: 120,
value: this.maxTimerValue ?? 0,
ariaLabel: translateText("host_modal.max_timer"),
placeholder: translateText("host_modal.mins_placeholder"),
onInput: this.handleMaxTimerValueChanges,
onKeyDown: this.handleMaxTimerValueKeyDown,
}),
})}
<!-- Spawn Immunity -->
<div
role="button"
tabindex="0"
@click=${this.createToggleHandlers(
() => this.spawnImmunity,
(val) => (this.spawnImmunity = val),
() => this.spawnImmunityDurationMinutes,
(val) => (this.spawnImmunityDurationMinutes = val),
5,
).click}
@keydown=${this.createToggleHandlers(
() => this.spawnImmunity,
(val) => (this.spawnImmunity = val),
() => this.spawnImmunityDurationMinutes,
(val) => (this.spawnImmunityDurationMinutes = val),
5,
).keydown}
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${this
.spawnImmunity
? "bg-blue-500/20 border-blue-500/50 shadow-[0_0_15px_rgba(59,130,246,0.2)]"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20 opacity-80"}"
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${this
.spawnImmunity
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${this.spawnImmunity
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${this.spawnImmunity
? html`
<input
type="number"
min="0"
max="120"
step="1"
.value=${String(
this.spawnImmunityDurationMinutes ?? 0,
)}
class="w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1"
@click=${(e: Event) => e.stopPropagation()}
@input=${this.handleSpawnImmunityDurationInput}
@keydown=${this.handleSpawnImmunityDurationKeyDown}
placeholder=${translateText(
"host_modal.mins_placeholder",
)}
/>
`
: html`<div
class="h-[2px] w-4 bg-white/10 rounded my-3"
></div>`}
<div
class="text-[10px] uppercase font-bold tracking-wider text-center w-full leading-tight ${this
.spawnImmunity
? "text-white"
: "text-white/60"}"
>
${translateText("host_modal.player_immunity_duration")}
</div>
</div>
${renderToggleInputCard({
labelKey: "host_modal.player_immunity_duration",
checked: this.spawnImmunity,
onClick: spawnImmunityHandlers.click,
input: renderToggleInputCardInput({
min: 0,
max: 120,
step: 1,
value: this.spawnImmunityDurationMinutes ?? 0,
ariaLabel: translateText(
"host_modal.player_immunity_duration",
),
placeholder: translateText("host_modal.mins_placeholder"),
onInput: this.handleSpawnImmunityDurationInput,
onKeyDown: this.handleSpawnImmunityDurationKeyDown,
}),
})}
<!-- Gold Multiplier -->
<div
role="button"
tabindex="0"
@click=${this.createToggleHandlers(
() => this.goldMultiplier,
(val) => (this.goldMultiplier = val),
() => this.goldMultiplierValue,
(val) => (this.goldMultiplierValue = val),
2,
).click}
@keydown=${this.createToggleHandlers(
() => this.goldMultiplier,
(val) => (this.goldMultiplier = val),
() => this.goldMultiplierValue,
(val) => (this.goldMultiplierValue = val),
2,
).keydown}
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${this
.goldMultiplier
? "bg-blue-500/20 border-blue-500/50"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20"}"
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${this
.goldMultiplier
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${this.goldMultiplier
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${this.goldMultiplier
? html`<input
type="number"
id="gold-multiplier-value"
min="0.1"
max="1000"
step="any"
value=${this.goldMultiplierValue ?? ""}
class="w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1"
aria-label=${translateText(
"single_modal.gold_multiplier",
)}
@change=${this.handleGoldMultiplierValueChanges}
@keydown=${this.handleGoldMultiplierValueKeyDown}
placeholder=${translateText(
"single_modal.gold_multiplier_placeholder",
)}
/>`
: html`<div
class="h-[2px] w-4 bg-white/10 rounded my-3"
></div>`}
<div
class="text-[10px] uppercase font-bold text-white/60 tracking-wider text-center w-full leading-tight break-words hyphens-auto"
>
${translateText("single_modal.gold_multiplier")}
</div>
</div>
${renderToggleInputCard({
labelKey: "single_modal.gold_multiplier",
checked: this.goldMultiplier,
onClick: goldMultiplierHandlers.click,
input: renderToggleInputCardInput({
id: "gold-multiplier-value",
min: 0.1,
max: 1000,
step: "any",
value: this.goldMultiplierValue ?? "",
ariaLabel: translateText("single_modal.gold_multiplier"),
placeholder: translateText(
"single_modal.gold_multiplier_placeholder",
),
onChange: this.handleGoldMultiplierValueChanges,
onKeyDown: this.handleGoldMultiplierValueKeyDown,
}),
})}
<!-- Starting Gold -->
<div
role="button"
tabindex="0"
@click=${this.createToggleHandlers(
() => this.startingGold,
(val) => (this.startingGold = val),
() => this.startingGoldValue,
(val) => (this.startingGoldValue = val),
5000000,
).click}
@keydown=${this.createToggleHandlers(
() => this.startingGold,
(val) => (this.startingGold = val),
() => this.startingGoldValue,
(val) => (this.startingGoldValue = val),
5000000,
).keydown}
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${this
.startingGold
? "bg-blue-500/20 border-blue-500/50"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20"}"
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${this
.startingGold
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${this.startingGold
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${this.startingGold
? html`<input
type="number"
id="starting-gold-value"
min="0"
max="1000000000"
step="100000"
.value=${String(this.startingGoldValue ?? "")}
class="w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1"
aria-label=${translateText(
"single_modal.starting_gold",
)}
@input=${this.handleStartingGoldValueChanges}
@keydown=${this.handleStartingGoldValueKeyDown}
placeholder=${translateText(
"single_modal.starting_gold_placeholder",
)}
/>`
: html`<div
class="h-[2px] w-4 bg-white/10 rounded my-3"
></div>`}
<div
class="text-[10px] uppercase font-bold text-white/60 tracking-wider text-center w-full leading-tight break-words hyphens-auto"
>
${translateText("single_modal.starting_gold")}
</div>
</div>
${renderToggleInputCard({
labelKey: "single_modal.starting_gold",
checked: this.startingGold,
onClick: startingGoldHandlers.click,
input: renderToggleInputCardInput({
id: "starting-gold-value",
min: 0,
max: 1000000000,
step: 100000,
value: this.startingGoldValue ?? "",
ariaLabel: translateText("single_modal.starting_gold"),
placeholder: translateText(
"single_modal.starting_gold_placeholder",
),
onInput: this.handleStartingGoldValueChanges,
onKeyDown: this.handleStartingGoldValueKeyDown,
}),
})}
</div>
</div>
+60 -196
View File
@@ -30,6 +30,10 @@ import { fetchCosmetics } from "./Cosmetics";
import { FlagInput } from "./FlagInput";
import { JoinLobbyEvent } from "./Main";
import { UsernameInput } from "./UsernameInput";
import {
renderToggleInputCard,
renderToggleInputCardInput,
} from "./utilities/RenderToggleInputCard";
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
import randomMap from "/images/RandomMap.webp?url";
@@ -522,20 +526,10 @@ export class SinglePlayerModal extends BaseModal {
}
},
)}
<!-- Toggle with input support for Max Timer -->
<div
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${this
.maxTimer
? "bg-blue-500/20 border-blue-500/50"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20"}"
@click=${(e: Event) => {
// Prevent toggling when clicking the input
if (
(e.target as HTMLElement).tagName.toLowerCase() ===
"input"
)
return;
${renderToggleInputCard({
labelKey: "single_modal.max_timer",
checked: this.maxTimer,
onClick: () => {
this.maxTimer = !this.maxTimer;
if (!this.maxTimer) {
this.maxTimerValue = undefined;
@@ -553,71 +547,26 @@ export class SinglePlayerModal extends BaseModal {
}
}, 0);
}
}}
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${this
.maxTimer
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${this.maxTimer
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${this.maxTimer
? html`<input
type="number"
id="end-timer-value"
min="1"
max="120"
.value=${String(this.maxTimerValue ?? "")}
class="w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1"
aria-label=${translateText("single_modal.max_timer")}
@input=${this.handleMaxTimerValueChanges}
@keydown=${this.handleMaxTimerValueKeyDown}
placeholder=${translateText(
"single_modal.max_timer_placeholder",
)}
/>`
: html`<div
class="h-[2px] w-4 bg-white/10 rounded my-3"
></div>`}
<!-- Spacer/Icon placeholder -->
<div
class="text-[10px] uppercase font-bold text-white/60 tracking-wider text-center w-full leading-tight break-words hyphens-auto"
>
${translateText("single_modal.max_timer")}
</div>
</div>
},
input: renderToggleInputCardInput({
id: "end-timer-value",
min: 1,
max: 120,
value: this.maxTimerValue ?? "",
ariaLabel: translateText("single_modal.max_timer"),
placeholder: translateText(
"single_modal.max_timer_placeholder",
),
onInput: this.handleMaxTimerValueChanges,
onKeyDown: this.handleMaxTimerValueKeyDown,
}),
})}
<!-- Gold Multiplier -->
<div
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${this
.goldMultiplier
? "bg-blue-500/20 border-blue-500/50"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20"}"
@click=${(e: Event) => {
if (
(e.target as HTMLElement).tagName.toLowerCase() ===
"input"
)
return;
${renderToggleInputCard({
labelKey: "single_modal.gold_multiplier",
checked: this.goldMultiplier,
onClick: () => {
this.goldMultiplier = !this.goldMultiplier;
if (!this.goldMultiplier) {
this.goldMultiplierValue = undefined;
@@ -638,73 +587,27 @@ export class SinglePlayerModal extends BaseModal {
}
}, 0);
}
}}
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${this
.goldMultiplier
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${this.goldMultiplier
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${this.goldMultiplier
? html`<input
type="number"
id="gold-multiplier-value"
min="0.1"
max="1000"
step="any"
value=${this.goldMultiplierValue ?? ""}
class="w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1"
aria-label=${translateText(
"single_modal.gold_multiplier",
)}
@change=${this.handleGoldMultiplierValueChanges}
@keydown=${this.handleGoldMultiplierValueKeyDown}
placeholder=${translateText(
"single_modal.gold_multiplier_placeholder",
)}
/>`
: html`<div
class="h-[2px] w-4 bg-white/10 rounded my-3"
></div>`}
<div
class="text-[10px] uppercase font-bold text-white/60 tracking-wider text-center w-full leading-tight break-words hyphens-auto"
>
${translateText("single_modal.gold_multiplier")}
</div>
</div>
},
input: renderToggleInputCardInput({
id: "gold-multiplier-value",
min: 0.1,
max: 1000,
step: "any",
value: this.goldMultiplierValue ?? "",
ariaLabel: translateText("single_modal.gold_multiplier"),
placeholder: translateText(
"single_modal.gold_multiplier_placeholder",
),
onChange: this.handleGoldMultiplierValueChanges,
onKeyDown: this.handleGoldMultiplierValueKeyDown,
}),
})}
<!-- Starting Gold -->
<div
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${this
.startingGold
? "bg-blue-500/20 border-blue-500/50"
: "bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20"}"
@click=${(e: Event) => {
if (
(e.target as HTMLElement).tagName.toLowerCase() ===
"input"
)
return;
${renderToggleInputCard({
labelKey: "single_modal.starting_gold",
checked: this.startingGold,
onClick: () => {
this.startingGold = !this.startingGold;
if (!this.startingGold) {
this.startingGoldValue = undefined;
@@ -725,60 +628,21 @@ export class SinglePlayerModal extends BaseModal {
}
}, 0);
}
}}
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${this
.startingGold
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${this.startingGold
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${this.startingGold
? html`<input
type="number"
id="starting-gold-value"
min="0"
max="1000000000"
step="100000"
.value=${String(this.startingGoldValue ?? "")}
class="w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1"
aria-label=${translateText(
"single_modal.starting_gold",
)}
@input=${this.handleStartingGoldValueChanges}
@keydown=${this.handleStartingGoldValueKeyDown}
placeholder=${translateText(
"single_modal.starting_gold_placeholder",
)}
/>`
: html`<div
class="h-[2px] w-4 bg-white/10 rounded my-3"
></div>`}
<div
class="text-[10px] uppercase font-bold text-white/60 tracking-wider text-center w-full leading-tight break-words hyphens-auto"
>
${translateText("single_modal.starting_gold")}
</div>
</div>
},
input: renderToggleInputCardInput({
id: "starting-gold-value",
min: 0,
max: 1000000000,
step: 100000,
value: this.startingGoldValue ?? "",
ariaLabel: translateText("single_modal.starting_gold"),
placeholder: translateText(
"single_modal.starting_gold_placeholder",
),
onInput: this.handleStartingGoldValueChanges,
onKeyDown: this.handleStartingGoldValueKeyDown,
}),
})}
</div>
</div>
@@ -0,0 +1,162 @@
import { TemplateResult, html, nothing } from "lit";
import { translateText } from "../Utils";
export const TOGGLE_INPUT_CARD_CLASSES = {
containerActive:
"bg-blue-500/20 border-blue-500/50 shadow-[0_0_15px_rgba(59,130,246,0.2)]",
containerInactive:
"bg-white/5 border-white/10 hover:bg-white/10 hover:border-white/20 opacity-80",
labelBase:
"text-[10px] uppercase font-bold tracking-wider text-center w-full leading-tight break-words hyphens-auto",
labelActive: "text-white",
labelInactive: "text-white/60",
input:
"w-full text-center rounded bg-black/60 text-white text-sm font-bold border border-white/20 focus:outline-none focus:border-blue-500 p-1 my-1",
};
export interface ToggleInputCardInputOptions {
id?: string;
type?: string;
min?: number | string;
max?: number | string;
step?: number | string;
value?: number | string;
ariaLabel?: string;
placeholder?: string;
onInput?: (e: Event) => void;
onChange?: (e: Event) => void;
onKeyDown?: (e: KeyboardEvent) => void;
onClick?: (e: Event) => void;
className?: string;
}
export function renderToggleInputCardInput({
id,
type = "number",
min,
max,
step,
value,
ariaLabel,
placeholder,
onInput,
onChange,
onKeyDown,
onClick,
className = TOGGLE_INPUT_CARD_CLASSES.input,
}: ToggleInputCardInputOptions): TemplateResult {
const resolvedValue = value ?? "";
const handleClick = onClick ?? ((e: Event) => e.stopPropagation());
return html`
<input
type=${type}
id=${id ?? nothing}
min=${min ?? nothing}
max=${max ?? nothing}
step=${step ?? nothing}
.value=${String(resolvedValue)}
class=${className}
aria-label=${ariaLabel ?? nothing}
placeholder=${placeholder ?? nothing}
@click=${handleClick}
@input=${onInput}
@change=${onChange}
@keydown=${onKeyDown}
/>
`;
}
export interface ToggleInputCardRenderContext {
labelKey: string;
checked: boolean;
input?: TemplateResult;
onClick?: (e: Event) => void;
onKeyDown?: (e: KeyboardEvent) => void;
activeClassName?: string;
inactiveClassName?: string;
labelBaseClassName?: string;
labelActiveClassName?: string;
labelInactiveClassName?: string;
role?: string;
tabIndex?: number;
}
export function renderToggleInputCard({
labelKey,
checked,
input,
onClick,
onKeyDown,
activeClassName = TOGGLE_INPUT_CARD_CLASSES.containerActive,
inactiveClassName = TOGGLE_INPUT_CARD_CLASSES.containerInactive,
labelBaseClassName = TOGGLE_INPUT_CARD_CLASSES.labelBase,
labelActiveClassName = TOGGLE_INPUT_CARD_CLASSES.labelActive,
labelInactiveClassName = TOGGLE_INPUT_CARD_CLASSES.labelInactive,
role,
tabIndex,
}: ToggleInputCardRenderContext): TemplateResult {
const shouldBehaveLikeButton = Boolean(onClick ?? onKeyDown);
const resolvedRole = role ?? (shouldBehaveLikeButton ? "button" : undefined);
const resolvedTabIndex = tabIndex ?? (shouldBehaveLikeButton ? 0 : undefined);
const resolvedOnKeyDown =
onKeyDown ??
(onClick
? (e: KeyboardEvent) => {
if ((e.target as HTMLElement).tagName.toLowerCase() === "input") {
return;
}
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onClick(e);
}
}
: undefined);
return html`
<div
role=${resolvedRole ?? nothing}
tabindex=${resolvedTabIndex ?? nothing}
@click=${onClick}
@keydown=${resolvedOnKeyDown}
class="relative p-3 rounded-xl border transition-all duration-200 flex flex-col items-center justify-between gap-2 h-full cursor-pointer min-h-[100px] ${checked
? activeClassName
: inactiveClassName}"
>
<div class="flex items-center justify-center w-full mt-1">
<div
class="w-5 h-5 rounded border flex items-center justify-center transition-colors ${checked
? "bg-blue-500 border-blue-500"
: "border-white/20 bg-white/5"}"
>
${checked
? html`<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-white"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>`
: ""}
</div>
</div>
${checked
? (input ?? html``)
: html`<div class="h-[2px] w-4 bg-white/10 rounded my-3"></div>`}
<div
class="${labelBaseClassName} ${checked
? labelActiveClassName
: labelInactiveClassName}"
>
${translateText(labelKey)}
</div>
</div>
`;
}