- LyricSearch: misc/field focus, validation

- Nav: further improvements
This commit is contained in:
2025-11-26 09:17:30 -05:00
parent ee25ad243c
commit eb38f8865f
5 changed files with 80 additions and 12 deletions

BIN
public/images/zim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -195,6 +195,11 @@ blockquote p:first-of-type::after {
Custom
*/
.logo-auth {
height: 64px;
width: 64px;
}
.footer {
display: grid;
align-items: end;

View File

@@ -101,8 +101,8 @@ export default function LoginPage() {
<div className="flex items-start justify-center bg-gray-50 dark:bg-[#121212] px-4 pt-20 py-10">
<div className="max-w-md w-full bg-white dark:bg-[#1E1E1E] rounded-2xl shadow-xl px-10 pb-6">
<h2 className="flex flex-col items-center text-3xl font-semibold text-gray-900 dark:text-white mb-8 font-sans">
<img className="logo-auth mb-4" src="/images/kode.png" alt="Logo" />
Authentication Required
<img className="logo-auth mb-4" src="/images/zim.png" alt="Logo" />
Log In
</h2>
<form className="space-y-6 relative" onSubmit={handleSubmit} noValidate>

View File

@@ -324,6 +324,16 @@ export function LyricSearchInputField({ id, placeholder, setShowLyrics }) {
const statusTitle = statusLabels[inputStatus];
const StatusIcon = statusIcons[inputStatus] || RemoveRoundedIcon;
useEffect(() => {
const inputEl = autoCompleteInputRef.current;
if (!inputEl) return;
if (statusTitle) {
inputEl.setAttribute("title", statusTitle);
} else {
inputEl.removeAttribute("title");
}
}, [statusTitle]);
return (
<div>
<div className="lyric-search-input-wrapper">

View File

@@ -2,7 +2,7 @@
import { metaData, API_URL } from "../config";
import { Icon } from "astro-icon/components";
const isLoggedIn = Astro.cookies.get('access_token') || Astro.cookies.get('refresh_token');
const isLoggedIn = Boolean(Astro.cookies.get('access_token') || Astro.cookies.get('refresh_token'));
const padlockIconSvg = `
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.2" viewBox="0 0 24 24">
@@ -21,14 +21,27 @@ const externalLinkIconSvg = `
const navItems = [
{ label: "Home", href: "/" },
{ label: "Radio", href: "/radio" },
{ label: "Memes", href: "/memes" },
{ label: "Lighting", href: "/lighting", auth: true, icon: "padlock" },
{ label: "TRip", href: "/TRip", auth: true, icon: "padlock" },
{ label: "Memes", href: "/memes" },
{ label: "Lighting", href: "/lighting", auth: true },
{ label: "TRip", href: "/TRip", auth: true, icon: "pirate" },
{ label: "Status", href: "https://status.boatson.boats", icon: "external" },
{ label: "Git", href: "https://kode.boatson.boats", icon: "external" },
{ label: "Login", href: "/login", guestOnly: true },
...(isLoggedIn ? [{ label: "Logout", href: "#logout", onclick: "handleLogout()" }] : []),
];
const visibleNavItems = navItems.filter((item) => {
if (item.auth && !isLoggedIn) {
return false;
}
if (item.guestOnly && isLoggedIn) {
return false;
}
return true;
});
const currentPath = Astro.url.pathname;
---
@@ -37,7 +50,7 @@ const currentPath = Astro.url.pathname;
<nav class="w-full px-4 sm:px-6 py-4 bg-transparent sticky top-0 z-50 backdrop-blur-sm bg-white/80 dark:bg-[#121212]/80 border-b border-neutral-200/50 dark:border-neutral-800/50">
<div class="max-w-7xl mx-auto">
<div class="flex items-center justify-between">
<div class="nav-bar-row flex items-center gap-4 justify-between">
<!-- Logo/Brand -->
<a
href="/"
@@ -47,9 +60,9 @@ const currentPath = Astro.url.pathname;
</a>
<!-- Desktop Navigation -->
<div class="desktop-nav flex items-center gap-0.5">
<ul class="flex items-center gap-0.5">
{navItems.map((item) => {
<div class="desktop-nav flex items-center">
<ul class="desktop-nav-list">
{visibleNavItems.map((item) => {
const isExternal = item.href?.startsWith("http");
const isAuthedPath = item.auth ?? false;
const normalize = (url) => (url || '/').replace(/\/+$/, '') || '/';
@@ -80,6 +93,9 @@ const currentPath = Astro.url.pathname;
{item.icon === "padlock" && (
<span class="inline-flex" aria-hidden="true" set:html={padlockIconSvg}></span>
)}
{item.icon === "pirate" && (
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴‍☠️</span>
)}
</a>
</li>
);
@@ -90,7 +106,7 @@ const currentPath = Astro.url.pathname;
<button
aria-label="Toggle theme"
type="button"
class="flex items-center justify-center w-8 h-8 ml-1 rounded-md hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
onclick="toggleTheme()"
>
<Icon
@@ -137,7 +153,7 @@ const currentPath = Astro.url.pathname;
class="mobile-menu-dropdown md:hidden"
>
<ul class="flex flex-col gap-1 py-4">
{navItems.map((item) => {
{visibleNavItems.map((item) => {
const isExternal = item.href?.startsWith("http");
const isAuthedPath = item.auth ?? false;
const normalize = (url) => (url || '/').replace(/\/+$/, '') || '/';
@@ -168,6 +184,9 @@ const currentPath = Astro.url.pathname;
{item.icon === "padlock" && (
<span class="inline-flex" aria-hidden="true" set:html={padlockIconSvg}></span>
)}
{item.icon === "pirate" && (
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴‍☠️</span>
)}
</a>
</li>
);
@@ -222,4 +241,38 @@ const currentPath = Astro.url.pathname;
nav {
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.nav-bar-row {
width: 100%;
}
@media (min-width: 768px) {
.desktop-nav {
flex: 1;
display: flex;
align-items: center;
min-width: 0;
margin-left: 2rem;
gap: clamp(0.75rem, 1.5vw, 1.25rem);
}
}
.desktop-nav-list {
display: flex;
flex: 1;
justify-content: flex-end;
align-items: center;
gap: clamp(0.75rem, 1.5vw, 1.25rem);
margin: 0;
padding: 0;
}
.desktop-nav-list li {
flex-shrink: 1;
}
.desktop-nav-list a {
white-space: nowrap;
}
</style>