Skip to content

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>

Released under the MIT License.