feat: add mobile widget view
This commit is contained in:
parent
15eb364ebd
commit
7b8c8ec5e8
|
|
@ -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>
|
||||||
|
|
@ -244,6 +281,7 @@ export function ApiKeyWidget() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,23 +141,37 @@ 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">
|
||||||
{items.map((item) => {
|
{/* ApiKeyWidget - Mobile */}
|
||||||
const active = isActive(item.href);
|
{rightSlot && (
|
||||||
return (
|
<>
|
||||||
<a
|
<div className="flex justify-end mb-3">
|
||||||
key={item.href}
|
{rightSlot}
|
||||||
href={item.href}
|
</div>
|
||||||
onClick={() => setMobileMenuOpen(false)}
|
{/* Visual separator */}
|
||||||
className={`
|
<div className="border-t border-white/10 mb-3" />
|
||||||
block px-4 py-3 rounded-lg text-sm font-medium transition-colors
|
</>
|
||||||
${active ? 'bg-purple-500/10 text-purple-400' : 'text-gray-400 hover:text-white hover:bg-white/5'}
|
)}
|
||||||
`}
|
|
||||||
>
|
{/* Navigation items */}
|
||||||
{item.label}
|
<div className="space-y-2">
|
||||||
</a>
|
{items.map((item) => {
|
||||||
);
|
const active = isActive(item.href);
|
||||||
})}
|
return (
|
||||||
|
<a
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
onClick={() => setMobileMenuOpen(false)}
|
||||||
|
className={`
|
||||||
|
block px-4 py-3 rounded-lg text-sm font-medium transition-colors
|
||||||
|
${active ? 'bg-purple-500/10 text-purple-400' : 'text-gray-400 hover:text-white hover:bg-white/5'}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue