Skip to content

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 ​

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>

Set searchable when the page has many filters. The search filters the list by label.

vue
<FTFilterMenu v-model:open="open" :filters="filters" searchable @update="update" />
<FTFilterMenu v-model:open="open" :filters="filters" searchable @update="update" />

Behaviour ​

ActionResult
Click or Enter on the buttonThe 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 filterThe panel shows the values of that filter
Enter, Space or click on the back rowThe panel shows every filter again
Tick a valueupdate(id, values); the panel stays open
Clearupdate(id, []); shows only when the filter has values
Pin toggle (with pinnable)toggle-pin(id)
EscapeThe panel closes and the focus returns to the button
Click outside, or Tab outThe panel closes

Props ​

PropTypeDefaultDescription
filtersFTFilterMenuFilter[]requiredEvery filter, with its options and selected values
open (v-model)booleanfalseWhether the panel is open
pinnedstring[][]Ids of the pinned filters
pinnablebooleanfalseShows pin markers and the pin toggle
startAtstring | nullnullOpens on the values of this filter
onlystring | nullnullSingle-filter mode: only this filter's values, no list, no search, no back row. For a pinned filter chip (see FTFilterBar)
labelstring'Filter'Accessible name of the button and the panel
disabledbooleanfalseThe button cannot open the panel
searchablebooleanfalseShows a search above the list of filters. Use it when the page has many filters
searchPlaceholder, allFiltersLabel, noMatchText, clearLabel, backLabel, pinLabel, unpinLabel, pinnedLabelstringEnglish textTranslated labels

FTFilterMenuFilter ​

FieldTypeDescription
idstringStable identity
labelstringVisible name
iconstringOptional FontAwesome icon
optionsFTFilterMenuOption[]{ value, label, color?, logoUrl? }
values(string | number)[]The selected values

Events ​

EventPayloadDescription
update:openbooleanThe panel opens or closes
updateid, valuesA selection change request
toggle-pinidA pin change request

Slots ​

SlotPropsDescription
triggeropen, toggle, activeCount, attrsReplaces the button. Bind attrs to your element: it carries the ARIA attributes.

CSS custom properties ​

PropertyDefault
--ft-filter-menu-min-widthcalc(var(--spacing-3xl) * 4): the smallest panel width; the panel fits its content
--ft-filter-menu-widthcalc(var(--spacing-3xl) * 6): the largest panel width
--ft-filter-menu-bgvar(--color-mono-white)
--ft-filter-menu-bordervar(--color-mono-300)
--ft-filter-menu-row-hovervar(--color-mono-200)
--ft-filter-menu-badge-bgvar(--color-brand-blue-100)
--ft-filter-menu-badge-fgvar(--color-brand-blue-600)