From 5de84f135fd727f959b243b560836dcf9ecf6fbe Mon Sep 17 00:00:00 2001 From: Julien Calixte Date: Sat, 13 Jun 2026 21:42:04 +0200 Subject: [PATCH] feat(todo): add priority filter and stack filter rows inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a Priorities filter column with toggleable (A)/(B)/(C)/(D)/no-priority chips. Reflows the filter section from a two-column grid into single-line strips so each filter type (Priorities · Projects · Contexts) reads on one row with a fixed-width label and wrap-only-on-overflow chips. --- src/views/TodoNotes.vue | 94 ++++++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 15 deletions(-) diff --git a/src/views/TodoNotes.vue b/src/views/TodoNotes.vue index da74d0d..9203bf0 100644 --- a/src/views/TodoNotes.vue +++ b/src/views/TodoNotes.vue @@ -89,8 +89,23 @@ const allContexts = computed(() => { return Array.from(set).sort() }) +// "none" represents tasks without a priority. +const allPriorities = computed>(() => { + const set = new Set() + items.value.forEach((line) => { + if (isBlank(line)) return + set.add(line.priority ?? "none") + }) + return Array.from(set).sort((a, b) => { + if (a === "none") return 1 + if (b === "none") return -1 + return a.localeCompare(b) + }) +}) + const activeProjects = ref>(new Set()) const activeContexts = ref>(new Set()) +const activePriorities = ref>(new Set()) const toggleProject = (p: string) => { const next = new Set(activeProjects.value) @@ -106,15 +121,39 @@ const toggleContext = (c: string) => { activeContexts.value = next } +const togglePriority = (p: string | "none") => { + const next = new Set(activePriorities.value) + if (next.has(p)) next.delete(p) + else next.add(p) + activePriorities.value = next +} + const clearFilters = () => { activeProjects.value = new Set() activeContexts.value = new Set() + activePriorities.value = new Set() } const hasFilters = computed( - () => activeProjects.value.size > 0 || activeContexts.value.size > 0 + () => + activeProjects.value.size > 0 || + activeContexts.value.size > 0 || + activePriorities.value.size > 0 ) +const priorityBadgeFilterClass = (p: string | "none"): string => { + switch (p) { + case "A": + return "badge-error" + case "B": + return "badge-warning" + case "C": + return "badge-info" + default: + return "badge-ghost" + } +} + const filteredEntries = computed(() => { if (!hasFilters.value) return taskEntries.value return taskEntries.value.filter(({ line }) => { @@ -126,7 +165,10 @@ const filteredEntries = computed(() => { const contextsOk = activeContexts.value.size === 0 || taskContexts.some((c) => activeContexts.value.has(c)) - return projectsOk && contextsOk + const prioritiesOk = + activePriorities.value.size === 0 || + activePriorities.value.has(line.priority ?? "none") + return projectsOk && contextsOk && prioritiesOk }) }) @@ -229,10 +271,33 @@ const createTodoFile = async () => {
+
+

Priorities

+
+ +
+
{ } .todo-filter-columns { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; + display: flex; + flex-direction: column; + gap: 0.35rem; } .todo-filter-column { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.4rem; min-width: 0; - text-align: center; } .todo-filter-label { font-size: 0.8rem; opacity: 0.7; - margin: 0 0 0.35rem; + margin: 0; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; - text-align: center; + flex: 0 0 auto; + min-width: 5.5rem; } .todo-filter-chips { display: flex; flex-wrap: wrap; gap: 0.35rem; - justify-content: center; + flex: 1 1 auto; } .todo-filter-chip { cursor: pointer; } - @media (max-width: 480px) { - .todo-filter-columns { - grid-template-columns: 1fr; - } - } }