Appearance
Filter Menu
FTFilterMenu is the Filter button of a list page. It opens a panel with every filter the page offers. A search above the list is optional: set searchable to show it. A click on a filter shows its values; the user ticks the values to filter by. Below 640 px wide the panel is a bottom sheet.
The page owns the selected values: the menu requests changes with update, and the page passes the new values back. The menu never keeps its own copy.
Contract: src/components/FTFilterMenu/contract.ts (status: draft). History: contract-log.md.
Basic usage
1
vue
<script setup lang="ts">
import { ref } from 'vue';
import { FTFilterMenu } from '@fasttrack-solutions/vue-components-lib';
import type {
FTFilterMenuFilter,
FTFilterMenuValue,
} from '@fasttrack-solutions/vue-components-lib';
const open = ref(false);
const pinned = ref<string[]>(['status']);
const filters = ref<FTFilterMenuFilter[]>([
{
id: 'origin',
label: 'Origin',
icon: 'fas globe',
options: [{ value: 'north', label: 'Northstar Casino' }],
values: [],
},
{
id: 'status',
label: 'Status',
icon: 'fas circle-dot',
options: [{ value: 'prod', label: 'Prod', color: 'var(--color-secondary-green-500)' }],
values: [],
},
]);
const update = (id: string, values: FTFilterMenuValue[]) => {
filters.value = filters.value.map((f) => (f.id === id ? { ...f, values } : f));
};
const togglePin = (id: string) => {
pinned.value = pinned.value.includes(id)
? pinned.value.filter((p) => p !== id)
: [...pinned.value, id];
};
</script>
<template>
<FTFilterMenu
v-model:open="open"
:filters="filters"
:pinned="pinned"
pinnable
@update="update"
@toggle-pin="togglePin"
/>
</template><script setup lang="ts">
import { ref } from 'vue';
import { FTFilterMenu } from '@fasttrack-solutions/vue-components-lib';
import type {
FTFilterMenuFilter,
FTFilterMenuValue,
} from '@fasttrack-solutions/vue-components-lib';
const open = ref(false);
const pinned = ref<string[]>(['status']);
const filters = ref<FTFilterMenuFilter[]>([
{
id: 'origin',
label: 'Origin',
icon: 'fas globe',
options: [{ value: 'north', label: 'Northstar Casino' }],
values: [],
},
{
id: 'status',
label: 'Status',
icon: 'fas circle-dot',
options: [{ value: 'prod', label: 'Prod', color: 'var(--color-secondary-green-500)' }],
values: [],
},
]);
const update = (id: string, values: FTFilterMenuValue[]) => {
filters.value = filters.value.map((f) => (f.id === id ? { ...f, values } : f));
};
const togglePin = (id: string) => {
pinned.value = pinned.value.includes(id)
? pinned.value.filter((p) => p !== id)
: [...pinned.value, id];
};
</script>
<template>
<FTFilterMenu
v-model:open="open"
:filters="filters"
:pinned="pinned"
pinnable
@update="update"
@toggle-pin="togglePin"
/>
</template>With search
Set searchable when the page has many filters. The search filters the list by label.
1
vue
<FTFilterMenu v-model:open="open" :filters="filters" searchable @update="update" /><FTFilterMenu v-model:open="open" :filters="filters" searchable @update="update" />Behaviour
| Action | Result |
|---|---|
| Click or Enter on the button | The panel opens and the focus moves into it |
Type in the search (with searchable) | The list shows only the filters whose label contains the text |
| Enter, Space or click on a filter | The panel shows the values of that filter |
| Enter, Space or click on the back row | The panel shows every filter again |
| Tick a value | update(id, values); the panel stays open |
| Clear | update(id, []); shows only when the filter has values |
Pin toggle (with pinnable) | toggle-pin(id) |
| Escape | The panel closes and the focus returns to the button |
| Click outside, or Tab out | The panel closes |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
filters | FTFilterMenuFilter[] | required | Every filter, with its options and selected values |
open (v-model) | boolean | false | Whether the panel is open |
pinned | string[] | [] | Ids of the pinned filters |
pinnable | boolean | false | Shows pin markers and the pin toggle |
startAt | string | null | null | Opens on the values of this filter |
only | string | null | null | Single-filter mode: only this filter's values, no list, no search, no back row. For a pinned filter chip (see FTFilterBar) |
label | string | 'Filter' | Accessible name of the button and the panel |
disabled | boolean | false | The button cannot open the panel |
searchable | boolean | false | Shows a search above the list of filters. Use it when the page has many filters |
searchPlaceholder, allFiltersLabel, noMatchText, clearLabel, backLabel, pinLabel, unpinLabel, pinnedLabel | string | English text | Translated labels |
FTFilterMenuFilter
| Field | Type | Description |
|---|---|---|
id | string | Stable identity |
label | string | Visible name |
icon | string | Optional FontAwesome icon |
options | FTFilterMenuOption[] | { value, label, color?, logoUrl? } |
values | (string | number)[] | The selected values |
Events
| Event | Payload | Description |
|---|---|---|
update:open | boolean | The panel opens or closes |
update | id, values | A selection change request |
toggle-pin | id | A pin change request |
Slots
| Slot | Props | Description |
|---|---|---|
trigger | open, toggle, activeCount, attrs | Replaces the button. Bind attrs to your element: it carries the ARIA attributes. |
CSS custom properties
| Property | Default |
|---|---|
--ft-filter-menu-min-width | calc(var(--spacing-3xl) * 4): the smallest panel width; the panel fits its content |
--ft-filter-menu-width | calc(var(--spacing-3xl) * 6): the largest panel width |
--ft-filter-menu-bg | var(--color-mono-white) |
--ft-filter-menu-border | var(--color-mono-300) |
--ft-filter-menu-row-hover | var(--color-mono-200) |
--ft-filter-menu-badge-bg | var(--color-brand-blue-100) |
--ft-filter-menu-badge-fg | var(--color-brand-blue-600) |