Search, filters and columns
import { useState } from 'react';
import { ColumnPicker, FilterSelect, ListToolbar, SearchField, useColumnState } from '@arbol-ai/ui';
const NO_VALUES: string[] = [];
const COLUMNS = [
{ id: 'title', label: 'Conversation', canHide: false },
{ id: 'contact', label: 'Contact' },
{ id: 'agent', label: 'Agent' },
{ id: 'started', label: 'Started' },
];
const ConversationsToolbar = () => {
const [q, setQ] = useState('');
const [channels, setChannels] = useState<readonly string[]>(NO_VALUES);
const [results, setResults] = useState<readonly string[]>(['missed']);
const { onOrderChange, onReset, onVisibleChange, state } = useColumnState({ columns: COLUMNS });
return (
<ListToolbar
activeFilterCount={[channels, results].filter((values) => values.length > 0).length}
columns={
<ColumnPicker
columns={COLUMNS}
onOrderChange={onOrderChange}
onReset={onReset}
onVisibleChange={onVisibleChange}
state={state}
/>
}
filters={
<>
<FilterSelect
label="Channel"
onChange={setChannels}
options={[
{ value: 'whatsapp', label: 'WhatsApp' },
{ value: 'voice', label: 'Phone' },
{ value: 'web', label: 'Web chat' },
]}
selectionMode="multiple"
value={channels}
/>
<FilterSelect
label="Result"
onChange={setResults}
options={[
{ value: 'resolved', label: 'Resolved' },
{ value: 'missed', label: 'Missed' },
]}
value={results}
/>
</>
}
onClearFilters={() => {
setChannels(NO_VALUES);
setResults(NO_VALUES);
}}
search={<SearchField onChange={setQ} placeholder="Search conversations" value={q} />}
/>
);
};
<ConversationsToolbar />Chips, a records filter and saved views
The agent filter lists records with a face and a second line; the views menu is the app’s own, in the views slot.
import { BookmarkSimpleIcon, CaretDownIcon } from '@phosphor-icons/react';
import { useState } from 'react';
import { Avatar, Button, FilterChips, FilterSelect, FilterToggle, ListToolbar, Menu, MenuItem, MenuTrigger, SearchField } from '@arbol-ai/ui';
const NO_VALUES: string[] = [];
const AGENTS = [
{ value: 'agt_1', label: 'Front desk', description: 'Answers every call' },
{ value: 'agt_2', label: 'Reminders', description: 'Confirms appointments' },
{ value: 'agt_3', label: 'Collections', description: 'Calls about payments' },
].map((agent) => ({ ...agent, icon: <Avatar name={agent.label} size="xs" /> }));
const PatientsToolbar = () => {
const [q, setQ] = useState('');
const [kind, setKind] = useState<'first' | 'returning' | null>(null);
const [agents, setAgents] = useState<readonly string[]>(NO_VALUES);
const [archived, setArchived] = useState(false);
return (
<ListToolbar
activeFilterCount={[agents.length > 0, archived].filter(Boolean).length}
chips={
<FilterChips
label="Kind of patient"
onChange={setKind}
options={[
{ label: 'First visit', value: 'first', count: 18 },
{ label: 'Returning', value: 'returning', count: 230 },
]}
value={kind}
/>
}
filters={
<>
<FilterSelect
allLabel="All agents"
label="Agent"
onChange={setAgents}
options={AGENTS}
value={agents}
/>
<FilterToggle count={12} isSelected={archived} label="Archived" onChange={setArchived} />
</>
}
onClearFilters={() => {
setAgents(NO_VALUES);
setArchived(false);
}}
search={<SearchField onChange={setQ} placeholder="Name, document or phone" value={q} />}
views={
<MenuTrigger>
<Button variant="outline">
<BookmarkSimpleIcon aria-hidden />
Views
<CaretDownIcon aria-hidden />
</Button>
<Menu placement="bottom end">
<MenuItem>Booked by an agent today</MenuItem>
<MenuItem>No show this week</MenuItem>
</Menu>
</MenuTrigger>
}
/>
);
};
<PatientsToolbar />Source
The examples above are src/blocks/list-toolbar/list-toolbar.examples.tsx, which the library’s tests render too.