import { Component, EventEmitter, Output, computed, signal, effect } from '@angular/core'; import { input } from '@angular/core'; import { CommonModule } from '@angular/common'; import type { Note } from '../../../types'; import { ScrollableOverlayDirective } from '../../shared/overlay-scrollbar/scrollable-overlay.directive'; @Component({ selector: 'app-notes-list', standalone: true, imports: [CommonModule, ScrollableOverlayDirective], template: `
`, styles: [` :host { display: block; height: 100%; min-height: 0; /* critical for nested flex scrolling */ } /* Smooth, bounded vertical scrolling only on the list area */ .list-scroll { overscroll-behavior: contain; /* prevent parent scroll chaining */ -webkit-overflow-scrolling: touch; /* momentum scrolling on iOS */ scroll-behavior: smooth; /* smooth programmatic scrolls */ scrollbar-gutter: stable both-edges; /* avoid layout shift when scrollbar shows */ max-height: 100%; /* cap to available space within the central section */ contain: content; /* small perf win for large lists */ } `] }) export class NotesListComponent { notes = input([]); folderFilter = input(null); // like "folder/subfolder" query = input(''); tagFilter = input(null); @Output() openNote = new EventEmitter(); @Output() queryChange = new EventEmitter(); private q = signal(''); private syncQuery = effect(() => { this.q.set(this.query() || ''); }); filtered = computed(() => { const q = (this.q() || '').toLowerCase().trim(); const folder = (this.folderFilter() || '').toLowerCase(); const tag = (this.tagFilter() || '').toLowerCase(); let list = this.notes(); if (folder) { list = list.filter(n => (n.originalPath || '').toLowerCase().startsWith(folder)); } if (tag) { list = list.filter(n => Array.isArray(n.tags) && n.tags.some(t => (t || '').toLowerCase() === tag)); } // Apply query if present if (q) { list = list.filter(n => { const title = (n.title || '').toLowerCase(); const filePath = (n.filePath || '').toLowerCase(); return title.includes(q) || filePath.includes(q); }); } // Sort by most recent first (mtime desc; fallback updatedAt/createdAt) const parseDate = (s?: string) => (s ? Date.parse(s) : 0) || 0; const score = (n: Note) => n.mtime || parseDate(n.updatedAt) || parseDate(n.createdAt) || 0; return [...list].sort((a, b) => (score(b) - score(a))); }); onQuery(v: string) { this.q.set(v); this.queryChange.emit(v); } }