@phila/phila-ui-filter-chip
v1.1.1
Published
Filter chips for faceted filtering
Readme
FilterChip Component
Component Status
| Component | Status |
| --------------- | ------------------------------------------------------------------ |
| FilterChip | |
| FilterChipGroup |
|
@phila/phila-ui-filter-chip — pill-shaped filter controls for faceted filtering. A chip is a toggle when no choices are provided, or a dropdown (with deferred Apply/Reset) when choices is present.
Installation
pnpm add @phila/phila-ui-filter-chip @phila/phila-ui-core
# or
npm install @phila/phila-ui-filter-chip @phila/phila-ui-coreImport core styles in your main entry file (e.g., main.js|ts):
import "@phila/phila-ui-core/styles/template-light.css";Shared model
import type { FilterDefinition, FilterValues } from "@phila/phila-ui-core";
// Describes one filter axis.
interface FilterDefinition {
key: string; // unique key for FilterValues map
label: string; // chip label
choices?: FilterChoice[]; // present → dropdown; absent → toggle
multiple?: boolean; // dropdown only: checkbox (true) vs radio (false)
icon?: IconComponent;
iconColor?: string; // CSS color for the leading icon, independent of text color
excludeFromCount?: boolean; // leave out of the filter button's count (e.g. Sort)
showActions?: boolean; // dropdown only: show Reset/Apply (deferred). Default false = live apply.
}
// Holds the current value of every filter, keyed by FilterDefinition.key.
type FilterValues = Record<string, string | string[] | boolean>;FilterValues is the single object that represents your entire filter state. Toggle chips store a
boolean. In practice, dropdown filters (single- and multi-select alike) store a
Record<string, boolean> map of every choice value → selected — not the string/string[] the
type declares. The type is shared/aspirational; the runtime shape (confirmed in
FilterChip.vue/FilterChipGroup.vue) is always the full map. Treat the examples below, not the
FilterValues type signature, as the source of truth for dropdown filters.
FilterChip — toggle
<script setup lang="ts">
import { ref } from "vue";
import { FilterChip } from "@phila/phila-ui-filter-chip";
const openNow = ref(false);
</script>
<template>
<FilterChip text="Open Now" v-model:selected="openNow" />
</template>FilterChip — dropdown (single-select)
modelValue is a map of every choice's value → selected, not the selected value itself — same
shape whether multiple is true or false:
<script setup lang="ts">
import { ref } from "vue";
import { FilterChip } from "@phila/phila-ui-filter-chip";
import type { FilterChoice } from "@phila/phila-ui-core";
const districtChoices: FilterChoice[] = [
{ text: "Center City", value: "center-city" },
{ text: "North Philadelphia", value: "north-philly" },
{ text: "West Philadelphia", value: "west-philly" },
];
const district = ref<Record<string, boolean>>({});
</script>
<template>
<FilterChip label="District" :choices="districtChoices" v-model="district" />
</template>By default (showActions unset/false), selections apply live — update:modelValue fires on
every toggle and the panel stays open. Set show-actions to defer instead: the panel then shows
Reset/Apply buttons, and update:modelValue only fires when Apply is clicked (Reset clears the
value without closing).
FilterChip — dropdown (multi-select)
<script setup lang="ts">
const selectedNeighborhoods = ref<Record<string, boolean>>({});
</script>
<template>
<FilterChip
label="Neighborhoods"
:choices="neighborhoodChoices"
:multiple="true"
v-model="selectedNeighborhoods"
show-actions
/>
</template>FilterChipGroup — data-driven row
FilterChipGroup renders a row of chips from a filters array. The row never wraps: on hover-capable (mouse) devices, hovering reveals ‹/› buttons at the edges to scroll offscreen chips into view; on touch devices the row drag-scrolls instead (a drag suppresses the click that ends it).
With filterButton, a leading button (sliders icon + a count of total active selections) scrolls with the chips and emits open-filters when clicked — wire that to your app's own filter panel. A filter can opt out of the count with excludeFromCount (e.g. a Sort chip, which is ordering rather than filtering).
<script setup lang="ts">
import { ref } from "vue";
import { FilterChipGroup } from "@phila/phila-ui-filter-chip";
import type { FilterDefinition, FilterValues } from "@phila/phila-ui-core";
const filters: FilterDefinition[] = [
{ key: "openNow", label: "Open Now" }, // toggle
{ key: "accessible", label: "Accessible" }, // toggle
{
key: "district",
label: "District",
choices: [
{ text: "Center City", value: "center-city" },
{ text: "North Philadelphia", value: "north-philly" },
],
},
{
key: "amenities",
label: "Amenities",
multiple: true,
choices: [
{ text: "Parking", value: "parking" },
{ text: "Wi-Fi", value: "wifi" },
],
},
];
const values = ref<FilterValues>({});
</script>
<template>
<FilterChipGroup :filters="filters" v-model="values" />
</template>values updates whenever any chip is applied or toggled. Toggle filters store a plain boolean;
dropdown filters (single- or multi-select) store a full map of every choice → selected:
// Example shape after interaction:
{
openNow: true,
district: { "center-city": true, "north-philly": false },
amenities: { parking: true, wifi: true },
}FilterChipGroup props
| Prop | Type | Default | Description |
| ------------------ | -------------------- | ----------- | ------------------------------------------------------------------------------------------ |
| filters | FilterDefinition[] | — | Required. Ordered list of filter axes. |
| modelValue | FilterValues | {} | Current filter state (v-model). |
| label | string | — | Optional row label rendered above chips. |
| size | ComponentSize | "medium" | Chip size applied to all chips. |
| color | FilterChipColor | "blue" | Chip color applied to all chips. |
| filterButton | boolean | false | Show a leading sliders button with the active-selection count. |
| filterButtonText | string | "Filters" | Label for the leading filter button. The active-selection count is appended automatically. |
| applyText | string | "Apply" | Reset/Apply label forwarded to every chip's dropdown footer (for localization). |
| resetText | string | "Reset" | Reset/Apply label forwarded to every chip's dropdown footer (for localization). |
| elevated | boolean | false | Applies a drop-shadow to each chip (for floating the row over a map). |
FilterChipGroup events
| Event | Payload | Description |
| ------------------- | -------------- | ------------------------------------------------------------------ |
| update:modelValue | FilterValues | Next filter state when any chip is applied or toggled (v-model). |
| open-filters | — | The filterButton was clicked; open your app's filter panel. |
| dropdown-close | key: string | A chip's dropdown panel closed; payload is that filter's key. |
FilterChip props
| Prop | Type | Default | Description |
| -------------- | ------------------------- | ---------- | ------------------------------------------------------------------------------------------------------ |
| label | string | — | Chip label (also the group label in dropdown mode). |
| text | string | — | Alias of label for toggle mode. |
| size | ComponentSize | "medium" | Chip size. |
| color | FilterChipColor | "blue" | Chip color. |
| icon | IconComponent | — | Optional leading icon, from @phila/phila-ui-core/icons. |
| iconColor | string | — | CSS color for the leading icon, independent of the chip's text color. |
| trailingIcon | IconComponent | — | Toggle mode: optional trailing icon (dropdown mode always uses a chevron instead). |
| selected | boolean | false | Toggle mode: active state (v-model:selected). |
| choices | FilterChoice[] | — | Presence switches to dropdown mode. |
| multiple | boolean | false | Dropdown mode: multi-select (checkbox) vs single (radio). |
| modelValue | Record<string, boolean> | — | Dropdown mode: map of every choice's value → selected (v-model) — see Shared model. |
| showActions | boolean | false | Dropdown mode: show Reset/Apply buttons (deferred). Default false applies live on every toggle. |
| applyText | string | "Apply" | Label for the Apply button. |
| resetText | string | "Reset" | Label for the Reset button. |
A toggle chip with an icon but no label/text renders the icon centered as the chip's content (icon-only), rather than as a leading icon beside an empty label. Chip icons are a fixed size regardless of chip size; only the text scales.
FilterChip events
| Event | Payload | Description |
| ------------------- | ------------------------- | ------------------------------------------------------------------------------------- |
| update:selected | boolean | Toggle mode: fired on every click. |
| update:modelValue | Record<string, boolean> | Dropdown mode: fired live on every toggle, or only on Apply if showActions is true. |
| click | MouseEvent | Toggle mode: the raw click event. |
| close | — | Dropdown mode: fired when the dropdown panel closes. |
Building a custom filter panel
FilterDefinition and FilterValues are the same model that FilterChipGroup uses internally. An app can bind its own summary panel or drawer to the same values ref — the chip row and the panel are two views over one piece of state. No generic aggregating panel ships in this package.
Development
Install Dependencies
pnpm installRun Demo
pnpm devRun lint
pnpm lintBuild Library
pnpm buildType Check
pnpm type-checkPublishing to NPM
Follow the release instructions using changesets.
License
MIT
