feat(manager): favorites filter as chip toggle box in filter panel

Move the favorites filter from a per-view checkbox field into a built-in
"Quick filters" chip section in ModuleListView, styled identically to the
extra-filter syntax chips (wp-chip--toggle). Clicking toggles the filled
accent-tinted box state. Removes duplicated wp-fav-toggle label+Checkbox
blocks from all six list views and cleans up the now-unused Checkbox imports.
Updates the ModuleListView test to assert both the favorites chip and any
extra-filter chips are present in the panel.
This commit is contained in:
DumiFlex
2026-04-27 23:31:17 +03:00
parent e594bc2adf
commit f51b752fd7
9 changed files with 25 additions and 72 deletions
+5 -3
View File
@@ -119,9 +119,11 @@ describe("ModuleListView.vue", () => {
await filtersBtn.trigger("click");
await flushPromises();
expect(wrap.find(".wp-filter-panel").exists()).toBe(true);
// Extra filter chip is rendered inside.
expect(wrap.find(".wp-chip--toggle").exists()).toBe(true);
expect(wrap.find(".wp-chip--toggle").text()).toContain("Favorited");
// Favorites quick-filter chip and extra-filter chips are both rendered.
const toggleChips = wrap.findAll(".wp-chip--toggle");
expect(toggleChips.length).toBeGreaterThanOrEqual(2);
expect(toggleChips.some((c) => c.text().includes("Favorites only"))).toBe(true);
expect(toggleChips.some((c) => c.text().includes("Favorited"))).toBe(true);
});
it("paginates 20 items into pages of 10 then 10 when pageSize=10", async () => {
+13
View File
@@ -354,6 +354,19 @@ defineExpose({
<Transition name="filter-collapse">
<div v-if="filtersOpen" class="wp-filter-panel">
<slot name="filter-panel" :filter="filter" :emit-fetch="emitFetch" />
<div v-if="showFavorite" class="wp-filter-panel__extra">
<span class="wp-filter-panel__extra-label">Quick filters</span>
<div class="wp-filter-panel__extra-chips">
<button
type="button"
class="wp-chip wp-chip--toggle"
:data-active="filter.favorites ? '' : null"
@click="filter.favorites = !filter.favorites; emitFetch()"
>
★ Favorites only
</button>
</div>
</div>
<div v-if="extraFilters?.length" class="wp-filter-panel__extra">
<span class="wp-filter-panel__extra-label">Syntax</span>
<div class="wp-filter-panel__extra-chips">
+1 -11
View File
@@ -4,7 +4,6 @@ import { useRouter } from "vue-router";
import { useToast } from "../composables/useToast";
import ModuleListView from "../components/ModuleListView.vue";
import Button from "../components/ui/Button.vue";
import Checkbox from "../components/ui/Checkbox.vue";
import Select from "../components/ui/Select.vue";
import { useModuleStore } from "../stores/moduleStore";
import { catChipStyle } from "../utils/catChip";
@@ -152,16 +151,7 @@ function templateParts(row: ModuleRow): TemplatePart[] {
@update:model-value="(v) => { filter.category = v as string | null; emitFetch(); }"
/>
</div>
<div class="wp-field">
<label class="wp-field__label">Favorites</label>
<label class="wp-fav-toggle">
<Checkbox
:model-value="!!filter.favorites"
@update:model-value="(v) => { filter.favorites = v; emitFetch(); }"
/>
<span>Favorites only</span>
</label>
</div>
<div class="wp-field wp-field--full">
<label class="wp-field__label">
Tags{{ filter.tags?.length ? ` (${filter.tags.length})` : "" }}
+1 -11
View File
@@ -4,7 +4,6 @@ import { useRouter } from "vue-router";
import { useToast } from "../composables/useToast";
import ModuleListView from "../components/ModuleListView.vue";
import Button from "../components/ui/Button.vue";
import Checkbox from "../components/ui/Checkbox.vue";
import Select from "../components/ui/Select.vue";
import { useModuleStore } from "../stores/moduleStore";
import { catChipStyle } from "../utils/catChip";
@@ -188,16 +187,7 @@ function formatFactor(f: number): string {
@update:model-value="(v) => { filter.category = v as string | null; emitFetch(); }"
/>
</div>
<div class="wp-field">
<label class="wp-field__label">Favorites</label>
<label class="wp-fav-toggle">
<Checkbox
:model-value="!!filter.favorites"
@update:model-value="(v) => { filter.favorites = v; emitFetch(); }"
/>
<span>Favorites only</span>
</label>
</div>
<div class="wp-field wp-field--full">
<label class="wp-field__label">
Tags{{ filter.tags?.length ? ` (${filter.tags.length})` : "" }}
+1 -11
View File
@@ -4,7 +4,6 @@ import { useRouter } from "vue-router";
import { useToast } from "../composables/useToast";
import ModuleListView from "../components/ModuleListView.vue";
import Button from "../components/ui/Button.vue";
import Checkbox from "../components/ui/Checkbox.vue";
import Select from "../components/ui/Select.vue";
import { useModuleStore } from "../stores/moduleStore";
import { catChipStyle } from "../utils/catChip";
@@ -152,16 +151,7 @@ function actView(a: DerivationAction | undefined): { verb: string; target: strin
@update:model-value="(v) => { filter.category = v as string | null; emitFetch(); }"
/>
</div>
<div class="wp-field">
<label class="wp-field__label">Favorites</label>
<label class="wp-fav-toggle">
<Checkbox
:model-value="!!filter.favorites"
@update:model-value="(v) => { filter.favorites = v; emitFetch(); }"
/>
<span>Favorites only</span>
</label>
</div>
<div class="wp-field wp-field--full">
<label class="wp-field__label">
Tags{{ filter.tags?.length ? ` (${filter.tags.length})` : "" }}
+1 -11
View File
@@ -4,7 +4,6 @@ import { useRouter } from "vue-router";
import { useToast } from "../composables/useToast";
import ModuleListView from "../components/ModuleListView.vue";
import Button from "../components/ui/Button.vue";
import Checkbox from "../components/ui/Checkbox.vue";
import Select from "../components/ui/Select.vue";
import { useModuleStore } from "../stores/moduleStore";
import { catChipStyle } from "../utils/catChip";
@@ -128,16 +127,7 @@ function topValues(row: ModuleRow): NamedValue[] { return values(row).slice(0, 4
@update:model-value="(v) => { filter.category = v as string | null; emitFetch(); }"
/>
</div>
<div class="wp-field">
<label class="wp-field__label">Favorites</label>
<label class="wp-fav-toggle">
<Checkbox
:model-value="!!filter.favorites"
@update:model-value="(v) => { filter.favorites = v; emitFetch(); }"
/>
<span>Favorites only</span>
</label>
</div>
<div class="wp-field wp-field--full">
<label class="wp-field__label">
Tags{{ filter.tags?.length ? ` (${filter.tags.length})` : "" }}
+1 -11
View File
@@ -4,7 +4,6 @@ import { useRouter } from "vue-router";
import { useToast } from "../composables/useToast";
import ModuleListView from "../components/ModuleListView.vue";
import Button from "../components/ui/Button.vue";
import Checkbox from "../components/ui/Checkbox.vue";
import Select from "../components/ui/Select.vue";
import { useModuleStore } from "../stores/moduleStore";
import { catChipStyle } from "../utils/catChip";
@@ -198,16 +197,7 @@ function pad2(n: number): string { return String(n + 1).padStart(2, "0"); }
@update:model-value="(v) => { filter.category = v as string | null; emitFetch(); }"
/>
</div>
<div class="wp-field">
<label class="wp-field__label">Favorites</label>
<label class="wp-fav-toggle">
<Checkbox
:model-value="!!filter.favorites"
@update:model-value="(v) => { filter.favorites = v; emitFetch(); }"
/>
<span>Favorites only</span>
</label>
</div>
<div class="wp-field wp-field--full">
<label class="wp-field__label">
Tags{{ filter.tags?.length ? ` (${filter.tags.length})` : "" }}
+1 -13
View File
@@ -5,7 +5,6 @@ import { useToast } from "../composables/useToast";
import ModuleListView from "../components/ModuleListView.vue";
import RichTextPreview from "../components/RichTextPreview.vue";
import Button from "../components/ui/Button.vue";
import Checkbox from "../components/ui/Checkbox.vue";
import Select from "../components/ui/Select.vue";
import { useModuleStore } from "../stores/moduleStore";
import { catChipStyle } from "../utils/catChip";
@@ -188,18 +187,7 @@ function isValid(row: ModuleRow): boolean {
@update:model-value="(v) => { filter.category = v as string | null; emitFetch(); }"
/>
</div>
<div class="wp-field">
<label class="wp-field__label">
Favorites
</label>
<label class="wp-fav-toggle">
<Checkbox
:model-value="!!filter.favorites"
@update:model-value="(v) => { filter.favorites = v; emitFetch(); }"
/>
<span>Favorites only</span>
</label>
</div>
<div class="wp-field wp-field--full">
<label class="wp-field__label">
Tags{{ filter.tags?.length ? ` (${filter.tags.length})` : "" }}
+1 -1
View File
@@ -7,7 +7,7 @@
<link rel="icon" type="image/svg+xml" href="/wp/images/favicon.svg" />
<link rel="apple-touch-icon" href="/wp/images/apple-touch-icon.png" />
<link rel="manifest" href="/wp/images/site.webmanifest" />
<script type="module" crossorigin src="/wp/assets/manager-D8EREGee.js"></script>
<script type="module" crossorigin src="/wp/assets/manager-baYgQ7LG.js"></script>
<link rel="stylesheet" crossorigin href="/wp/assets/manager-C6od6CgI.css">
</head>
<body>