feat: polish notes glass ui
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user