Search & Filter Toolbar
A dedicated control bar pattern for lists and grids, providing standardized search and filtering capabilities.
Demo
Implementation
vue
<script setup lang="ts">
import { ref } from 'vue';
// 1. Component Imports (Default)
import CLCard from '@codeandfunction/callaloo/CLCard';
import CLInput from '@codeandfunction/callaloo/CLInput';
import CLSelect from '@codeandfunction/callaloo/CLSelect';
import CLButton from '@codeandfunction/callaloo/CLButton';
import CLText from '@codeandfunction/callaloo/CLText';
import CLIcon from '@codeandfunction/callaloo/CLIcon';
import { useToast } from '@codeandfunction/callaloo/composables/useToast';
import type { CLOption } from '@codeandfunction/callaloo';
// 2. Enum & Type Imports (Named)
import {
CLColors,
CLIconNames,
CLColorVariants,
CLButtonTypes,
CLTextTypes,
} from '@codeandfunction/callaloo';
// 3. State & Logic
const searchQuery = ref('');
const category = ref('all');
const dateRange = ref('7d');
const toast = useToast();
const categoryOptions: CLOption[] = [
{ label: 'All Categories', value: 'all' },
{ label: 'Electronics', value: 'electronics' },
{ label: 'Furniture', value: 'furniture' },
{ label: 'Clothing', value: 'clothing' },
];
const dateOptions: CLOption[] = [
{ label: 'Last 7 Days', value: '7d' },
{ label: 'Last 30 Days', value: '30d' },
{ label: 'This Year', value: '1y' },
];
const handleApply = (): void => {
toast.showToast({
color: CLColors.Success,
message: `Searching for "${searchQuery.value || 'all'}" in ${category.value}`,
title: 'Filters Applied',
});
};
const handleReset = (): void => {
searchQuery.value = '';
category.value = 'all';
dateRange.value = '7d';
toast.showToast({
color: CLColors.Neutral,
message: 'Filters have been cleared',
title: 'Reset',
});
};
</script>
<template>
<div class="pattern-preview">
<div class="toolbar-demo">
<CLCard :variant="CLColorVariants.Ghost" :padded="true" elevated>
<div class="toolbar-container">
<div class="search-group">
<CLInput
id="search"
name="search"
label="Search"
v-model="searchQuery"
placeholder="Search items..."
:prefix="CLIconNames.Search"
fluid
/>
</div>
<div class="filter-group">
<CLSelect
id="category"
name="category"
label="Category"
v-model="category"
:options="categoryOptions"
fluid
/>
</div>
<div class="filter-group">
<CLSelect
id="date"
name="date"
label="Time Range"
v-model="dateRange"
:options="dateOptions"
fluid
/>
</div>
<div class="action-group">
<CLButton
:color="CLColors.Primary"
@click="handleApply"
:type="CLButtonTypes.Button"
>
Apply
</CLButton>
<CLButton
:variant="CLColorVariants.Ghost"
:color="CLColors.Neutral"
@click="handleReset"
:type="CLButtonTypes.Button"
aria-label="Reset Filters"
>
<CLIcon :name="CLIconNames.X" />
</CLButton>
</div>
</div>
</CLCard>
<div class="result-area">
<CLText :type="CLTextTypes.Small">Results will appear here...</CLText>
</div>
</div>
</div>
</template>
<style scoped>
.toolbar-demo {
display: flex;
flex-direction: column;
gap: 2rem;
}
.result-area {
display: flex;
justify-content: center;
align-items: center;
height: 150px;
border: 2px dashed var(--vp-c-divider);
border-radius: 8px;
color: var(--vp-c-text-2);
}
.toolbar-container {
display: flex;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 768px) {
.toolbar-container {
flex-direction: row;
align-items: flex-end;
}
.search-group {
flex: 2;
}
.filter-group {
flex: 1;
min-width: 160px;
}
.action-group {
display: flex;
gap: 0.5rem;
}
}
</style>