ObsiViewer/src/app.component.corrected3.html
2025-09-27 18:13:02 -04:00

215 lines
14 KiB
HTML

@if (!isDesktop() && isSidebarOpen()) {
<div
class="fixed inset-0 z-30 bg-black/40 transition-opacity duration-200"
(click)="closeSidebar()"
></div>
}
<div class="hidden lg:flex">
<div
class="resize-handle h-full"
role="separator"
aria-orientation="vertical"
aria-label="Redimensionner la barre latérale gauche"
(pointerdown)="startLeftResize($event)"
></div>
</div>
<section class="flex min-w-0 flex-col bg-obs-l-bg-main pb-16 dark:bg-obs-d-bg-main lg:flex-1 lg:min-h-0 lg:overflow-hidden lg:pb-0">
<header class="flex flex-col gap-4 border-b border-obs-l-border/60 bg-obs-l-bg-main/95 px-4 py-3 backdrop-blur-xs dark:border-obs-d-border dark:bg-obs-d-bg-main/95 lg:px-6">
<div class="flex items-start justify-between gap-4">
<div class="flex items-center gap-3 min-w-0">
<button
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary lg:hidden"
(click)="toggleSidebar()"
[attr.aria-expanded]="isSidebarOpen()"
aria-label="Basculer le menu"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /></svg>
</button>
<button
class="hidden rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary lg:inline-flex"
(click)="toggleSidebar()"
[attr.aria-expanded]="isSidebarOpen()"
aria-label="Afficher ou masquer la barre latérale gauche"
>
@if (isSidebarOpen()) {
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="m16 15-3-3 3-3"/></svg>
} @else {
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/><path d="m14 9 3 3-3 3"/></svg>
}
</button>
<div class="min-w-0 flex flex-col gap-1">
<div class="flex items-center gap-2 min-w-0">
<span class="inline-flex items-center rounded-lg border border-obs-l-border bg-obs-l-bg-secondary/60 px-2.5 py-1 text-xs font-semibold uppercase tracking-wide text-obs-l-text-muted dark:border-obs-d-border dark:bg-obs-d-bg-secondary/60 dark:text-obs-d-text-muted">{{ vaultName() }}</span>
<h1 class="text-lg font-semibold leading-tight text-obs-l-text-main dark:text-obs-d-text-main">ObsiWatcher</h1>
</div>
@if (selectedNoteBreadcrumb().length > 0) {
<p class="truncate text-xs text-obs-l-text-muted dark:text-obs-d-text-muted">{{ selectedNoteBreadcrumb().join(' / ') }}</p>
} @else {
<p class="truncate text-xs text-obs-l-text-muted dark:text-obs-d-text-muted">Aucune note sélectionnée</p>
}
</div>
</div>
<div class="hidden items-center gap-2 lg:flex">
<button
(click)="toggleTheme()"
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary"
aria-label="Basculer le thème"
>
@if (isDarkMode()) {
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" /></svg>
} @else {
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" /></svg>
}
</button>
<button
(click)="toggleOutline()"
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary"
[attr.aria-expanded]="isOutlineOpen()"
aria-label="Basculer la barre latérale droite"
>
@if (isOutlineOpen()) {
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m8 9 3 3-3 3"/></svg>
} @else {
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m10 15-3-3 3-3"/></svg>
}
</button>
</div>
</div>
<div class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between lg:gap-6">
<div class="flex items-center gap-2 lg:hidden">
<button
(click)="toggleTheme()"
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary"
aria-label="Basculer le thème"
>
@if (isDarkMode()) {
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" /></svg>
} @else {
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" /></svg>
}
</button>
<button
(click)="toggleOutline()"
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-secondary dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-secondary"
[attr.aria-expanded]="isOutlineOpen()"
aria-label="Basculer le calendrier"
>
@if (isOutlineOpen()) {
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m8 9 3 3-3 3"/></svg>
} @else {
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m10 15-3-3 3-3"/></svg>
}
</button>
</div>
<div class="relative w-full max-w-2xl lg:max-w-3xl">
<svg class="pointer-events-none absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-obs-l-text-muted dark:text-obs-d-text-muted" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>
<input
type="text"
placeholder="Rechercher dans la voûte..."
[(ngModel)]="sidebarSearchTerm"
(ngModelChange)="activeView.set('search')"
class="w-full rounded-full border border-obs-l-border bg-obs-l-bg-secondary/60 pl-11 pr-4 py-2.5 text-sm text-obs-l-text-main placeholder:text-obs-l-text-muted shadow-sm focus:outline-none focus:ring-2 focus:ring-obs-l-accent dark:border-obs-d-border dark:bg-obs-d-bg-secondary/60 dark:text-obs-d-text-main dark:placeholder:text-obs-d-text-muted dark:focus:ring-obs-d-accent"
aria-label="Rechercher dans la voûte"
/>
</div>
</div>
</header>
<div class="min-h-0 px-4 py-6 note-content-area lg:flex-1 lg:overflow-y-auto lg:px-8">
@if (selectedNote(); as note) {
<app-note-viewer
[note]="note"
[noteHtmlContent]="renderedNoteContent()"
(noteLinkClicked)="selectNote($event)"
(tagClicked)="handleTagClick($event)"
></app-note-viewer>
} @else {
<div class="flex h-full items-center justify-center">
<p class="text-obs-l-text-muted dark:text-obs-d-text-muted">Sélectionnez une note pour commencer</p>
</div>
}
</div>
</section>
<div class="hidden lg:flex">
<div
class="resize-handle h-full"
role="separator"
aria-orientation="vertical"
aria-label="Redimensionner la barre latérale droite"
(pointerdown)="startRightResize($event)"
></div>
</div>
@if (!isDesktop() && isOutlineOpen()) {
<div
class="fixed inset-0 z-30 bg-black/40 transition-opacity duration-200"
(click)="closeOutlinePanel()"
></div>
}
@if (isDesktop() || isOutlineOpen()) {
<aside
class="fixed inset-x-0 bottom-0 z-40 flex max-h-[80vh] flex-col overflow-hidden border-t border-obs-l-border bg-obs-l-bg-secondary shadow-2xl transition-all duration-200 ease-out dark:border-obs-d-border dark:bg-obs-d-bg-secondary lg:static lg:max-h-none lg:border-l lg:shadow-none"
[ngClass]="{
'translate-y-0 opacity-100 pointer-events-auto': isOutlineOpen() || isDesktop(),
'translate-y-full opacity-0 pointer-events-none': !isOutlineOpen() && !isDesktop()
}"
[style.width.px]="isDesktop() ? (isOutlineOpen() ? rightSidebarWidth() : 0) : null"
[style.minWidth.px]="isDesktop() ? (isOutlineOpen() ? rightSidebarWidth() : 0) : null"
[style.maxWidth.px]="isDesktop() ? (isOutlineOpen() ? rightSidebarWidth() : 0) : null"
role="complementary"
aria-label="Table des matières et calendrier"
>
<div class="flex flex-col lg:h-full">
@if (!isDesktop()) {
<div class="px-4 pt-3">
<div class="mx-auto mb-3 h-1.5 w-12 rounded-full bg-obs-l-border dark:bg-obs-d-border"></div>
<div class="flex items-center justify-between">
<h2 class="text-base font-semibold text-obs-l-text-main dark:text-obs-d-text-main">Navigation</h2>
<button
class="rounded-lg p-2 text-obs-l-text-muted transition hover:bg-obs-l-bg-main/70 dark:text-obs-d-text-muted dark:hover:bg-obs-d-bg-main/60"
(click)="closeOutlinePanel()"
aria-label="Fermer le calendrier"
>
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
</button>
</div>
<p class="mt-1 text-xs uppercase tracking-wide text-obs-l-text-muted dark:text-obs-d-text-muted">Table des matières & calendrier</p>
</div>
}
<div class="hidden border-b border-obs-l-border px-4 py-3 text-sm font-semibold uppercase tracking-wide text-obs-l-text-muted dark:border-obs-d-border dark:text-obs-d-text-muted lg:block">
Outline
</div>
<div class="flex-1 overflow-y-auto px-4 py-4">
@if (tableOfContents().length > 0) {
<ul class="space-y-2">
@for (entry of tableOfContents(); track entry.id) {
<li [style.padding-left.rem]="(entry.level - 1) * 0.75" class="flex items-start gap-2 text-sm text-obs-l-text-muted dark:text-obs-d-text-muted">
<svg xmlns="http://www.w3.org/2000/svg" class="mt-1 h-3 w-3 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 20l4-16m2 16l4-16M6 9h14M4 15h14" /></svg>
<a (click)="scrollToHeading(entry.id)" class="cursor-pointer leading-tight transition hover:text-obs-l-text-main dark:hover:text-obs-d-text-main">
{{ entry.text }}
</a>
</li>
}
</ul>
} @else {
<p class="text-sm italic text-obs-l-text-muted dark:text-obs-d-text-muted">Aucun titre dans cette note.</p>
}
</div>
<div class="border-t border-obs-l-border bg-obs-l-bg-secondary/60 p-4 dark:border-obs-d-border dark:bg-obs-d-bg-secondary/60">
<app-markdown-calendar
(fileSelected)="selectNote($event)"
(searchResultsChange)="onCalendarResultsChange($event)"
(searchStateChange)="onCalendarSearchStateChange($event)"
(searchErrorChange)="onCalendarSearchErrorChange($event)"
(selectionSummaryChange)="onCalendarSelectionSummaryChange($event)"
(requestSearchPanel)="onCalendarRequestSearchPanel()"
></app-markdown-calendar>
</div>
</div>
</aside>
}
</main>