feat: add mobile widget view

This commit is contained in:
Oleg Proskurin 2025-10-23 23:13:50 +07:00
parent 15eb364ebd
commit 7b8c8ec5e8
2 changed files with 78 additions and 26 deletions

View File

@ -15,7 +15,7 @@
* Must be used within ApiKeyProvider context * Must be used within ApiKeyProvider context
*/ */
import { useEffect, useRef } from 'react'; import { useEffect, useRef, useState } from 'react';
import { useApiKey } from './apikey-context'; import { useApiKey } from './apikey-context';
export function ApiKeyWidget() { export function ApiKeyWidget() {
@ -35,6 +35,22 @@ export function ApiKeyWidget() {
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const [isMobileContext, setIsMobileContext] = useState(false);
// Detect mobile context (inside mobile dropdown menu)
useEffect(() => {
const checkContext = () => {
if (containerRef.current) {
// Check if ancestor has md:hidden class (mobile menu)
const mobileMenu = containerRef.current.closest('.md\\:hidden');
setIsMobileContext(mobileMenu !== null);
}
};
checkContext();
window.addEventListener('resize', checkContext);
return () => window.removeEventListener('resize', checkContext);
}, []);
// Click outside to close // Click outside to close
useEffect(() => { useEffect(() => {
@ -72,13 +88,29 @@ export function ApiKeyWidget() {
}; };
return ( return (
<div ref={containerRef} className="relative h-full flex items-center"> <>
{!ui.expanded ? ( {/* Backdrop for mobile expanded state */}
// COLLAPSED STATE - Inline badge in nav {ui.expanded && isMobileContext && (
<div
className="fixed inset-0 bg-black/50 z-40"
onClick={() => setExpanded(false)}
aria-label="Close API key details"
/>
)}
<div ref={containerRef} className={isMobileContext ? "relative" : "relative h-full flex items-center"}>
{/* COLLAPSED STATE - Always rendered to maintain layout space */}
<button <button
onClick={() => setExpanded(true)} onClick={() => setExpanded(true)}
className="group px-3 py-2 bg-slate-900/95 backdrop-blur-sm border border-slate-700 rounded-lg hover:border-purple-500/50 transition-all h-10 flex items-center" className={`group px-3 bg-slate-900/95 backdrop-blur-sm border border-slate-700 rounded-lg hover:border-purple-500/50 transition-all flex items-center ${
isMobileContext
? 'w-auto max-w-full py-2.5 h-11' // Mobile: auto-width, larger touch target
: 'py-2 h-10' // Desktop: original size
} ${
ui.expanded && isMobileContext ? 'opacity-50 pointer-events-none' : '' // Fade when expanded on mobile
}`}
aria-label="Expand API key details" aria-label="Expand API key details"
disabled={ui.expanded}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div <div
@ -104,9 +136,14 @@ export function ApiKeyWidget() {
</svg> </svg>
</div> </div>
</button> </button>
) : (
// EXPANDED STATE - Popup dropdown {/* EXPANDED STATE - Rendered as overlay when expanded */}
<div className="absolute top-0 right-0 w-96 p-5 bg-slate-900/95 backdrop-blur-sm border border-slate-700 rounded-xl shadow-2xl z-50 animate-in fade-in slide-in-from-top-2 duration-200"> {ui.expanded && (
<div className={`bg-slate-900/95 backdrop-blur-sm border border-slate-700 rounded-xl shadow-2xl animate-in fade-in duration-200 ${
isMobileContext
? 'absolute top-0 left-0 right-0 p-4 z-50' // Mobile: absolute positioned, top-aligned
: 'absolute top-0 right-0 w-96 p-5 z-50' // Desktop: absolute positioned
}`}>
{/* Header */} {/* Header */}
<div className="flex items-start justify-between mb-4"> <div className="flex items-start justify-between mb-4">
<div> <div>
@ -245,5 +282,6 @@ export function ApiKeyWidget() {
</div> </div>
)} )}
</div> </div>
</>
); );
} }

View File

@ -141,7 +141,20 @@ export const SubsectionNav = ({ items, currentPath, leftSlot, rightSlot }: Subse
{/* Mobile Menu Overlay */} {/* Mobile Menu Overlay */}
{mobileMenuOpen && ( {mobileMenuOpen && (
<div className="md:hidden border-t border-white/10 bg-slate-900/95 backdrop-blur-sm"> <div className="md:hidden border-t border-white/10 bg-slate-900/95 backdrop-blur-sm">
<div className="px-6 py-4 space-y-2"> <div className="px-6 py-4">
{/* ApiKeyWidget - Mobile */}
{rightSlot && (
<>
<div className="flex justify-end mb-3">
{rightSlot}
</div>
{/* Visual separator */}
<div className="border-t border-white/10 mb-3" />
</>
)}
{/* Navigation items */}
<div className="space-y-2">
{items.map((item) => { {items.map((item) => {
const active = isActive(item.href); const active = isActive(item.href);
return ( return (
@ -160,6 +173,7 @@ export const SubsectionNav = ({ items, currentPath, leftSlot, rightSlot }: Subse
})} })}
</div> </div>
</div> </div>
</div>
)} )}
</nav> </nav>
); );