mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-30 09:09:40 +00:00
Create base components button, modal .. (#331)
Create base components with shared styles, as start of make ui better. For now shoul look same but underhood new copoments are used. This should be first PR that handle this and many more comes. I am in rush due conflict with other ppl, but should work as i tested. Testing again and look at structure Main goal i have global css not scope in component die loading times and size of elements. (Modal due nature of lit and shadow dom is exception, maybe later find better way). Documenting the components will happen later as base components establish their usage.
This commit is contained in:
@@ -1,311 +1,93 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { consolex } from "../core/Consolex";
|
||||
import { GameMapType, GameType } from "../core/game/Game";
|
||||
import { GameInfo, GameRecord } from "../core/Schemas";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import "./components/baseComponents/Modal";
|
||||
import "./components/baseComponents/Button";
|
||||
|
||||
@customElement("join-private-lobby-modal")
|
||||
export class JoinPrivateLobbyModal extends LitElement {
|
||||
@state() private isModalOpen = false;
|
||||
@state() private message: string = "";
|
||||
@query("o-modal") private modalEl!: HTMLElement & {
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
};
|
||||
@query("#lobbyIdInput") private lobbyIdInput!: HTMLInputElement;
|
||||
@state() private message: string = "";
|
||||
@state() private hasJoined = false;
|
||||
@state() private players: string[] = [];
|
||||
|
||||
private playersInterval = null;
|
||||
|
||||
static styles = css`
|
||||
.modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.modal-content {
|
||||
background-color: rgb(35 35 35 / 0.8);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
backdrop-filter: blur(12px);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
width: 80%;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(8px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Add custom scrollbar styles */
|
||||
.modal-content::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.modal-content::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.modal-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.modal-content::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 0 0 20px;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: sticky;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
color: #aaa;
|
||||
float: right;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.start-game-button {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
padding: 15px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.3s;
|
||||
display: inline-block;
|
||||
margin: 0 0 20px 0;
|
||||
}
|
||||
|
||||
.start-game-button:not(:disabled):hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.start-game-button:disabled {
|
||||
background: linear-gradient(to right, #4a4a4a, #3d3d3d);
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* JoinPrivateLobbyModal css */
|
||||
|
||||
.message-area {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
transition: opacity 0.3s ease;
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.message-area.show {
|
||||
opacity: 1;
|
||||
height: auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.message-area.error {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
.message-area.success {
|
||||
background-color: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.lobby-id-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin: 40px 0px 0px 0px;
|
||||
}
|
||||
.lobby-id-box input {
|
||||
flex-grow: 1;
|
||||
max-width: 200px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.lobby-id-paste-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
padding: 10px 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.lobby-id-paste-button:hover {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.lobby-id-paste-button-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.options-section {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
padding: 12px 24px 24px 24px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.option-title {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.players-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.player-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 4px 16px;
|
||||
border-radius: 16px;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.modal-content {
|
||||
max-height: calc(90vh - 42px);
|
||||
max-width: 100vw;
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div
|
||||
class="modal-overlay"
|
||||
style="display: ${this.isModalOpen ? "flex" : "none"}"
|
||||
>
|
||||
<div
|
||||
style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"
|
||||
class="${this.isModalOpen ? "" : "hidden"}"
|
||||
@click=${this.close}
|
||||
></div>
|
||||
<div class="modal-content">
|
||||
<span class="close" @click=${this.closeAndLeave}>×</span>
|
||||
<div class="title">Join Private Lobby</div>
|
||||
<div class="lobby-id-box">
|
||||
<input
|
||||
type="text"
|
||||
id="lobbyIdInput"
|
||||
placeholder="Enter Lobby ID"
|
||||
@keyup=${this.handleChange}
|
||||
/>
|
||||
<button
|
||||
@click=${this.pasteFromClipboard}
|
||||
class="lobby-id-paste-button"
|
||||
<o-modal title="Join Private Lobby">
|
||||
<div class="lobby-id-box">
|
||||
<input
|
||||
type="text"
|
||||
id="lobbyIdInput"
|
||||
placeholder="Enter Lobby ID"
|
||||
@keyup=${this.handleChange}
|
||||
/>
|
||||
<button
|
||||
@click=${this.pasteFromClipboard}
|
||||
class="lobby-id-paste-button"
|
||||
>
|
||||
<svg
|
||||
class="lobby-id-paste-button-icon"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
stroke-width="0"
|
||||
viewBox="0 0 32 32"
|
||||
height="18px"
|
||||
width="18px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
class="lobby-id-paste-button-icon"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
stroke-width="0"
|
||||
viewBox="0 0 32 32"
|
||||
height="18px"
|
||||
width="18px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M 15 3 C 13.742188 3 12.847656 3.890625 12.40625 5 L 5 5 L 5 28 L 13 28 L 13 30 L 27 30 L 27 14 L 25 14 L 25 5 L 17.59375 5 C 17.152344 3.890625 16.257813 3 15 3 Z M 15 5 C 15.554688 5 16 5.445313 16 6 L 16 7 L 19 7 L 19 9 L 11 9 L 11 7 L 14 7 L 14 6 C 14 5.445313 14.445313 5 15 5 Z M 7 7 L 9 7 L 9 11 L 21 11 L 21 7 L 23 7 L 23 14 L 13 14 L 13 26 L 7 26 Z M 15 16 L 25 16 L 25 28 L 15 28 Z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="message-area ${this.message ? "show" : ""}">
|
||||
${this.message}
|
||||
</div>
|
||||
<div class="options-layout">
|
||||
<!-- Lobby Selection -->
|
||||
${this.hasJoined && this.players.length > 0
|
||||
? html`<div class="options-section">
|
||||
<div class="option-title">
|
||||
${this.players.length}
|
||||
${this.players.length === 1 ? "Player" : "Players"}
|
||||
</div>
|
||||
<path
|
||||
d="M 15 3 C 13.742188 3 12.847656 3.890625 12.40625 5 L 5 5 L 5 28 L 13 28 L 13 30 L 27 30 L 27 14 L 25 14 L 25 5 L 17.59375 5 C 17.152344 3.890625 16.257813 3 15 3 Z M 15 5 C 15.554688 5 16 5.445313 16 6 L 16 7 L 19 7 L 19 9 L 11 9 L 11 7 L 14 7 L 14 6 C 14 5.445313 14.445313 5 15 5 Z M 7 7 L 9 7 L 9 11 L 21 11 L 21 7 L 23 7 L 23 14 L 13 14 L 13 26 L 7 26 Z M 15 16 L 25 16 L 25 28 L 15 28 Z"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="message-area ${this.message ? "show" : ""}">
|
||||
${this.message}
|
||||
</div>
|
||||
<div class="options-layout">
|
||||
${this.hasJoined && this.players.length > 0
|
||||
? html` <div class="options-section">
|
||||
<div class="option-title">
|
||||
${this.players.length}
|
||||
${this.players.length === 1 ? "Player" : "Players"}
|
||||
</div>
|
||||
|
||||
<div class="players-list">
|
||||
${this.players.map(
|
||||
(player) =>
|
||||
html`<span class="player-tag">${player}</span>`,
|
||||
)}
|
||||
</div>
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
${!this.hasJoined
|
||||
? html`<button class="start-game-button" @click=${this.joinLobby}>
|
||||
Join Lobby
|
||||
</button>`
|
||||
<div class="players-list">
|
||||
${this.players.map(
|
||||
(player) => html`<span class="player-tag">${player}</span>`,
|
||||
)}
|
||||
</div>
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
${!this.hasJoined
|
||||
? html` <o-button
|
||||
title="Join Lobby"
|
||||
block
|
||||
@click=${this.joinLobby}
|
||||
></o-button>`
|
||||
: ""}
|
||||
</div>
|
||||
</o-modal>
|
||||
`;
|
||||
}
|
||||
|
||||
public open(id: string = "") {
|
||||
this.isModalOpen = true;
|
||||
createRenderRoot() {
|
||||
return this; // light DOM
|
||||
}
|
||||
|
||||
public open(id: string = "") {
|
||||
this.modalEl?.open();
|
||||
if (id) {
|
||||
this.setLobbyId(id);
|
||||
this.joinLobby();
|
||||
@@ -313,8 +95,8 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.isModalOpen = false;
|
||||
this.lobbyIdInput.value = null;
|
||||
this.modalEl?.close();
|
||||
if (this.playersInterval) {
|
||||
clearInterval(this.playersInterval);
|
||||
this.playersInterval = null;
|
||||
|
||||
Reference in New Issue
Block a user