WIP: Améliorations UX et corrections de bugs avant création des épiques
This commit is contained in:
@@ -77,6 +77,41 @@ export function Header({
|
||||
setNotebookId(currentNotebook || null)
|
||||
}, [currentNotebook, setNotebookId])
|
||||
|
||||
// Prevent body scroll when mobile menu is open
|
||||
useEffect(() => {
|
||||
if (isSidebarOpen) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
document.body.style.position = 'fixed'
|
||||
document.body.style.width = '100%'
|
||||
} else {
|
||||
document.body.style.overflow = ''
|
||||
document.body.style.position = ''
|
||||
document.body.style.width = ''
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = ''
|
||||
document.body.style.position = ''
|
||||
document.body.style.width = ''
|
||||
}
|
||||
}, [isSidebarOpen])
|
||||
|
||||
// Close mobile menu on Esc key press
|
||||
useEffect(() => {
|
||||
const handleEscapeKey = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isSidebarOpen) {
|
||||
setIsSidebarOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (isSidebarOpen) {
|
||||
document.addEventListener('keydown', handleEscapeKey)
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleEscapeKey)
|
||||
}
|
||||
}, [isSidebarOpen])
|
||||
|
||||
// Simple debounced search with URL update (150ms for more responsiveness)
|
||||
const debouncedSearchQuery = useDebounce(searchQuery, 150)
|
||||
|
||||
@@ -224,6 +259,8 @@ export function Header({
|
||||
? "bg-[#EFB162] text-amber-900"
|
||||
: "hover:bg-gray-100 dark:hover:bg-zinc-800 text-gray-700 dark:text-gray-300"
|
||||
)}
|
||||
style={{ minHeight: '44px' }}
|
||||
aria-pressed={active}
|
||||
>
|
||||
{content}
|
||||
</button>
|
||||
@@ -240,6 +277,8 @@ export function Header({
|
||||
? "bg-[#EFB162] text-amber-900"
|
||||
: "hover:bg-gray-100 dark:hover:bg-zinc-800 text-gray-700 dark:text-gray-300"
|
||||
)}
|
||||
style={{ minHeight: '44px' }}
|
||||
aria-current={active ? 'page' : undefined}
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
@@ -250,20 +289,36 @@ export function Header({
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="h-20 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur-sm border-b border-transparent flex items-center justify-between px-6 lg:px-12 flex-shrink-0 z-30 sticky top-0">
|
||||
<header className="h-20 bg-background/90 backdrop-blur-sm border-b border-transparent flex items-center justify-between px-6 lg:px-12 flex-shrink-0 z-30 sticky top-0">
|
||||
{/* Mobile Menu Button */}
|
||||
<Sheet open={isSidebarOpen} onOpenChange={setIsSidebarOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="lg:hidden mr-4 text-slate-500 dark:text-slate-400">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="lg:hidden mr-4 text-muted-foreground"
|
||||
aria-label="Open menu"
|
||||
aria-expanded={isSidebarOpen}
|
||||
>
|
||||
<Menu className="h-6 w-6" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left" className="w-[280px] sm:w-[320px] p-0 pt-4">
|
||||
<SheetHeader className="px-4 mb-4">
|
||||
<SheetHeader className="px-4 mb-4 flex items-center justify-between">
|
||||
<SheetTitle className="flex items-center gap-2 text-xl font-normal">
|
||||
<StickyNote className="h-6 w-6 text-amber-500" />
|
||||
<StickyNote className="h-6 w-6 text-primary" />
|
||||
{t('nav.workspace')}
|
||||
</SheetTitle>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setIsSidebarOpen(false)}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
aria-label="Close menu"
|
||||
style={{ width: '44px', height: '44px' }}
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</Button>
|
||||
</SheetHeader>
|
||||
<div className="flex flex-col gap-1 py-2">
|
||||
<NavItem
|
||||
@@ -280,7 +335,7 @@ export function Header({
|
||||
/>
|
||||
|
||||
<div className="my-2 px-4 flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-gray-500 uppercase tracking-wider">{t('labels.title')}</span>
|
||||
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">{t('labels.title')}</span>
|
||||
</div>
|
||||
|
||||
{labels.map(label => (
|
||||
@@ -312,10 +367,10 @@ export function Header({
|
||||
</Sheet>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="flex-1 max-w-2xl flex items-center bg-white dark:bg-slate-800/80 rounded-2xl px-4 py-3 shadow-sm border border-transparent focus-within:border-indigo-500/50 focus-within:ring-2 ring-indigo-500/10 transition-all">
|
||||
<Search className="text-slate-400 dark:text-slate-500 text-xl" />
|
||||
<div className="flex-1 max-w-2xl flex items-center bg-card rounded-lg px-4 py-3 shadow-sm border border-transparent focus-within:border-primary/50 focus-within:ring-2 focus-within:ring-primary/10 transition-all">
|
||||
<Search className="text-muted-foreground text-xl" />
|
||||
<input
|
||||
className="bg-transparent border-none outline-none focus:ring-0 w-full text-sm text-slate-700 dark:text-slate-200 ml-3 placeholder-slate-400"
|
||||
className="bg-transparent border-none outline-none focus:ring-0 w-full text-sm text-foreground ml-3 placeholder-muted-foreground"
|
||||
placeholder={t('search.placeholder') || "Search notes, tags, or notebooks..."}
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
@@ -327,11 +382,11 @@ export function Header({
|
||||
onClick={handleSemanticSearch}
|
||||
disabled={!searchQuery.trim() || isSemanticSearching}
|
||||
className={cn(
|
||||
"flex items-center gap-1 px-2 py-1.5 rounded-md text-xs font-medium transition-colors",
|
||||
"hover:bg-indigo-100 dark:hover:bg-indigo-900/30",
|
||||
"flex items-center gap-1 px-2 py-1.5 rounded-md text-xs font-medium transition-colors min-h-[36px]",
|
||||
"hover:bg-accent",
|
||||
searchParams.get('semantic') === 'true'
|
||||
? "bg-indigo-200 dark:bg-indigo-900/50 text-indigo-900 dark:text-indigo-100"
|
||||
: "text-gray-500 dark:text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-300",
|
||||
? "bg-primary/20 text-primary"
|
||||
: "text-muted-foreground hover:text-primary",
|
||||
"disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
)}
|
||||
title={t('search.semanticTooltip')}
|
||||
@@ -342,7 +397,7 @@ export function Header({
|
||||
{searchQuery && (
|
||||
<button
|
||||
onClick={() => handleSearch('')}
|
||||
className="ml-2 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200"
|
||||
className="ml-2 text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
@@ -358,14 +413,14 @@ export function Header({
|
||||
/>
|
||||
|
||||
{/* Grid View Button */}
|
||||
<button className="p-2.5 text-slate-500 hover:bg-white hover:shadow-sm dark:text-slate-400 dark:hover:bg-slate-700 rounded-xl transition-all duration-200">
|
||||
<button className="p-2.5 text-muted-foreground hover:bg-accent rounded-lg transition-colors duration-200 min-h-[44px] min-w-[44px]">
|
||||
<Grid3x3 className="text-xl" />
|
||||
</button>
|
||||
|
||||
{/* Theme Toggle */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button className="p-2.5 text-slate-500 hover:bg-white hover:shadow-sm dark:text-slate-400 dark:hover:bg-slate-700 rounded-xl transition-all duration-200">
|
||||
<button className="p-2.5 text-muted-foreground hover:bg-accent rounded-lg transition-colors duration-200 min-h-[44px] min-w-[44px]">
|
||||
{theme === 'light' ? <Sun className="text-xl" /> : <Moon className="text-xl" />}
|
||||
</button>
|
||||
</DropdownMenuTrigger>
|
||||
@@ -384,12 +439,12 @@ export function Header({
|
||||
|
||||
{/* Active Filters Bar */}
|
||||
{hasActiveFilters && (
|
||||
<div className="px-6 lg:px-12 pb-3 flex items-center gap-2 overflow-x-auto border-t border-gray-100 dark:border-zinc-800 pt-2 bg-white/50 dark:bg-zinc-900/50 backdrop-blur-sm animate-in slide-in-from-top-2">
|
||||
<div className="px-6 lg:px-12 pb-3 flex items-center gap-2 overflow-x-auto border-t border-border pt-2 bg-background/50 backdrop-blur-sm animate-in slide-in-from-top-2">
|
||||
{currentColor && (
|
||||
<Badge variant="secondary" className="flex items-center gap-1 h-7 whitespace-nowrap pl-2 pr-1">
|
||||
<div className={cn("w-3 h-3 rounded-full border border-black/10", `bg-${currentColor}-500`)} />
|
||||
{t('notes.color')}: {currentColor}
|
||||
<button onClick={removeColorFilter} className="ml-1 hover:bg-black/10 dark:hover:bg-white/10 rounded-full p-0.5">
|
||||
<button onClick={removeColorFilter} className="ml-1 hover:bg-black/10 dark:hover:bg-white/10 rounded-full p-0.5 min-h-[24px] min-w-[24px]">
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
</Badge>
|
||||
@@ -409,7 +464,7 @@ export function Header({
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={clearAllFilters}
|
||||
className="h-7 text-xs text-indigo-600 hover:text-indigo-700 hover:bg-indigo-50 dark:text-indigo-400 dark:hover:bg-indigo-900/20 whitespace-nowrap ml-auto"
|
||||
className="h-7 text-xs text-primary hover:text-primary hover:bg-accent whitespace-nowrap ml-auto"
|
||||
>
|
||||
{t('labels.clearAll')}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user