feat: polish notes glass ui

This commit is contained in:
Julien Froidefond
2025-10-09 16:23:10 +02:00
parent d6538356a1
commit 1c28d6b782
3 changed files with 60 additions and 45 deletions

View File

@@ -6,6 +6,7 @@ import { notesClient } from '@/clients/notes';
import { NotesList } from '@/components/notes/NotesList'; import { NotesList } from '@/components/notes/NotesList';
import { MarkdownEditor } from '@/components/notes/MarkdownEditor'; import { MarkdownEditor } from '@/components/notes/MarkdownEditor';
import { Header } from '@/components/ui/Header'; import { Header } from '@/components/ui/Header';
import { Card } from '@/components/ui';
import { TasksProvider, useTasksContext } from '@/contexts/TasksContext'; import { TasksProvider, useTasksContext } from '@/contexts/TasksContext';
import { Tag } from '@/lib/types'; import { Tag } from '@/lib/types';
import { FileText, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-react'; import { FileText, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-react';
@@ -22,6 +23,9 @@ function NotesPageContent({ initialNotes }: { initialNotes: Note[] }) {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const glassSurface =
'bg-[var(--card)]/40 backdrop-blur-md relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-[color-mix(in_srgb,var(--primary)_8%,transparent)] before:via-[color-mix(in_srgb,var(--primary)_4%,transparent)] before:to-transparent before:opacity-80 before:pointer-events-none';
const glassDivider = `${glassSurface} border-b border-[var(--border)]/60`;
// Select first note if none selected // Select first note if none selected
useEffect(() => { useEffect(() => {
@@ -152,11 +156,14 @@ function NotesPageContent({ initialNotes }: { initialNotes: Note[] }) {
}, [selectedNote, hasUnsavedChanges, handleSave]); }, [selectedNote, hasUnsavedChanges, handleSave]);
return ( return (
<div className="h-screen bg-[var(--background)] flex flex-col"> <div className="min-h-screen bg-[var(--background)] flex flex-col">
<Header title="Notes" subtitle="Gestionnaire de notes markdown" /> <Header title="Notes" subtitle="Gestionnaire de notes markdown" />
<div className="flex-1 container mx-auto px-4 py-4"> <div className="flex-1 container mx-auto px-4 py-6">
<div className="flex h-full bg-[var(--card)]/40 border border-[var(--border)]/60 backdrop-blur-md shadow-xl shadow-[var(--card-shadow-medium)] rounded-lg overflow-hidden relative before:absolute before:inset-0 before:rounded-lg before:bg-gradient-to-br before:from-[color-mix(in_srgb,var(--primary)_12%,transparent)] before:via-[color-mix(in_srgb,var(--primary)_6%,transparent)] before:to-transparent before:opacity-90 before:pointer-events-none"> <Card
variant="glass"
className="flex h-full rounded-2xl overflow-hidden"
>
{/* Notes List Sidebar */} {/* Notes List Sidebar */}
<div <div
className={`${sidebarCollapsed ? 'w-12' : 'w-80'} flex-shrink-0 border-r border-[var(--border)] transition-all duration-300 ease-in-out`} className={`${sidebarCollapsed ? 'w-12' : 'w-80'} flex-shrink-0 border-r border-[var(--border)] transition-all duration-300 ease-in-out`}
@@ -175,7 +182,9 @@ function NotesPageContent({ initialNotes }: { initialNotes: Note[] }) {
</div> </div>
) : ( ) : (
<> <>
<div className="flex items-center justify-between p-3 border-b border-[var(--border)]/60 bg-[var(--card)]/40 backdrop-blur-md"> <div
className={`${glassDivider} flex items-center justify-between p-3`}
>
<h2 className="text-sm font-mono font-semibold text-[var(--foreground)] uppercase tracking-wider"> <h2 className="text-sm font-mono font-semibold text-[var(--foreground)] uppercase tracking-wider">
Notes Notes
</h2> </h2>
@@ -217,7 +226,7 @@ function NotesPageContent({ initialNotes }: { initialNotes: Note[] }) {
{selectedNote ? ( {selectedNote ? (
<> <>
{/* Note Header */} {/* Note Header */}
<div className="flex items-center gap-4 p-4 border-b border-[var(--border)]/60 bg-[var(--card)]/40 backdrop-blur-md relative before:absolute before:inset-0 before:bg-gradient-to-r before:from-[color-mix(in_srgb,var(--primary)_8%,transparent)] before:via-[color-mix(in_srgb,var(--primary)_4%,transparent)] before:to-transparent before:opacity-80 before:pointer-events-none"> <div className={`${glassDivider} flex items-center gap-4 p-4`}>
<FileText className="w-5 h-5 text-[var(--muted-foreground)]" /> <FileText className="w-5 h-5 text-[var(--muted-foreground)]" />
<div className="flex-1"> <div className="flex-1">
<h2 className="text-lg font-semibold text-[var(--foreground)] truncate"> <h2 className="text-lg font-semibold text-[var(--foreground)] truncate">
@@ -271,7 +280,7 @@ function NotesPageContent({ initialNotes }: { initialNotes: Note[] }) {
</div> </div>
)} )}
</div> </div>
</div> </Card>
</div> </div>
</div> </div>
); );

View File

@@ -345,6 +345,7 @@ export function MarkdownEditor({
onChange={onTagsChange} onChange={onTagsChange}
placeholder="Ajouter des tags..." placeholder="Ajouter des tags..."
maxTags={10} maxTags={10}
compactSuggestions={true}
/> />
</div> </div>
</div> </div>

View File

@@ -268,52 +268,57 @@ export function TagInput({
createPortal( createPortal(
<div <div
ref={suggestionsRef} ref={suggestionsRef}
className="fixed bg-[var(--card)] border border-[var(--border)] rounded-lg shadow-xl z-[9999] max-h-64 overflow-y-auto" className="fixed z-[9999] max-h-64 overflow-y-auto rounded-xl border border-[var(--border)]/60 bg-[var(--card)]/80 backdrop-blur-xl shadow-2xl shadow-[var(--card-shadow-medium)] relative"
style={{ style={{
top: dropdownPosition.top, top: dropdownPosition.top,
left: dropdownPosition.left, left: dropdownPosition.left,
width: dropdownPosition.width, width: dropdownPosition.width,
}} }}
> >
{loading ? ( <div className="absolute inset-0 rounded-xl bg-gradient-to-br from-[color-mix(in_srgb,var(--primary)_12%,transparent)] via-[color-mix(in_srgb,var(--primary)_6%,transparent)] to-transparent opacity-90 pointer-events-none" />
<div className="p-3 text-center text-[var(--muted-foreground)] text-sm"> <div className="relative z-10">
Recherche... {loading ? (
</div> <div className="p-3 text-center text-[var(--muted-foreground)] text-sm">
) : ( Recherche...
<div </div>
className={`gap-2 p-3 ${compactSuggestions ? 'grid grid-cols-1' : 'grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4'}`} ) : (
> <div
{suggestions.map((tag, index) => ( className={`gap-2 p-3 ${compactSuggestions ? 'grid grid-cols-1' : 'grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4'}`}
<button >
key={tag.id} {suggestions.map((tag, index) => (
type="button" <button
onClick={() => handleSuggestionClick(tag)} key={tag.id}
className={`flex items-center gap-2 px-2 py-1.5 text-xs rounded-md transition-colors ${ type="button"
index === selectedIndex onClick={() => handleSuggestionClick(tag)}
? 'bg-[var(--card-hover)] text-[var(--primary)] ring-1 ring-[var(--primary)]' className={`flex items-center gap-2 px-2 py-1.5 text-xs rounded-md transition-colors border ${
: 'text-[var(--foreground)] hover:bg-[var(--card-hover)]' index === selectedIndex
} ${tags.includes(tag.name) ? 'opacity-50 cursor-not-allowed' : ''}`} ? 'bg-[var(--primary)]/15 text-[var(--primary)] ring-1 ring-[var(--primary)]/40 border-[var(--primary)]/40 backdrop-blur-sm'
disabled={tags.includes(tag.name)} : 'text-[var(--foreground)] border-transparent hover:bg-[var(--card)]/40 hover:border-[var(--border)]/40 backdrop-blur-sm'
title={ } ${tags.includes(tag.name) ? 'opacity-50 cursor-not-allowed' : ''}`}
tags.includes(tag.name) disabled={tags.includes(tag.name)}
? 'Déjà ajouté' title={
: `Ajouter ${tag.name}` tags.includes(tag.name)
} ? 'Déjà ajouté'
> : `Ajouter ${tag.name}`
<div }
className="w-2 h-2 rounded-full flex-shrink-0" >
style={{ backgroundColor: tag.color }} <div
/> className="w-2 h-2 rounded-full flex-shrink-0"
<span className="truncate">{tag.name}</span> style={{ backgroundColor: tag.color }}
{tags.includes(tag.name) && ( />
<span className="text-[var(--muted-foreground)] ml-auto"> <span className="truncate text-[var(--foreground)]">
{tag.name}
</span> </span>
)} {tags.includes(tag.name) && (
</button> <span className="text-[var(--muted-foreground)] ml-auto">
))}
</div> </span>
)} )}
</button>
))}
</div>
)}
</div>
</div>, </div>,
document.body document.body
)} )}