website refactor
This commit is contained in:
@@ -229,7 +229,9 @@ export function DevToolbar() {
|
|||||||
|
|
||||||
if (isMinimized) {
|
if (isMinimized) {
|
||||||
return (
|
return (
|
||||||
<Stack position="fixed" right={4} bottom={4} zIndex={1000}>
|
<Stack
|
||||||
|
style={{ position: 'fixed', right: '1rem', bottom: '6rem', zIndex: 1000 }}
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={Wrench}
|
icon={Wrench}
|
||||||
onClick={() => setIsMinimized(false)}
|
onClick={() => setIsMinimized(false)}
|
||||||
@@ -243,10 +245,6 @@ export function DevToolbar() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack
|
<Stack
|
||||||
position="fixed"
|
|
||||||
right={4}
|
|
||||||
bottom={24}
|
|
||||||
zIndex={1000}
|
|
||||||
width="min(420px, calc(100vw - 2rem))"
|
width="min(420px, calc(100vw - 2rem))"
|
||||||
maxHeight="calc(100vh - 2rem)"
|
maxHeight="calc(100vh - 2rem)"
|
||||||
overflow="auto"
|
overflow="auto"
|
||||||
@@ -256,6 +254,10 @@ export function DevToolbar() {
|
|||||||
bg="rgba(20, 22, 25, 0.92)"
|
bg="rgba(20, 22, 25, 0.92)"
|
||||||
padding={3}
|
padding={3}
|
||||||
style={{
|
style={{
|
||||||
|
position: 'fixed',
|
||||||
|
right: '1rem',
|
||||||
|
bottom: '6rem',
|
||||||
|
zIndex: 1000,
|
||||||
boxShadow: '0 18px 40px rgba(0,0,0,0.55)',
|
boxShadow: '0 18px 40px rgba(0,0,0,0.55)',
|
||||||
backdropFilter: 'blur(12px)',
|
backdropFilter: 'blur(12px)',
|
||||||
WebkitBackdropFilter: 'blur(12px)',
|
WebkitBackdropFilter: 'blur(12px)',
|
||||||
@@ -277,12 +279,14 @@ export function DevToolbar() {
|
|||||||
onClick={() => setIsExpanded(!isExpanded)}
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
title={isExpanded ? "Collapse" : "Expand"}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={X}
|
icon={X}
|
||||||
onClick={() => setIsMinimized(true)}
|
onClick={() => setIsMinimized(true)}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
title="Minimize"
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Modal } from '@/components/shared/Modal';
|
|
||||||
import { Box } from '@/ui/Box';
|
import { Box } from '@/ui/Box';
|
||||||
import { Text } from '@/ui/Text';
|
import { Text } from '@/ui/Text';
|
||||||
import { Search, Command, ArrowRight } from 'lucide-react';
|
import { Search, Command, ArrowRight, X } from 'lucide-react';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
interface CommandModalProps {
|
interface CommandModalProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@@ -13,6 +13,27 @@ interface CommandModalProps {
|
|||||||
|
|
||||||
export function CommandModal({ isOpen, onClose }: CommandModalProps) {
|
export function CommandModal({ isOpen, onClose }: CommandModalProps) {
|
||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const down = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (isOpen) {
|
||||||
|
document.addEventListener('keydown', down);
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', down);
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
};
|
||||||
|
}, [isOpen, onClose]);
|
||||||
|
|
||||||
// Mock results
|
// Mock results
|
||||||
const results = [
|
const results = [
|
||||||
@@ -21,50 +42,75 @@ export function CommandModal({ isOpen, onClose }: CommandModalProps) {
|
|||||||
{ label: 'Create League', shortcut: 'C L' },
|
{ label: 'Create League', shortcut: 'C L' },
|
||||||
].filter(r => r.label.toLowerCase().includes(query.toLowerCase()));
|
].filter(r => r.label.toLowerCase().includes(query.toLowerCase()));
|
||||||
|
|
||||||
return (
|
if (!mounted) return null;
|
||||||
<Modal
|
if (!isOpen) return null;
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={onClose}
|
return createPortal(
|
||||||
title="Command Palette"
|
<div className="fixed inset-0 z-[9999] flex items-start justify-center pt-[20vh] px-4">
|
||||||
size="md"
|
{/* Backdrop */}
|
||||||
icon={<Command size={20} />}
|
<div
|
||||||
>
|
className="absolute inset-0 bg-black/60 backdrop-blur-sm transition-opacity"
|
||||||
<Box className="flex flex-col gap-4">
|
onClick={onClose}
|
||||||
<Box className="relative">
|
/>
|
||||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-text-low" size={16} />
|
|
||||||
|
{/* Modal Content */}
|
||||||
|
<div className="relative w-full max-w-lg bg-surface-charcoal border border-outline-steel rounded-xl shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200">
|
||||||
|
<div className="flex items-center border-b border-outline-steel px-4 py-3 gap-3">
|
||||||
|
<Search className="text-text-low" size={18} />
|
||||||
<input
|
<input
|
||||||
autoFocus
|
autoFocus
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Type a command or search..."
|
placeholder="Type a command or search..."
|
||||||
className="w-full bg-surface-charcoal border border-outline-steel rounded-md pl-10 pr-4 py-3 text-text-high placeholder:text-text-low focus:border-primary-accent focus:outline-none transition-colors"
|
className="flex-1 bg-transparent border-none outline-none text-text-high placeholder:text-text-low/50 text-base h-6"
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
<button onClick={onClose} className="text-text-low hover:text-text-high transition-colors">
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
<kbd className="hidden sm:inline-block px-1.5 py-0.5 text-[10px] font-mono bg-white/5 rounded border border-white/5">ESC</kbd>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Box className="flex flex-col gap-1">
|
<div className="p-2">
|
||||||
<Text size="xs" className="text-text-low font-mono uppercase tracking-wider px-2 mb-1">
|
{results.length > 0 ? (
|
||||||
Suggestions
|
<div className="flex flex-col gap-1">
|
||||||
</Text>
|
<div className="px-2 py-1.5 text-[10px] font-mono uppercase tracking-wider text-text-low/50 font-bold">
|
||||||
{results.map((result, i) => (
|
Suggestions
|
||||||
<button
|
</div>
|
||||||
key={i}
|
{results.map((result, i) => (
|
||||||
className="flex items-center justify-between px-3 py-2 rounded-md hover:bg-white/5 text-left group transition-colors"
|
<button
|
||||||
onClick={onClose}
|
key={i}
|
||||||
>
|
className="flex items-center justify-between px-3 py-2.5 rounded-lg hover:bg-white/5 text-left group transition-colors"
|
||||||
<Text size="sm" className="text-text-med group-hover:text-text-high">
|
onClick={onClose}
|
||||||
{result.label}
|
>
|
||||||
</Text>
|
<span className="text-sm text-text-med group-hover:text-text-high font-medium">
|
||||||
<Box className="flex items-center gap-2">
|
{result.label}
|
||||||
<Text size="xs" className="text-text-low font-mono bg-white/5 px-1.5 py-0.5 rounded">
|
</span>
|
||||||
{result.shortcut}
|
<div className="flex items-center gap-2">
|
||||||
</Text>
|
<span className="text-[10px] font-mono text-text-low bg-white/5 px-1.5 py-0.5 rounded border border-white/5">
|
||||||
<ArrowRight size={14} className="text-text-low opacity-0 group-hover:opacity-100 transition-opacity" />
|
{result.shortcut}
|
||||||
</Box>
|
</span>
|
||||||
</button>
|
<ArrowRight size={14} className="text-text-low opacity-0 group-hover:opacity-100 transition-opacity -translate-x-2 group-hover:translate-x-0" />
|
||||||
))}
|
</div>
|
||||||
</Box>
|
</button>
|
||||||
</Box>
|
))}
|
||||||
</Modal>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="px-4 py-8 text-center text-text-low text-sm">
|
||||||
|
No results found.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-4 py-2 bg-white/2 border-t border-white/5 flex items-center justify-between text-[10px] text-text-low">
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<span><strong className="text-text-med">↑↓</strong> to navigate</span>
|
||||||
|
<span><strong className="text-text-med">↵</strong> to select</span>
|
||||||
|
</div>
|
||||||
|
<span>GridPilot Command</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user