Components/FilterChips

FilterChips

A single-select filter row of chips with leading dots and counts, usable with toolbar or tablist semantics.

VerifiedSince 0.3.9

FilterChips

Basic Usage

FilterChips

Status filter

Counts are derived from real data rather than typed in as literals.

Loading demo...

Composing With A Table

The chip row only reports which option is selected; the host does the filtering — which keeps the counts, the row data, and the table on one source of truth.

Filter chips with a data table

Counts derive from the row data, and the date column sorts by timestamp rather than by the rendered month text.

Loading demo...

API

Props

PropTypeDefaultDescription
modelValuestring | number-Currently selected chip
itemsFilterChipItem[][]The chips
disabledbooleanfalseDisables the whole row
role'toolbar' | 'tablist''toolbar'ARIA shape; see Interaction Contract
indicatorbooleantruePaints the active fill as one element that slides between chips; off returns it to each chip
iconOnlybooleanfalseDraws only iconClass, moving label to aria-label and title; a chip without an icon keeps its text
ariaLabelstring'Filters'Accessible name for the chip row

FilterChipItem

FieldTypeDescription
valuestring | numberChip identity
labelstringChip text
iconClassstringLeading icon class, drawn before the dot and the label; omit to render none
dotstringLeading dot colour; omit to render no dot
countnumberTrailing count badge; omit to render none. Derive it from your data
disabledbooleanDisables this chip alone

Events

EventPayloadDescription
update:modelValue(value)Selection changed
change(value)Selection changed, emitted alongside the above

Slots

NameDescription
chipReplaces a chip's inner content; receives { item, active }. The button shell and keyboard behaviour stay with the component

Interaction Contract

  • The row is a single tab stop: focus lands on the selected chip (or the first enabled one when nothing is selected), and arrow keys move between chips from there.
  • Arrow navigation wraps and skips disabled chips; Home and End jump to the ends.
  • With role="toolbar" (the default) chips are aria-pressed toggles and moving focus does not change the filter — activation is explicit.
  • With role="tablist" chips become role="tab" with aria-selected and selection follows focus; the host should render the matching role="tabpanel" region.
  • Clicking the already-selected chip emits nothing.
  • indicator (on by default) paints the active fill as one element and moves it to the new chip. The resting look is pixel for pixel what it is with the indicator off; only the travel between two chips differs. Off, the fill returns to each chip.
  • The slider is an absolutely positioned child of the scrolling row, so it scrolls sideways with the chips — it measures a chip's offsetLeft / offsetTop, not viewport coordinates.
  • A position nobody asked to see travelled to is not tweened: the first paint, a rebuilt chip list (a host refetching its options), and any resize — a webfont landing widens the chips — all land rather than slide.
  • With nothing selected the slider is not drawn, rather than parked on chip zero.
  • Under iconOnly each chip squares up to a 30px slot (the pill's padding was sized for a word, and a lone glyph would float in the oval) and its label moves to aria-label and title, so assistive tech and hover still get the name. A chip without an iconClass is left alone and keeps its text — a blank chip is worse than a wordy one.
  • Under prefers-reduced-motion: reduce the slider lands without a tween.
  • The no-travel guard lifts only on the frame after the new box has been written to the DOM. Lifting it in the same frame re-arms the transition mid-write and the width tweens up from 0, painting a 0-wide fill on first paint — an ordering bug that shipped once, now pinned by filter-chips.test.ts.
  • Counts come from the host: the component cannot see your data and never infers them.

Best Practices

  • Always compute counts from the row data. A hardcoded literal starts lying the moment a row is added.
  • Keep the filtering in the host: the chips only say which option is selected, and filtered-out rows should genuinely leave the data rather than being hidden with CSS — a hidden row is still in the accessibility tree and the tab order.
  • Keep the default role="toolbar" for plain list filtering; reach for tablist only when mutually exclusive panels actually exist.
  • Treat the dot as supplementary. Colour must never be the only carrier of state, so the label has to stand on its own.
  • Give every chip an iconClass once there are more than two or three: a row of bare words runs together into one sentence, and the icon is what gives each chip a leading edge. The icon is decorative (aria-hidden); naming stays with label.
  • Reach for iconOnly only when the glyphs are recognisable on their own (a star, a brand mark). Keep the words when the icons are abstract category symbols — a chip you can only name on hover is not one you can recognise.
  • Turn indicator off only when the host itself moves the chips during its own animation: two travels layered on each other fight. Not wanting motion is not a reason — the system's reduced motion setting already cuts the tween.

Source

  • Component source: packages/tuffex/packages/components/src/filter-chips/src/TxFilterChips.vue.
  • Types: packages/tuffex/packages/components/src/filter-chips/src/types.ts exports FilterChipsProps, FilterChipItem, FilterChipsRole, FilterChipValue, and FilterChipsEmits.
  • Test coverage: packages/tuffex/packages/components/src/filter-chips/__tests__/filter-chips.test.ts has 15 cases covering chip / dot / count rendering, both ARIA shapes, v-model and change emission, silence on re-clicking the active chip, roving tabindex including the no-selection fallback, arrow wrapping and disabled skipping, selection-follows-focus in tablist mode, the disabled state, the chip slot, and the active-fill slider; filter-chips-motion.test.ts adds 3 compiled-style contract cases.
查看源码
packages/tuffex/packages/components/src/filter-chips/index.ts