PivotFilter
Horizontal or vertical strip of count-tagged pills used as a one-dimension quick filter above a grid, calendar or map, with refresh and a settings bottom sheet. API-aligned with the web PivotFilter.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-pivot-filterpnpm add react-native-reanimated react-native-gesture-handler tailwind-variantsPivotFilter only handles presentation. To connect it to a Docyrus data source, which covers the pivot query, pill items, date buckets, filter rules and settings content, use useDocyrusPivotFilter or DocyrusPivotFilterGroup for several strips that cross-filter each other.
Usage
import { useState } from 'react';
import { PivotFilter, type PivotFilterItem } from '@/components/docyrus-native/pivot-filter';
const items: PivotFilterItem[] = [
{ id: 'open', name: 'Open', stat: 24, color: 'sky', icon: 'fal circle' },
{ id: 'done', name: 'Done', stat: 1280, color: 'emerald', icon: 'fal circle-check' },
{ id: 'EMPTY', name: 'Not Set', stat: 3, isEmpty: true }
];
export function StatusFilter() {
const [selected, setSelected] = useState<string | null>(null);
return (
<PivotFilter
items={items}
total={1307}
selectedItemId={selected}
onSelect={item => setSelected(item?.id ?? null)}
onRefresh={() => refetch()} />
);
}Settings sheet
When you pass settingsContent, a gear button appears. Pressing it opens the content in a bottom sheet (ActionSheet, medium detent that expands to large). This replaces the web popover. The exported PivotFilterSettings panel holds the date-bucket and calculation controls that useDocyrusPivotFilter supplies on its own:
import { PivotFilter, PivotFilterSettings } from '@/components/docyrus-native/pivot-filter';
<PivotFilter
{...props}
settingsContent={(
<PivotFilterSettings
fieldType="date"
dateBucket={bucket}
onDateBucketChange={setBucket}
calculation={calculation}
onCalculationChange={setCalculation}
aggregateFields={[{ slug: 'budget', name: 'Budget' }]} />
)} />Mobile behaviour
- Horizontal (default): a horizontally scrolling strip without a scroll indicator. The selected pill is scrolled to the centre whenever the selection or the content changes. The refresh and gear buttons sit at the start of the strip.
- Vertical: a full-width bordered column. The selected row gets an accent background and a left accent bar, and the toolbar sits along the top edge. On phones, prefer horizontal strips.
- The accent colour comes from
item.color, a Tailwind family such assky, a token such asemerald-500, or a hex value, resolved withresolveColorHex. Users render withAvatar, icons withDocyrusIcon, and loading rendersSkeletonBasepills. - Stats of 1000 or more render as
1.3k, and non-integers render with 2 decimals.
API Reference
PivotFilterProps
| Prop | Type | Default | Description |
|---|---|---|---|
items | PivotFilterItem[] | — | Items rendered in the strip (required). |
selectedItemId | string | null | — | Selected item id; null = the "All" pill (required). |
onSelect | (item: PivotFilterItem | null) => void | — | Fired when a pill is pressed; "All" fires with null (required). |
total | number | — | Aggregate stat shown on the "All" pill. |
totalLabel | string | t('ui.pivotFilter.all', 'All') | Label of the "All" pill. |
loading | boolean | false | Render skeleton pills instead of items. |
vertical | boolean | false | Vertical column layout. |
compact | boolean | false | Pills shrink to their content width (no 96pt minimum). |
hideZeroValues | boolean | false | Hide items with stat === 0 (the selected item is always kept). isEmpty items with stat === 0 are always hidden. |
hideAllPill | boolean | false | Hide the "All" pill. |
onRefresh | () => void | — | Refresh button handler. When omitted, the button is hidden. |
settingsContent | ReactNode | — | Body of the settings bottom sheet. When omitted, the gear button is hidden. |
settingsOpen | boolean | — | Controlled sheet open state. |
onSettingsOpenChange | (open: boolean) => void | — | Sheet open-state handler. |
emptyState | ReactNode | — | Placeholder shown when there are no items and no "All" pill. |
className | string | — | Root View classes. |
ref | Ref<View> | — | Ref forwarded to the root View. |
PivotFilterItem
| Field | Type | Description |
|---|---|---|
id | string | Stable id, matched against selectedItemId. |
name | string | Pill label. |
stat | number | Value shown on the right of the pill. |
secondary | string | Muted secondary text after the name (e.g. 14 Sep). |
color | string | Tailwind colour family or token, or a hex value, for the selected accent. |
icon | string | DocyrusIcon identifier (e.g. fal star). |
user | PivotFilterUser | Renders an avatar instead of the icon. |
isEmpty | boolean | Synthetic "Not Set" bucket: muted, and hidden when its stat is 0. |
meta | Record<string, unknown> | Arbitrary payload passed back to onSelect. |
PivotFilterUser
| Field | Type | Description |
|---|---|---|
id | string | User id. |
name | string | Display name, also used for the initials fallback. |
picturePath | string | null | Avatar image URL. |
PivotFilterSettingsProps
| Prop | Type | Description |
|---|---|---|
fieldType | PivotFilterFieldType | The date-bucket radio grid is shown only for 'date'. |
dateBucket | PivotFilterDateBucket | Current bucket. |
onDateBucketChange | (next: PivotFilterDateBucket) => void | Bucket change handler. |
calculation | PivotFilterCalculation | null | Current calculation (null = COUNT of records). |
onCalculationChange | (next: PivotFilterCalculation | null) => void | Calculation change handler. |
aggregateFields | PivotFilterAggregateField[] | Numeric / money / duration fields offered as measures ({ slug, name }). |
The web panel uses two Selects for the aggregate function and the measure field. On native these are chip rows (SUM / AVG / MIN / MAX, then one chip per field), so a second sheet never opens on top of the settings sheet.
PivotFilterPillProps
| Prop | Type | Description |
|---|---|---|
item | PivotFilterItem | null | The item; null for the "All" pill. |
isAll | boolean | Renders the "All" pill. |
label | string | Pill label. |
stat | number | undefined | Stat value. |
selected | boolean | Selected state. |
vertical | boolean | Row style (vertical strip). |
compact | boolean | Compact width. |
onClick | () => void | Press handler (web name kept for API parity). |
ref | Ref<View> | Ref to the Pressable. |
Translation keys
The component reads these through useUiTranslation(), using the same keys as web: ui.pivotFilter.all, ui.pivotFilter.refresh, ui.pivotFilter.settings, ui.pivotFilter.empty. The settings panel uses ui.pivotFilter.dateFilterType, ui.pivotFilter.calculation, ui.pivotFilter.countOfRecords, ui.pivotFilter.function, ui.pivotFilter.field, ui.pivotFilter.noNumericFields, plus ui.pivotFilter.bucket.hoursOfToday | daysOfWeek | daysOfMonth | weeksOfMonth | weeksOfQuarter | monthsOfQuarter | monthsOfYear | quartersOfYear.
Components
| Component | Description |
|---|---|
PivotFilter | The strip (horizontal scroll or vertical column) with toolbar and settings sheet. |
PivotFilterPill | A single pill or row. |
PivotFilterSettings | Date-bucket and calculation controls for the settings sheet. |
Type Exports
| Type | Description |
|---|---|
PivotFilterProps | Props for PivotFilter. |
PivotFilterItem | A pill item. |
PivotFilterUser | User payload of an item. |
PivotFilterPillProps | Props for PivotFilterPill. |
PivotFilterSettingsProps | Props for PivotFilterSettings. |
PivotFilterAggregateFunc | 'count' | 'sum' | 'avg' | 'min' | 'max'. |
PivotFilterCalculation | { func, field }. |
PivotFilterDateBucket | The 8 date buckets. |
PivotFilterFieldType | 'list' | 'user' | 'user-multi' | 'date' | 'unsupported'. |
PivotFilterAggregateField | { slug, name } measure field. |
PivotCalendar
Date-bucketed pivot for React Native — a month calendar with per-day measures and a tap-to-expand day sheet, plus week, days-of-month and year grids with a sticky group column. API-aligned with the web PivotCalendar.
PivotGrid
Cross-tabulation pivot grid with multi-dimension rows and columns, aggregation, conditional cell coloring, expand and collapse, pinned value columns, a drilldown action sheet, a toolbar, and CSV, Excel and PDF export.