mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 16:56:38 +00:00
+ update user settings modal
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Присъединяване към частна игра",
|
||||
"single_player": "Самостоятелна игра",
|
||||
"instructions": "Инструкции",
|
||||
"settings": "настройки",
|
||||
"how_to_play": "Как се играе",
|
||||
"wiki": "Уики"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "লবিতে যোগ দিন",
|
||||
"single_player": "কম্পিউটার মোড",
|
||||
"instructions": "নির্দেশনা",
|
||||
"settings": "সেটিংস",
|
||||
"how_to_play": "খেলার নিয়ম",
|
||||
"wiki": "উইকি"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Lobby beitreten",
|
||||
"single_player": "Einzelspieler",
|
||||
"instructions": "Anleitung",
|
||||
"settings": "Einstellungen",
|
||||
"how_to_play": "Wie man Spielt",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"join_lobby": "main.join_lobby",
|
||||
"single_player": "main.single_player",
|
||||
"instructions": "main.instructions",
|
||||
"settings": "main.settings",
|
||||
"how_to_play": "main.how_to_play",
|
||||
"wiki": "main.wiki"
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"join_lobby": "Join Lobby",
|
||||
"single_player": "Single Player",
|
||||
"instructions": "Instructions",
|
||||
"settings": "Settings",
|
||||
"how_to_play": "How to Play",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Kunigi salonon",
|
||||
"single_player": "Sola Ludanto",
|
||||
"instructions": "Instrukcioj",
|
||||
"settings": "agordojn",
|
||||
"how_to_play": "Kiel ludi",
|
||||
"wiki": "Vikio"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"join_lobby": "Unirse a una Partida Privada",
|
||||
"single_player": "Un jugador",
|
||||
"instructions": "Instrucciones",
|
||||
"settings": "ajustes",
|
||||
"how_to_play": "Cómo jugar",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Rejoindre un salon",
|
||||
"single_player": "Mode solo",
|
||||
"instructions": "Instructions",
|
||||
"settings": "paramètres",
|
||||
"how_to_play": "Comment jouer ?",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "लॉबी में शामिल हों",
|
||||
"single_player": "कंप्यूटर मोड",
|
||||
"instructions": "निर्देश",
|
||||
"settings": "सेटिंग्स",
|
||||
"how_to_play": "कैसे खेलें",
|
||||
"wiki": "विकी"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Unisciti a una Lobby",
|
||||
"single_player": "Giocatore Singolo",
|
||||
"instructions": "Istruzioni",
|
||||
"settings": "Istruzioni",
|
||||
"how_to_play": "Come si gioca",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"join_lobby": "ロビーに参加",
|
||||
"single_player": "シングルプレイヤー",
|
||||
"instructions": "説明書",
|
||||
"settings": "設定",
|
||||
"how_to_play": "遊び方",
|
||||
"wiki": "ウィキ"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Lobby toetreden",
|
||||
"single_player": "Eén Speler",
|
||||
"instructions": "Instructies",
|
||||
"settings": "instellingen",
|
||||
"how_to_play": "Hoe spelen?",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Dołącz do lobby",
|
||||
"single_player": "Gra jednoosobowa",
|
||||
"instructions": "Instrukcje",
|
||||
"settings": "ustawienia",
|
||||
"how_to_play": "Jak grać",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Entrar na Sala",
|
||||
"single_player": "Um Jogador",
|
||||
"instructions": "Instruções",
|
||||
"settings": "configurações",
|
||||
"how_to_play": "Como Jogar",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Присоединиться к лобби",
|
||||
"single_player": "Одиночная игра",
|
||||
"instructions": "Инструкции",
|
||||
"settings": "настройки",
|
||||
"how_to_play": "Как играть",
|
||||
"wiki": "Вики"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Pridruži se čekaonici",
|
||||
"single_player": "Igraj sam",
|
||||
"instructions": "Instrukcije",
|
||||
"settings": "postavke",
|
||||
"how_to_play": "Kako igrati",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Lobiye Katıl",
|
||||
"single_player": "Tek Oyunculu",
|
||||
"instructions": "Rehber",
|
||||
"settings": "ayarlar",
|
||||
"how_to_play": "Nasıl Oynanır",
|
||||
"wiki": "Wiki"
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"join_lobby": "Приєднатися до лобі",
|
||||
"single_player": "Гра наодинці",
|
||||
"instructions": "Інструкції",
|
||||
"settings": "налаштування",
|
||||
"how_to_play": "Як грати",
|
||||
"wiki": "Вікі"
|
||||
},
|
||||
|
||||
@@ -1,293 +0,0 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import "./LanguageModal";
|
||||
|
||||
import bg from "../../resources/lang/bg.json";
|
||||
import bn from "../../resources/lang/bn.json";
|
||||
import de from "../../resources/lang/de.json";
|
||||
import en from "../../resources/lang/en.json";
|
||||
import eo from "../../resources/lang/eo.json";
|
||||
import es from "../../resources/lang/es.json";
|
||||
import fr from "../../resources/lang/fr.json";
|
||||
import hi from "../../resources/lang/hi.json";
|
||||
import it from "../../resources/lang/it.json";
|
||||
import ja from "../../resources/lang/ja.json";
|
||||
import nl from "../../resources/lang/nl.json";
|
||||
import pl from "../../resources/lang/pl.json";
|
||||
import pt_br from "../../resources/lang/pt_br.json";
|
||||
import ru from "../../resources/lang/ru.json";
|
||||
import sh from "../../resources/lang/sh.json";
|
||||
import tr from "../../resources/lang/tr.json";
|
||||
import uk from "../../resources/lang/uk.json";
|
||||
|
||||
@customElement("lang-selector")
|
||||
export class LangSelector extends LitElement {
|
||||
@state() public translations: any = {};
|
||||
@state() private defaultTranslations: any = {};
|
||||
@state() private currentLang: string = "en";
|
||||
@state() private languageList: any[] = [];
|
||||
@state() private showModal: boolean = false;
|
||||
@state() private debugMode: boolean = false;
|
||||
|
||||
private dKeyPressed: boolean = false;
|
||||
|
||||
private languageMap: Record<string, any> = {
|
||||
bg,
|
||||
bn,
|
||||
de,
|
||||
en,
|
||||
es,
|
||||
eo,
|
||||
fr,
|
||||
it,
|
||||
hi,
|
||||
ja,
|
||||
nl,
|
||||
pl,
|
||||
pt_br,
|
||||
ru,
|
||||
sh,
|
||||
tr,
|
||||
uk,
|
||||
};
|
||||
|
||||
createRenderRoot() {
|
||||
return this; // Use Light DOM if you prefer this
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.setupDebugKey();
|
||||
this.initializeLanguage();
|
||||
}
|
||||
|
||||
private setupDebugKey() {
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.key.toLowerCase() === "t") this.dKeyPressed = true;
|
||||
});
|
||||
window.addEventListener("keyup", (e) => {
|
||||
if (e.key.toLowerCase() === "t") this.dKeyPressed = false;
|
||||
});
|
||||
}
|
||||
|
||||
private async initializeLanguage() {
|
||||
const locale = new Intl.Locale(navigator.language);
|
||||
const defaultLang = locale.language;
|
||||
const userLang = localStorage.getItem("lang") || defaultLang;
|
||||
|
||||
this.defaultTranslations = await this.loadLanguage("en");
|
||||
this.translations = await this.loadLanguage(userLang);
|
||||
this.currentLang = userLang;
|
||||
|
||||
await this.loadLanguageList();
|
||||
this.applyTranslation(this.translations);
|
||||
}
|
||||
|
||||
private async loadLanguage(lang: string): Promise<any> {
|
||||
return Promise.resolve(this.languageMap[lang] || {});
|
||||
}
|
||||
|
||||
private async loadLanguageList() {
|
||||
try {
|
||||
const data = this.languageMap;
|
||||
let list: any[] = [];
|
||||
|
||||
const browserLang = new Intl.Locale(navigator.language).language;
|
||||
|
||||
for (const langCode of Object.keys(data)) {
|
||||
const langData = data[langCode].lang;
|
||||
if (!langData) continue;
|
||||
|
||||
list.push({
|
||||
code: langData.lang_code ?? langCode,
|
||||
native: langData.native ?? langCode,
|
||||
en: langData.en ?? langCode,
|
||||
svg: langData.svg ?? langCode,
|
||||
});
|
||||
}
|
||||
|
||||
let debugLang: any = null;
|
||||
if (this.dKeyPressed) {
|
||||
debugLang = {
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
svg: "xx",
|
||||
};
|
||||
this.debugMode = true;
|
||||
}
|
||||
|
||||
const currentLangEntry = list.find((l) => l.code === this.currentLang);
|
||||
const browserLangEntry =
|
||||
browserLang !== this.currentLang && browserLang !== "en"
|
||||
? list.find((l) => l.code === browserLang)
|
||||
: undefined;
|
||||
const englishEntry =
|
||||
this.currentLang !== "en"
|
||||
? list.find((l) => l.code === "en")
|
||||
: undefined;
|
||||
|
||||
list = list.filter(
|
||||
(l) =>
|
||||
l.code !== this.currentLang &&
|
||||
l.code !== browserLang &&
|
||||
l.code !== "en" &&
|
||||
l.code !== "debug",
|
||||
);
|
||||
|
||||
list.sort((a, b) => a.en.localeCompare(b.en));
|
||||
|
||||
const finalList: any[] = [];
|
||||
if (currentLangEntry) finalList.push(currentLangEntry);
|
||||
if (englishEntry) finalList.push(englishEntry);
|
||||
if (browserLangEntry) finalList.push(browserLangEntry);
|
||||
finalList.push(...list);
|
||||
if (debugLang) finalList.push(debugLang);
|
||||
|
||||
this.languageList = finalList;
|
||||
} catch (err) {
|
||||
console.error("Failed to load language list:", err);
|
||||
}
|
||||
}
|
||||
|
||||
private async changeLanguage(lang: string) {
|
||||
localStorage.setItem("lang", lang);
|
||||
this.translations = await this.loadLanguage(lang);
|
||||
this.currentLang = lang;
|
||||
this.applyTranslation(this.translations);
|
||||
this.showModal = false;
|
||||
}
|
||||
|
||||
private applyTranslation(translations: any) {
|
||||
const components = [
|
||||
"single-player-modal",
|
||||
"host-lobby-modal",
|
||||
"join-private-lobby-modal",
|
||||
"emoji-table",
|
||||
"leader-board",
|
||||
"build-menu",
|
||||
"win-modal",
|
||||
"game-starting-modal",
|
||||
"top-bar",
|
||||
"player-panel",
|
||||
"help-modal",
|
||||
"username-input",
|
||||
"public-lobby",
|
||||
"o-modal",
|
||||
"o-button",
|
||||
];
|
||||
|
||||
document.title = translations.main?.title || document.title;
|
||||
|
||||
document.querySelectorAll("[data-i18n]").forEach((element) => {
|
||||
const key = element.getAttribute("data-i18n");
|
||||
const keys = key?.split(".") || [];
|
||||
let text = translations;
|
||||
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) break;
|
||||
}
|
||||
|
||||
if (!text && this.defaultTranslations) {
|
||||
let fallback = this.defaultTranslations;
|
||||
for (const k of keys) {
|
||||
fallback = fallback?.[k];
|
||||
if (!fallback) break;
|
||||
}
|
||||
text = fallback;
|
||||
}
|
||||
|
||||
if (text) {
|
||||
element.innerHTML = text;
|
||||
} else {
|
||||
console.warn(`Translation key not found: ${key}`);
|
||||
}
|
||||
});
|
||||
|
||||
components.forEach((tag) => {
|
||||
document.querySelectorAll(tag).forEach((el) => {
|
||||
if (typeof (el as any).requestUpdate === "function") {
|
||||
(el as any).requestUpdate();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public translateText(
|
||||
key: string,
|
||||
params: Record<string, string | number> = {},
|
||||
): string {
|
||||
const keys = key.split(".");
|
||||
let text: any = this.translations;
|
||||
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) break;
|
||||
}
|
||||
|
||||
if (!text && this.defaultTranslations) {
|
||||
text = this.defaultTranslations;
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) return key;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [param, value] of Object.entries(params)) {
|
||||
text = text.replace(`{${param}}`, String(value));
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private openModal() {
|
||||
this.debugMode = this.dKeyPressed;
|
||||
this.showModal = true;
|
||||
this.loadLanguageList();
|
||||
}
|
||||
|
||||
render() {
|
||||
const currentLang =
|
||||
this.languageList.find((l) => l.code === this.currentLang) ??
|
||||
(this.currentLang === "debug"
|
||||
? {
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
svg: "xx",
|
||||
}
|
||||
: {
|
||||
native: "English",
|
||||
en: "English",
|
||||
svg: "uk_us_flag",
|
||||
});
|
||||
|
||||
return html`
|
||||
<div class="container__row">
|
||||
<button
|
||||
id="lang-selector"
|
||||
@click=${this.openModal}
|
||||
class="text-center appearance-none w-full bg-blue-100 hover:bg-blue-200 text-blue-900 p-3 sm:p-4 lg:p-5 font-medium text-sm sm:text-base lg:text-lg rounded-md border-none cursor-pointer transition-colors duration-300 flex items-center gap-2 justify-center"
|
||||
>
|
||||
<img
|
||||
id="lang-flag"
|
||||
class="w-6 h-4"
|
||||
src="/flags/${currentLang.svg}.svg"
|
||||
alt="flag"
|
||||
/>
|
||||
<span id="lang-name">${currentLang.native} (${currentLang.en})</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<language-modal
|
||||
.visible=${this.showModal}
|
||||
.languageList=${this.languageList}
|
||||
.currentLang=${this.currentLang}
|
||||
@language-selected=${(e: CustomEvent) =>
|
||||
this.changeLanguage(e.detail.lang)}
|
||||
@close-modal=${() => (this.showModal = false)}
|
||||
></language-modal>
|
||||
`;
|
||||
}
|
||||
}
|
||||
+1
-16
@@ -16,9 +16,6 @@ import GoogleAdElement from "./GoogleAdElement";
|
||||
import { HelpModal } from "./HelpModal";
|
||||
import { HostLobbyModal as HostPrivateLobbyModal } from "./HostLobbyModal";
|
||||
import { JoinPrivateLobbyModal } from "./JoinPrivateLobbyModal";
|
||||
import "./LangSelector";
|
||||
import { LangSelector } from "./LangSelector";
|
||||
import { LanguageModal } from "./LanguageModal";
|
||||
import "./PublicLobby";
|
||||
import { PublicLobby } from "./PublicLobby";
|
||||
import { SinglePlayerModal } from "./SinglePlayerModal";
|
||||
@@ -28,6 +25,7 @@ import { UsernameInput } from "./UsernameInput";
|
||||
import { generateCryptoRandomUUID } from "./Utils";
|
||||
import "./components/baseComponents/Button";
|
||||
import "./components/baseComponents/Modal";
|
||||
import "./components/baseComponents/Select";
|
||||
import "./styles.css";
|
||||
|
||||
export interface JoinLobbyEvent {
|
||||
@@ -55,19 +53,6 @@ class Client {
|
||||
constructor() {}
|
||||
|
||||
initialize(): void {
|
||||
const langSelector = document.querySelector(
|
||||
"lang-selector",
|
||||
) as LangSelector;
|
||||
const LanguageModal = document.querySelector(
|
||||
"lang-selector",
|
||||
) as LanguageModal;
|
||||
if (!langSelector) {
|
||||
consolex.warn("Lang selector element not found");
|
||||
}
|
||||
if (!LanguageModal) {
|
||||
consolex.warn("Language modal element not found");
|
||||
}
|
||||
|
||||
this.flagInput = document.querySelector("flag-input") as FlagInput;
|
||||
if (!this.flagInput) {
|
||||
consolex.warn("Flag input element not found");
|
||||
|
||||
+293
-118
@@ -1,6 +1,24 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import bg from "../../resources/lang/bg.json";
|
||||
import bn from "../../resources/lang/bn.json";
|
||||
import de from "../../resources/lang/de.json";
|
||||
import en from "../../resources/lang/en.json";
|
||||
import eo from "../../resources/lang/eo.json";
|
||||
import es from "../../resources/lang/es.json";
|
||||
import fr from "../../resources/lang/fr.json";
|
||||
import hi from "../../resources/lang/hi.json";
|
||||
import it from "../../resources/lang/it.json";
|
||||
import ja from "../../resources/lang/ja.json";
|
||||
import nl from "../../resources/lang/nl.json";
|
||||
import pl from "../../resources/lang/pl.json";
|
||||
import pt_br from "../../resources/lang/pt_br.json";
|
||||
import ru from "../../resources/lang/ru.json";
|
||||
import sh from "../../resources/lang/sh.json";
|
||||
import tr from "../../resources/lang/tr.json";
|
||||
import uk from "../../resources/lang/uk.json";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import "./components/baseComponents/Select";
|
||||
import "./components/baseComponents/setting/SettingNumber";
|
||||
import "./components/baseComponents/setting/SettingSlider";
|
||||
import "./components/baseComponents/setting/SettingToggle";
|
||||
@@ -10,29 +28,69 @@ export class UserSettingModal extends LitElement {
|
||||
private userSettings: UserSettings = new UserSettings();
|
||||
|
||||
@state() private darkMode: boolean = this.userSettings.darkMode();
|
||||
|
||||
@state() public translations: any = {};
|
||||
@state() private defaultTranslations: any = {};
|
||||
@state() private keySequence: string[] = [];
|
||||
@state() private showEasterEggSettings = false;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("keydown", this.handleKeyDown);
|
||||
}
|
||||
|
||||
@state() private language = localStorage.getItem("lang") || "en";
|
||||
@state() private languageList: any[] = [];
|
||||
@query("o-modal") private modalEl!: HTMLElement & {
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
isModalOpen: boolean;
|
||||
};
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
private languageMap: Record<string, any> = {
|
||||
bg,
|
||||
bn,
|
||||
de,
|
||||
en,
|
||||
eo,
|
||||
es,
|
||||
fr,
|
||||
hi,
|
||||
it,
|
||||
ja,
|
||||
nl,
|
||||
pl,
|
||||
pt_br,
|
||||
ru,
|
||||
sh,
|
||||
tr,
|
||||
uk,
|
||||
};
|
||||
|
||||
private async loadLanguageList() {
|
||||
const data = this.languageMap;
|
||||
const list: any[] = [];
|
||||
|
||||
for (const langCode of Object.keys(data)) {
|
||||
const langData = data[langCode].lang;
|
||||
if (!langData) continue;
|
||||
list.push({
|
||||
code: langData.lang_code ?? langCode,
|
||||
native: langData.native ?? langCode,
|
||||
en: langData.en ?? langCode,
|
||||
svg: langData.svg ?? langCode,
|
||||
});
|
||||
}
|
||||
|
||||
list.sort((a, b) => a.en.localeCompare(b.en));
|
||||
this.languageList = list;
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener("keydown", this.handleKeyDown);
|
||||
super.disconnectedCallback();
|
||||
document.body.style.overflow = "auto";
|
||||
private async initializeLanguage() {
|
||||
const locale = new Intl.Locale(navigator.language);
|
||||
const defaultLang = locale.language;
|
||||
const userLang = localStorage.getItem("lang") || defaultLang;
|
||||
|
||||
this.defaultTranslations = await this.loadLanguage("en");
|
||||
this.translations = await this.loadLanguage(userLang);
|
||||
this.language = userLang;
|
||||
|
||||
await this.loadLanguageList();
|
||||
this.applyTranslation(this.translations);
|
||||
}
|
||||
|
||||
private handleKeyDown = (e: KeyboardEvent) => {
|
||||
@@ -49,7 +107,6 @@ export class UserSettingModal extends LitElement {
|
||||
};
|
||||
|
||||
private triggerEasterEgg() {
|
||||
console.log("🪺 Setting~ unlocked by EVAN combo!");
|
||||
this.showEasterEggSettings = true;
|
||||
const popup = document.createElement("div");
|
||||
popup.className = "easter-egg-popup";
|
||||
@@ -61,23 +118,65 @@ export class UserSettingModal extends LitElement {
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
toggleDarkMode(e: CustomEvent<{ checked: boolean }>) {
|
||||
const enabled = e.detail?.checked;
|
||||
private applyTranslation(translations: any) {
|
||||
const components = [
|
||||
"single-player-modal",
|
||||
"host-lobby-modal",
|
||||
"join-private-lobby-modal",
|
||||
"emoji-table",
|
||||
"leader-board",
|
||||
"build-menu",
|
||||
"win-modal",
|
||||
"game-starting-modal",
|
||||
"top-bar",
|
||||
"player-panel",
|
||||
"help-modal",
|
||||
"username-input",
|
||||
"public-lobby",
|
||||
"user-setting",
|
||||
"setting-slider",
|
||||
"setting-number",
|
||||
"setting-number",
|
||||
"o-modal",
|
||||
"o-button",
|
||||
"o-select",
|
||||
];
|
||||
|
||||
if (typeof enabled !== "boolean") {
|
||||
console.warn("Unexpected toggle event payload", e);
|
||||
return;
|
||||
}
|
||||
document.title = translations.main?.title || document.title;
|
||||
|
||||
this.userSettings.set("settings.darkMode", enabled);
|
||||
document.querySelectorAll("[data-i18n]").forEach((element) => {
|
||||
const key = element.getAttribute("data-i18n");
|
||||
const keys = key?.split(".") || [];
|
||||
let text = translations;
|
||||
|
||||
if (enabled) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) break;
|
||||
}
|
||||
|
||||
console.log("🌙 Dark Mode:", enabled ? "ON" : "OFF");
|
||||
if (!text && this.defaultTranslations) {
|
||||
let fallback = this.defaultTranslations;
|
||||
for (const k of keys) {
|
||||
fallback = fallback?.[k];
|
||||
if (!fallback) break;
|
||||
}
|
||||
text = fallback;
|
||||
}
|
||||
|
||||
if (text) {
|
||||
element.innerHTML = text;
|
||||
} else {
|
||||
console.warn(`Translation key not found: ${key}`);
|
||||
}
|
||||
});
|
||||
|
||||
components.forEach((tag) => {
|
||||
document.querySelectorAll(tag).forEach((el) => {
|
||||
if (typeof (el as any).requestUpdate === "function") {
|
||||
(el as any).requestUpdate();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private toggleEmojis(e: CustomEvent<{ checked: boolean }>) {
|
||||
@@ -85,8 +184,6 @@ export class UserSettingModal extends LitElement {
|
||||
if (typeof enabled !== "boolean") return;
|
||||
|
||||
this.userSettings.set("settings.emojis", enabled);
|
||||
|
||||
console.log("🤡 Emojis:", enabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
private toggleLeftClickOpensMenu(e: CustomEvent<{ checked: boolean }>) {
|
||||
@@ -94,7 +191,6 @@ export class UserSettingModal extends LitElement {
|
||||
if (typeof enabled !== "boolean") return;
|
||||
|
||||
this.userSettings.set("settings.leftClickOpensMenu", enabled);
|
||||
console.log("🖱️ Left Click Opens Menu:", enabled ? "ON" : "OFF");
|
||||
|
||||
this.requestUpdate();
|
||||
}
|
||||
@@ -119,101 +215,59 @@ export class UserSettingModal extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<o-modal title="User Settings">
|
||||
<div class="modal-overlay">
|
||||
<div class="modal-content user-setting-modal">
|
||||
<div class="settings-list">
|
||||
<setting-toggle
|
||||
label="🌙 Dark Mode"
|
||||
description="Toggle the site’s appearance between light and dark themes"
|
||||
id="dark-mode-toggle"
|
||||
.checked=${this.userSettings.darkMode()}
|
||||
@change=${(e: CustomEvent<{ checked: boolean }>) =>
|
||||
this.toggleDarkMode(e)}
|
||||
></setting-toggle>
|
||||
private async loadLanguage(lang: string): Promise<any> {
|
||||
return Promise.resolve(this.languageMap[lang] || {});
|
||||
}
|
||||
|
||||
<setting-toggle
|
||||
label="😊 Emojis"
|
||||
description="Toggle whether emojis are shown in game"
|
||||
id="emoji-toggle"
|
||||
.checked=${this.userSettings.emojis()}
|
||||
@change=${this.toggleEmojis}
|
||||
></setting-toggle>
|
||||
private async changeLanguage(lang: string) {
|
||||
localStorage.setItem("lang", lang);
|
||||
this.language = lang;
|
||||
this.translations = await this.loadLanguage(lang);
|
||||
this.applyTranslation(this.translations);
|
||||
}
|
||||
|
||||
<setting-toggle
|
||||
label="🖱️ Left Click to Open Menu"
|
||||
description="When ON, left-click opens menu and sword button attacks. When OFF, right-click attacks directly."
|
||||
id="left-click-toggle"
|
||||
.checked=${this.userSettings.leftClickOpensMenu()}
|
||||
@change=${this.toggleLeftClickOpensMenu}
|
||||
></setting-toggle>
|
||||
public toggleDarkMode(e: CustomEvent<{ checked: boolean }>) {
|
||||
const enabled = e.detail?.checked;
|
||||
|
||||
<setting-slider
|
||||
label="⚔️ Attack Ratio"
|
||||
description="What percentage of your troops to send in an attack (1–100%)"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${Number(
|
||||
localStorage.getItem("settings.attackRatio") ?? "0.2",
|
||||
) * 100}
|
||||
@change=${this.sliderAttackRatio}
|
||||
></setting-slider>
|
||||
if (typeof enabled !== "boolean") {
|
||||
console.warn("Unexpected toggle event payload", e);
|
||||
return;
|
||||
}
|
||||
|
||||
<setting-slider
|
||||
label="🪖🛠️ Troops and Workers Ratio"
|
||||
description="Adjust the balance between troops (for combat) and workers (for gold production) (1–100%)"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${Number(
|
||||
localStorage.getItem("settings.troopRatio") ?? "0.95",
|
||||
) * 100}
|
||||
@change=${this.sliderTroopRatio}
|
||||
></setting-slider>
|
||||
this.userSettings.set("settings.darkMode", enabled);
|
||||
|
||||
${this.showEasterEggSettings
|
||||
? html`
|
||||
<setting-slider
|
||||
label="Writing Speed Multiplier"
|
||||
description="Adjust how fast you pretend to code (x1–x100)"
|
||||
min="0"
|
||||
max="100"
|
||||
value="40"
|
||||
easter="true"
|
||||
@change=${(e: CustomEvent) => {
|
||||
const value = e.detail?.value;
|
||||
if (typeof value !== "undefined") {
|
||||
console.log("Changed:", value);
|
||||
} else {
|
||||
console.warn("Slider event missing detail.value", e);
|
||||
}
|
||||
}}
|
||||
></setting-slider>
|
||||
if (enabled) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
}
|
||||
|
||||
<setting-number
|
||||
label="Bug Count"
|
||||
description="How many bugs you're okay with (0–1000, emotionally)"
|
||||
value="100"
|
||||
min="0"
|
||||
max="1000"
|
||||
easter="true"
|
||||
@change=${(e: CustomEvent) => {
|
||||
const value = e.detail?.value;
|
||||
if (typeof value !== "undefined") {
|
||||
console.log("Changed:", value);
|
||||
} else {
|
||||
console.warn("Slider event missing detail.value", e);
|
||||
}
|
||||
}}
|
||||
></setting-number>
|
||||
`
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</o-modal>
|
||||
`;
|
||||
public translateText(
|
||||
key: string,
|
||||
params: Record<string, string | number> = {},
|
||||
): string {
|
||||
const keys = key.split(".");
|
||||
let text: any = this.translations;
|
||||
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) break;
|
||||
}
|
||||
|
||||
if (!text && this.defaultTranslations) {
|
||||
text = this.defaultTranslations;
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) return key;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [param, value] of Object.entries(params)) {
|
||||
text = text.replace(`{${param}}`, String(value));
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
public open() {
|
||||
@@ -223,4 +277,125 @@ export class UserSettingModal extends LitElement {
|
||||
public close() {
|
||||
this.modalEl?.close();
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.loadLanguageList();
|
||||
this.initializeLanguage();
|
||||
window.addEventListener("keydown", this.handleKeyDown);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
window.removeEventListener("keydown", this.handleKeyDown);
|
||||
super.disconnectedCallback();
|
||||
document.body.style.overflow = "auto";
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<o-modal title="User Settings">
|
||||
<div class="container">
|
||||
<o-select
|
||||
id="lang-selector"
|
||||
label="Language"
|
||||
translationKey="select_lang.title"
|
||||
.items=${this.languageList.map((l) => ({
|
||||
label: `${l.native} (${l.en})`,
|
||||
value: l.code,
|
||||
image: `/flags/${l.svg}.svg`,
|
||||
}))}
|
||||
.selectedValue=${this.language}
|
||||
.showImageWithLabel=${true}
|
||||
@o-select-change=${(e: CustomEvent) =>
|
||||
this.changeLanguage(e.detail)}
|
||||
></o-select>
|
||||
|
||||
<setting-toggle
|
||||
label="🌙 Dark Mode"
|
||||
description="Toggle the site’s appearance between light and dark themes"
|
||||
id="dark-mode-toggle"
|
||||
.checked=${this.userSettings.darkMode()}
|
||||
@change=${(e: CustomEvent<{ checked: boolean }>) =>
|
||||
this.toggleDarkMode(e)}
|
||||
></setting-toggle>
|
||||
|
||||
<setting-toggle
|
||||
label="😊 Emojis"
|
||||
description="Toggle whether emojis are shown in game"
|
||||
id="emoji-toggle"
|
||||
.checked=${this.userSettings.emojis()}
|
||||
@change=${this.toggleEmojis}
|
||||
></setting-toggle>
|
||||
|
||||
<setting-toggle
|
||||
label="🖱️ Left Click to Open Menu"
|
||||
description="When ON, left-click opens menu and sword button attacks. When OFF, right-click attacks directly."
|
||||
id="left-click-toggle"
|
||||
.checked=${this.userSettings.leftClickOpensMenu()}
|
||||
@change=${this.toggleLeftClickOpensMenu}
|
||||
></setting-toggle>
|
||||
|
||||
<setting-slider
|
||||
label="⚔️ Attack Ratio"
|
||||
description="What percentage of your troops to send in an attack (1–100%)"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${Number(
|
||||
localStorage.getItem("settings.attackRatio") ?? "0.2",
|
||||
) * 100}
|
||||
@change=${this.sliderAttackRatio}
|
||||
></setting-slider>
|
||||
|
||||
<setting-slider
|
||||
label="🪖🛠️ Troops and Workers Ratio"
|
||||
description="Adjust the balance between troops (for combat) and workers (for gold production) (1–100%)"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${Number(
|
||||
localStorage.getItem("settings.troopRatio") ?? "0.95",
|
||||
) * 100}
|
||||
@change=${this.sliderTroopRatio}
|
||||
></setting-slider>
|
||||
|
||||
${this.showEasterEggSettings
|
||||
? html`
|
||||
<setting-slider
|
||||
label="Writing Speed Multiplier"
|
||||
description="Adjust how fast you pretend to code (x1–x100)"
|
||||
min="0"
|
||||
max="100"
|
||||
value="40"
|
||||
easter="true"
|
||||
@change=${(e: CustomEvent) => {
|
||||
const value = e.detail?.value;
|
||||
if (typeof value === "undefined") {
|
||||
console.warn("Slider event missing detail.value", e);
|
||||
}
|
||||
}}
|
||||
></setting-slider>
|
||||
|
||||
<setting-number
|
||||
label="Bug Count"
|
||||
description="How many bugs you're okay with (0–1000, emotionally)"
|
||||
value="100"
|
||||
min="0"
|
||||
max="1000"
|
||||
easter="true"
|
||||
@change=${(e: CustomEvent) => {
|
||||
const value = e.detail?.value;
|
||||
if (typeof value === "undefined") {
|
||||
console.warn("Slider event missing detail.value", e);
|
||||
}
|
||||
}}
|
||||
></setting-number>
|
||||
`
|
||||
: null}
|
||||
</div>
|
||||
</o-modal>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
import { LangSelector } from "./LangSelector";
|
||||
import { UserSettingModal } from "./UserSettingModal";
|
||||
|
||||
export function renderTroops(troops: number): string {
|
||||
return renderNumber(troops / 10);
|
||||
@@ -78,7 +78,9 @@ export const translateText = (
|
||||
key: string,
|
||||
params: Record<string, string | number> = {},
|
||||
): string => {
|
||||
const langSelector = document.querySelector("lang-selector") as LangSelector;
|
||||
const langSelector = document.querySelector(
|
||||
"user-setting",
|
||||
) as UserSettingModal;
|
||||
if (!langSelector) {
|
||||
console.warn("LangSelector not found in DOM");
|
||||
return key;
|
||||
|
||||
@@ -39,14 +39,21 @@ export class OModal extends LitElement {
|
||||
background: #000000a1;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
padding: 1rem 2.4rem 1rem 1.4rem;
|
||||
padding: 0.6rem 2.4rem 0.6rem 1.4rem;
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
padding: 1rem 2.4rem 1rem 1.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.c-modal__close {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
top: 1rem;
|
||||
top: 0.6rem;
|
||||
@media (min-width: 1024px) {
|
||||
top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.c-modal__content {
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { translateText } from "../../Utils";
|
||||
|
||||
interface selectItems {
|
||||
label: string;
|
||||
value: any;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
@customElement("o-select")
|
||||
export class OSelect extends LitElement {
|
||||
/**
|
||||
* Array of selectable items.
|
||||
*/
|
||||
@property({ type: Array }) items: selectItems[] = [];
|
||||
|
||||
/**
|
||||
* Currently selected value.
|
||||
*/
|
||||
@property({ type: String }) selectedValue: string = "";
|
||||
|
||||
/**
|
||||
* Error message to display.
|
||||
*/
|
||||
@property({ type: String }) errorMessage: string = "";
|
||||
|
||||
/**
|
||||
* Enables search filtering in the dropdown.
|
||||
*/
|
||||
@property({ type: Boolean }) filterEnabled: boolean = false;
|
||||
|
||||
/**
|
||||
* If true, show image next to label.
|
||||
*/
|
||||
@property({ type: Boolean }) showImageWithLabel: boolean = false;
|
||||
|
||||
/**
|
||||
* Label shown above the select.
|
||||
*/
|
||||
@property({ type: String }) label: string = "";
|
||||
@property({ type: String }) translationKey = "";
|
||||
|
||||
@state() private selectedItem: selectItems | null = null;
|
||||
@state() private filter: string = "";
|
||||
@state() private isOpen: boolean = false;
|
||||
|
||||
static styles = css`
|
||||
.c-label {
|
||||
color: var(--fontColorLight);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.c-select {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
padding: 0 10px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--borderRadius--md);
|
||||
background: #1e1e1e;
|
||||
height: 50px;
|
||||
|
||||
&.is-error {
|
||||
border-color: var(--errorColor);
|
||||
|
||||
+ .c-message {
|
||||
color: var(--errorColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.c-select__display {
|
||||
align-items: center;
|
||||
color: var(--fontColorLight);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.c-select__listWrapper {
|
||||
border: 1px solid var(--secondaryBorderColor);
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
right: -1px;
|
||||
background: var(--boxBackgroundColor);
|
||||
backdrop-filter: blur(var(--blur-md));
|
||||
top: 50px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.c-select__list {
|
||||
list-style: none;
|
||||
overflow: scroll;
|
||||
max-height: 35dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.c-select__item {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
min-height: 35px;
|
||||
color: var(--fontColorLight);
|
||||
gap: 10px;
|
||||
|
||||
img {
|
||||
max-width: 35px;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.c-select__input {
|
||||
outline: none;
|
||||
padding: 4px 8px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
`;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("click", this._handleOutsideClick);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener("click", this._handleOutsideClick);
|
||||
}
|
||||
|
||||
willUpdate(changedProps: Map<string | number | symbol, unknown>) {
|
||||
if (changedProps.has("items") || changedProps.has("selectedValue")) {
|
||||
const match = this.items.find(
|
||||
(item) => item.value === this.selectedValue,
|
||||
);
|
||||
if (match) {
|
||||
this.selectedItem = match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _handleOutsideClick = (event: MouseEvent) => {
|
||||
if (!this.contains(event.target as Node)) {
|
||||
this.isOpen = false;
|
||||
}
|
||||
};
|
||||
|
||||
private selectItem(item: selectItems) {
|
||||
this.selectedItem = item;
|
||||
this.filter = "";
|
||||
this.isOpen = false;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("o-select-change", {
|
||||
detail: item.value,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private renderSelectedDisplay() {
|
||||
if (!this.selectedItem) {
|
||||
return html`<span>Select</span>`;
|
||||
}
|
||||
|
||||
const { image, label } = this.selectedItem;
|
||||
|
||||
if (this.showImageWithLabel) {
|
||||
return html`
|
||||
${image ? html`<img src="${image}" alt="${label} flag" />` : null}
|
||||
<span>${label}</span>
|
||||
`;
|
||||
}
|
||||
|
||||
return image
|
||||
? html`<img src="${image}" alt="${label} flag" />`
|
||||
: html`<span>${label}</span>`;
|
||||
}
|
||||
|
||||
get filteredItems() {
|
||||
return this.items.filter((item) =>
|
||||
item.label.toLowerCase().includes(this.filter.toLowerCase()),
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div
|
||||
class="c-select ${this.errorMessage ? "is-error" : ""}"
|
||||
@click=${() => (this.isOpen = !this.isOpen)}
|
||||
>
|
||||
${this.label
|
||||
? html`<label class="c-label">
|
||||
${`${this.translationKey}` === ""
|
||||
? `${this.label}`
|
||||
: `${translateText(this.translationKey)}`}</label
|
||||
>`
|
||||
: null}
|
||||
<div class="c-select__display">${this.renderSelectedDisplay()}</div>
|
||||
|
||||
${this.isOpen
|
||||
? html`
|
||||
<div class="c-select__listWrapper">
|
||||
${this.filterEnabled
|
||||
? html`
|
||||
<input
|
||||
class="c-select__input"
|
||||
type="text"
|
||||
placeholder="Search..."
|
||||
.value=${this.filter}
|
||||
@input=${(e: InputEvent) => {
|
||||
this.filter = (e.target as HTMLInputElement).value;
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}}
|
||||
@click=${(e: Event) => e.stopPropagation()}
|
||||
/>
|
||||
`
|
||||
: null}
|
||||
<ul class="c-select__list">
|
||||
${this.filteredItems.map(
|
||||
(item) => html`
|
||||
<li
|
||||
class="c-select__item"
|
||||
@click=${(e: Event) => {
|
||||
this.selectItem(item);
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
}}
|
||||
>
|
||||
${item.image
|
||||
? html`<img
|
||||
src="${item.image}"
|
||||
alt="${item.label}"
|
||||
/>`
|
||||
: ""}
|
||||
<div>${item.label}</div>
|
||||
</li>
|
||||
`,
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
${this.errorMessage
|
||||
? html` <div class="c-message">${this.errorMessage}</div>`
|
||||
: ""}
|
||||
`;
|
||||
}
|
||||
}
|
||||
+7
-36
@@ -263,26 +263,15 @@
|
||||
block
|
||||
secondary
|
||||
></o-button>
|
||||
<div class="container__row">
|
||||
<lang-selector class="w-full"></lang-selector>
|
||||
</div>
|
||||
<o-button
|
||||
id="settings-button"
|
||||
title="Settings"
|
||||
translationKey="main.settings"
|
||||
block
|
||||
secondary
|
||||
></o-button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- User Setting -->
|
||||
<button
|
||||
id="settings-button"
|
||||
title="Settings"
|
||||
class="fixed bottom-4 right-4 z-50 rounded-full p-2 shadow-lg transition-colors duration-300 flex items-center justify-center"
|
||||
style="width: 80px; height: 80px; background-color: #0075ff"
|
||||
>
|
||||
<img
|
||||
src="../../resources/images/SettingIconWhite.svg"
|
||||
alt="Settings"
|
||||
style="width: 72px; height: 72px"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<!-- Game components -->
|
||||
<div id="customMenu" class="mt-4 sm:mt-6 lg:mt-8">
|
||||
<ul></ul>
|
||||
@@ -359,24 +348,6 @@
|
||||
<dark-mode-button></dark-mode-button>
|
||||
<user-setting></user-setting>
|
||||
<multi-tab-modal></multi-tab-modal>
|
||||
<div
|
||||
id="language-modal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex justify-center items-center"
|
||||
>
|
||||
<div class="bg-white rounded-lg shadow-lg p-6 w-96 max-w-full">
|
||||
<h2 class="text-xl font-semibold mb-4">Select Language</h2>
|
||||
<div
|
||||
id="language-list"
|
||||
class="space-y-2 max-h-80 overflow-y-auto"
|
||||
></div>
|
||||
<button
|
||||
class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded"
|
||||
onclick="document.getElementById('language-modal').classList.add('hidden')"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<script>
|
||||
// Remove preload class after everything is loaded
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
@import url("./styles/components/modal.css");
|
||||
@import url("./styles/components/setting.css");
|
||||
@import url("./styles/components/controls.css");
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
@@ -228,6 +229,13 @@ label.option-card:hover {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
o-select,
|
||||
setting-toggle,
|
||||
setting-slider,
|
||||
setting-number {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.player-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
border: 1px solid #333;
|
||||
border-radius: 10px;
|
||||
padding: 12px 20px;
|
||||
width: 360px !important;
|
||||
max-width: 360px !important;
|
||||
min-width: 360px !important;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
|
||||
transition: background 0.3s ease;
|
||||
gap: 12px;
|
||||
|
||||
@@ -3,17 +3,38 @@
|
||||
--breakPoint-tablet: 800px;
|
||||
--breakPoint-Desktop: 1024px;
|
||||
--blur-md: 4px;
|
||||
--boxBackgroundColor: #111827cc;
|
||||
--fontColor: #202020;
|
||||
--borderRadius--md: 0px;
|
||||
--borderRadius--lg: 2px;
|
||||
--inputBackgroundColor: #fefefe;
|
||||
--boxBackgroundColor: #2434574f;
|
||||
--secondaryBoxBackgroundColor: #3a4d766e;
|
||||
--boxShadow: inset 0px 0px 4px 2px rgba(0, 175, 255, 0.2);
|
||||
--boxShadowBorder: inset 0px 0px 4px 2px rgba(0, 175, 255, 0.2);
|
||||
--fontColor: #1e293b;
|
||||
--fontColorLight: #fff;
|
||||
--primaryColor: #2563eb;
|
||||
--primaryColorHover: #1d4ed8;
|
||||
--primaryColorHover: color-mix(in srgb, var(--primaryColor) 80%, transparent);
|
||||
--primaryColorDisabled: linear-gradient(
|
||||
to right,
|
||||
rgb(74, 74, 74),
|
||||
rgb(61, 61, 61)
|
||||
);
|
||||
--secondaryColor: #dbeafe;
|
||||
--secondaryColorHover: #bfdbfe;
|
||||
--secondaryColorHover: color-mix(
|
||||
in srgb,
|
||||
var(--secondaryColor) 80%,
|
||||
transparent
|
||||
);
|
||||
--errorColor: #f0572d;
|
||||
--errorColorTransparent: #ff005529;
|
||||
--successColor: #17eb14;
|
||||
--successColorBackground: #00b37a;
|
||||
--borderColor: #0000004d;
|
||||
--secondaryBorderColor: #333;
|
||||
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--space-xs: 6px;
|
||||
--space-sm: 8px;
|
||||
--space-md: 10px;
|
||||
--space-lg: 12px;
|
||||
--space-xl: 14px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user