feat: recent files type filter + calendar middle-click + calendar spacing
Recent files: - Type filter bar with pill tags above the list - Scans frontmatter 'type' property of each recent file - Click a pill to filter, click again or 'all' to clear - Active pill shows accent color Calendar: - Middle-click on day/week/quarter/month/year opens in new tab - openPeriodNoteInLeaf() method for leaf-targeted opening - More spacing: taller day cells, more padding, wider margins Bookmarks: - Right-click on recent file → 'Add to bookmarks' option
This commit is contained in:
@@ -63,6 +63,7 @@ export class WaypointView extends ItemView {
|
|||||||
private currentDisplayMonth: number = moment().month(); // 0-indexed
|
private currentDisplayMonth: number = moment().month(); // 0-indexed
|
||||||
private currentDisplayYear: number = moment().year();
|
private currentDisplayYear: number = moment().year();
|
||||||
private dragId: string | null = null;
|
private dragId: string | null = null;
|
||||||
|
private recentFilesFilter: string | null = null; // null = show all, else filter by type
|
||||||
|
|
||||||
private renderCalendar(): void {
|
private renderCalendar(): void {
|
||||||
const section = this.contentEl.createDiv({ cls: 'waypoint-section' });
|
const section = this.contentEl.createDiv({ cls: 'waypoint-section' });
|
||||||
@@ -230,17 +231,62 @@ export class WaypointView extends ItemView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const openFile = this.app.workspace.getActiveFile();
|
// ── Type filter bar ──
|
||||||
|
const typeCounts: Record<string, number> = {};
|
||||||
|
for (const file of this.plugin.recentFiles) {
|
||||||
|
const tfile = this.app.vault.getAbstractFileByPath(file.path);
|
||||||
|
if (tfile instanceof TFile) {
|
||||||
|
const cache = this.app.metadataCache.getFileCache(tfile);
|
||||||
|
const type = cache?.frontmatter?.type;
|
||||||
|
if (type && typeof type === 'string') {
|
||||||
|
typeCounts[type] = (typeCounts[type] || 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(typeCounts).length > 0) {
|
||||||
|
const filterBar = section.createDiv({ cls: 'waypoint-recent-filter' });
|
||||||
|
// "All" pill
|
||||||
|
const allPill = filterBar.createSpan({ cls: `waypoint-recent-pill${!this.recentFilesFilter ? ' is-active' : ''}`, text: 'all' });
|
||||||
|
allPill.addEventListener('click', () => {
|
||||||
|
this.recentFilesFilter = null;
|
||||||
|
this.redraw();
|
||||||
|
});
|
||||||
|
// Type pills
|
||||||
|
const sortedTypes = Object.entries(typeCounts).sort(([,a], [,b]) => b - a);
|
||||||
|
for (const [type, count] of sortedTypes) {
|
||||||
|
const pill = filterBar.createSpan({ cls: `waypoint-recent-pill${this.recentFilesFilter === type ? ' is-active' : ''}` });
|
||||||
|
pill.setText(`${type} ${count}`);
|
||||||
|
pill.addEventListener('click', () => {
|
||||||
|
this.recentFilesFilter = this.recentFilesFilter === type ? null : type;
|
||||||
|
this.redraw();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Filter the file list ──
|
||||||
|
let filteredFiles = this.plugin.recentFiles;
|
||||||
|
if (this.recentFilesFilter) {
|
||||||
|
filteredFiles = this.plugin.recentFiles.filter(file => {
|
||||||
|
const tfile = this.app.vault.getAbstractFileByPath(file.path);
|
||||||
|
if (tfile instanceof TFile) {
|
||||||
|
const cache = this.app.metadataCache.getFileCache(tfile);
|
||||||
|
const type = cache?.frontmatter?.type;
|
||||||
|
return type === this.recentFilesFilter;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const openFile = this.app.workspace.getActiveFile();
|
||||||
const rootEl = section.createDiv({ cls: 'nav-folder mod-root' });
|
const rootEl = section.createDiv({ cls: 'nav-folder mod-root' });
|
||||||
const childrenEl = rootEl.createDiv({ cls: 'nav-folder-children' });
|
const childrenEl = rootEl.createDiv({ cls: 'nav-folder-children' });
|
||||||
|
|
||||||
for (const file of this.plugin.recentFiles) {
|
for (const file of filteredFiles) {
|
||||||
const navFile = childrenEl.createDiv({ cls: 'tree-item nav-file' });
|
const navFile = childrenEl.createDiv({ cls: 'tree-item nav-file' });
|
||||||
const navFileTitle = navFile.createDiv({ cls: 'tree-item-self is-clickable nav-file-title' });
|
const navFileTitle = navFile.createDiv({ cls: 'tree-item-self is-clickable nav-file-title' });
|
||||||
const navFileTitleContent = navFileTitle.createDiv({ cls: 'tree-item-inner nav-file-title-content' });
|
const navFileTitleContent = navFileTitle.createDiv({ cls: 'tree-item-inner nav-file-title-content' });
|
||||||
navFileTitleContent.setText(file.basename);
|
navFileTitleContent.setText(file.basename);
|
||||||
|
|
||||||
const spacer = navFileTitle.createDiv({ cls: 'tree-item-spacer' });
|
const spacer = navFileTitle.createDiv({ cls: 'tree-item-spacer' });
|
||||||
|
|
||||||
// Remove button
|
// Remove button
|
||||||
@@ -252,9 +298,7 @@ export class WaypointView extends ItemView {
|
|||||||
this.plugin.saveSettings();
|
this.plugin.saveSettings();
|
||||||
this.redraw();
|
this.redraw();
|
||||||
});
|
});
|
||||||
|
|
||||||
setTooltip(navFile, file.path);
|
setTooltip(navFile, file.path);
|
||||||
|
|
||||||
if (openFile && file.path === openFile.path) {
|
if (openFile && file.path === openFile.path) {
|
||||||
navFileTitle.addClass('is-active');
|
navFileTitle.addClass('is-active');
|
||||||
}
|
}
|
||||||
@@ -306,7 +350,8 @@ export class WaypointView extends ItemView {
|
|||||||
}
|
}
|
||||||
menu.showAtPosition({ x: event.clientX, y: event.clientY });
|
menu.showAtPosition({ x: event.clientX, y: event.clientY });
|
||||||
});
|
});
|
||||||
// Left click
|
|
||||||
|
// Left click
|
||||||
navFileTitle.addEventListener('click', (event: MouseEvent) => {
|
navFileTitle.addEventListener('click', (event: MouseEvent) => {
|
||||||
const newLeaf = Keymap.isModEvent(event);
|
const newLeaf = Keymap.isModEvent(event);
|
||||||
this.focusFile(file, newLeaf);
|
this.focusFile(file, newLeaf);
|
||||||
@@ -322,7 +367,7 @@ export class WaypointView extends ItemView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private focusFile(file: { path: string; basename: string }, newLeaf: boolean | string | 'split'): void {
|
private focusFile(file: { path: string; basename: string }, newLeaf: boolean | string | 'split'): void {
|
||||||
const targetFile = this.app.vault.getFiles().find(f => f.path === file.path);
|
const targetFile = this.app.vault.getFiles().find(f => f.path === file.path);
|
||||||
if (targetFile) {
|
if (targetFile) {
|
||||||
const leaf = this.app.workspace.getLeaf(newLeaf as any);
|
const leaf = this.app.workspace.getLeaf(newLeaf as any);
|
||||||
|
|||||||
+28
@@ -189,6 +189,34 @@
|
|||||||
}
|
}
|
||||||
/* ── Recent Files ── */
|
/* ── Recent Files ── */
|
||||||
|
|
||||||
|
.waypoint-recent-filter {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waypoint-recent-pill {
|
||||||
|
font-size: var(--font-ui-smaller);
|
||||||
|
color: var(--text-muted);
|
||||||
|
background: var(--background-modifier-hover);
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: var(--cursor);
|
||||||
|
transition: color 80ms, background 80ms;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.waypoint-recent-pill:hover {
|
||||||
|
color: var(--text-normal);
|
||||||
|
background: var(--background-modifier-active-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.waypoint-recent-pill.is-active {
|
||||||
|
color: var(--text-on-accent);
|
||||||
|
background: var(--interactive-accent);
|
||||||
|
}
|
||||||
|
|
||||||
.waypoint-recent-files .nav-file {
|
.waypoint-recent-files .nav-file {
|
||||||
padding: 2px 0;
|
padding: 2px 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user