mirror of
https://github.com/101island/lolisland.us.git
synced 2026-03-01 03:49:42 +08:00
feat: add login window
This commit is contained in:
1
src/assets/icons/qq.svg
Normal file
1
src/assets/icons/qq.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="64" height="64"><path d="M824.8 613.2c-16-51.4-34.4-94.6-62.7-165.3C766.5 262.2 689.3 112 511.5 112 331.7 112 256.2 265.2 261 447.9c-28.4 70.8-46.7 113.7-62.7 165.3-34 109.5-23 154.8-14.6 155.8 18 2.2 70.1-82.4 70.1-82.4 0 49 25.2 112.9 79.8 159-26.4 8.1-85.7 29.9-71.6 53.8 11.4 19.3 196.2 12.3 249.5 6.3 53.3 6 238.1 13 249.5-6.3 14.1-23.8-45.3-45.7-71.6-53.8 54.6-46.2 79.8-110.1 79.8-159 0 0 52.1 84.6 70.1 82.4 8.5-1.1 19.5-46.4-14.5-155.8z"></path></svg>
|
||||
|
After Width: | Height: | Size: 545 B |
194
src/components/LoginWindow.astro
Normal file
194
src/components/LoginWindow.astro
Normal file
@@ -0,0 +1,194 @@
|
||||
---
|
||||
import qqIcon from "../assets/icons/qq.svg?raw";
|
||||
---
|
||||
|
||||
<div class="login-window">
|
||||
<button class="close-btn" aria-label="Close Login">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="login-form">
|
||||
<h2>Login</h2>
|
||||
<input type="text" placeholder="Username" class="input-field" />
|
||||
<input type="password" placeholder="Password" class="input-field" />
|
||||
|
||||
<button class="qq-btn" aria-label="Login with QQ" set:html={qqIcon} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const loginWindow = document.querySelector(".login-window") as HTMLElement;
|
||||
const closeBtn = document.querySelector(".close-btn");
|
||||
|
||||
/* Input Validation */
|
||||
const inputs = document.querySelectorAll(
|
||||
".input-field",
|
||||
) as NodeListOf<HTMLInputElement>;
|
||||
inputs.forEach((input) => {
|
||||
input.addEventListener("input", () => {
|
||||
// Allow only a-z, A-Z, 0-9, -, _, @, .
|
||||
input.value = input.value.replace(/[^a-zA-Z0-9\-_@\.]/g, "");
|
||||
});
|
||||
});
|
||||
|
||||
if (loginWindow && closeBtn) {
|
||||
closeBtn.addEventListener("click", () => {
|
||||
window.dispatchEvent(new CustomEvent("close-login"));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.login-window {
|
||||
padding: 2rem 2.4rem;
|
||||
border-radius: 22px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
background: linear-gradient(
|
||||
145deg,
|
||||
rgba(255, 255, 255, 0.12),
|
||||
rgba(255, 255, 255, 0.04)
|
||||
);
|
||||
backdrop-filter: blur(6px);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.08),
|
||||
0 10px 40px rgba(0, 0, 0, 0.5);
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 300px;
|
||||
max-width: 90vw;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
pointer-events: none; /* Initially hidden interaction */
|
||||
z-index: 10;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Shared with Title Card for consistency if needed, but defining locally */
|
||||
.login-window::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 22px;
|
||||
background: linear-gradient(
|
||||
120deg,
|
||||
rgba(124, 251, 255, 0.06),
|
||||
rgba(255, 127, 209, 0.05),
|
||||
rgba(255, 209, 102, 0.05)
|
||||
);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
transition:
|
||||
background 0.2s,
|
||||
color 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 1.5rem 0;
|
||||
font-size: 1.8rem;
|
||||
color: var(--text);
|
||||
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
outline: none;
|
||||
transition:
|
||||
border-color 0.2s,
|
||||
background 0.2s;
|
||||
font-family:
|
||||
monospace, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
"Liberation Mono", "Courier New";
|
||||
}
|
||||
|
||||
.input-field:focus {
|
||||
border-color: rgba(124, 251, 255, 0.5);
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.input-field::placeholder {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.qq-btn {
|
||||
margin-top: 0.5rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
background: linear-gradient(
|
||||
145deg,
|
||||
rgba(255, 255, 255, 0.12),
|
||||
rgba(255, 255, 255, 0.04)
|
||||
);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
background 0.2s;
|
||||
}
|
||||
|
||||
.qq-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
background: linear-gradient(
|
||||
145deg,
|
||||
rgba(255, 255, 255, 0.16),
|
||||
rgba(255, 255, 255, 0.08)
|
||||
);
|
||||
}
|
||||
|
||||
.qq-btn :global(svg) {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: currentColor;
|
||||
}
|
||||
</style>
|
||||
@@ -3,6 +3,7 @@ import titleSvg from "../assets/title.svg?raw";
|
||||
import Footer from "./Footer.astro";
|
||||
import Navbar from "./Navbar.astro";
|
||||
import Particles from "./Particles.astro";
|
||||
import LoginWindow from "./LoginWindow.astro";
|
||||
---
|
||||
|
||||
<Navbar />
|
||||
@@ -18,6 +19,7 @@ import Particles from "./Particles.astro";
|
||||
<div class="title-card">
|
||||
<div class="title-svg-container" set:html={titleSvg} />
|
||||
</div>
|
||||
<LoginWindow />
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -227,6 +229,29 @@ import Particles from "./Particles.astro";
|
||||
}, 500) as unknown as number;
|
||||
}
|
||||
}) as EventListener);
|
||||
|
||||
// Login Window Logic
|
||||
const loginWindowEl = document.querySelector(
|
||||
".login-window",
|
||||
) as HTMLElement;
|
||||
|
||||
window.addEventListener("open-login", () => {
|
||||
if (titleCard) {
|
||||
titleCard.style.opacity = "0";
|
||||
titleCard.style.pointerEvents = "none";
|
||||
}
|
||||
if (loginWindowEl) {
|
||||
loginWindowEl.style.opacity = "1";
|
||||
loginWindowEl.style.pointerEvents = "auto";
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("close-login", () => {
|
||||
if (loginWindowEl) {
|
||||
loginWindowEl.style.opacity = "0";
|
||||
loginWindowEl.style.pointerEvents = "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -289,6 +314,10 @@ import Particles from "./Particles.astro";
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.content > :global(*) {
|
||||
grid-area: 1 / 1;
|
||||
}
|
||||
|
||||
.title-card {
|
||||
padding: 1.8rem 2.4rem;
|
||||
border-radius: 22px;
|
||||
|
||||
@@ -9,7 +9,7 @@ import titleImg from "../assets/title.svg";
|
||||
class="nav-title"
|
||||
height="32"
|
||||
style="height: 32px; width: auto;"
|
||||
>
|
||||
/>
|
||||
<a
|
||||
href="https://github.com/101island"
|
||||
target="_blank"
|
||||
@@ -30,6 +30,24 @@ import titleImg from "../assets/title.svg";
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<button class="user-login-btn" aria-label="Login">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="28"
|
||||
height="28"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="user-icon"
|
||||
>
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
@@ -64,6 +82,8 @@ import titleImg from "../assets/title.svg";
|
||||
transform 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: auto; /* Push to right */
|
||||
margin-right: 1.5rem; /* Gap between github and user */
|
||||
}
|
||||
|
||||
.github-link:hover {
|
||||
@@ -71,6 +91,25 @@ import titleImg from "../assets/title.svg";
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.user-login-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition:
|
||||
opacity 0.2s,
|
||||
transform 0.2s;
|
||||
}
|
||||
|
||||
.user-login-btn:hover {
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.octicon {
|
||||
fill: currentColor;
|
||||
}
|
||||
@@ -91,4 +130,11 @@ import titleImg from "../assets/title.svg";
|
||||
navTitle.addEventListener("load", reveal);
|
||||
}
|
||||
}
|
||||
|
||||
const loginBtn = document.querySelector(".user-login-btn");
|
||||
if (loginBtn) {
|
||||
loginBtn.addEventListener("click", () => {
|
||||
window.dispatchEvent(new CustomEvent("open-login"));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -210,6 +210,41 @@
|
||||
menu.classList.remove("active");
|
||||
}
|
||||
});
|
||||
|
||||
// Login Window Logic Integration
|
||||
let titlePrevState = true;
|
||||
|
||||
window.addEventListener("open-login", () => {
|
||||
// When login opens:
|
||||
// 1. Save current title toggle state
|
||||
// 2. Force title toggle to OFF (unchecked)
|
||||
// 3. Disable title toggle (and UI)
|
||||
if (toggles.title) {
|
||||
titlePrevState = toggles.title.checked;
|
||||
toggles.title.checked = false;
|
||||
toggles.title.disabled = true;
|
||||
|
||||
const parent = toggles.title.parentElement;
|
||||
if (parent) parent.classList.add("disabled");
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("close-login", () => {
|
||||
// When login closes:
|
||||
// 1. Enable title toggle
|
||||
// 2. Restore previous state
|
||||
// 3. Emit toggle-title event to let MainView know what to do
|
||||
if (toggles.title) {
|
||||
toggles.title.disabled = false;
|
||||
const parent = toggles.title.parentElement;
|
||||
if (parent) parent.classList.remove("disabled");
|
||||
|
||||
toggles.title.checked = titlePrevState;
|
||||
|
||||
// Important: Tell MainView to restore the title card if it was previously enabled
|
||||
emit("toggle-title", titlePrevState);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user