Filter Modal

The Filter Component is a reusable modal that provides a menu with customizable filter options. It allows users to filter data based on different criteria and apply those filters to the parent component.

Features
  • Popup overlay menu with filter options
  • Support for dropdown select filters
  • Filter clearing functionality
  • Reset all filters option
  • Apply filters button
Usage
  • ModalFilterMenu.vue - This is the main filter menu component that renders a popup overlay with filter options.

<template>
  <OverlayPanel
    ref="op"
    id="overlay_menu"
    :popup="true"
    class="w-[240px] rounded-lg"
  >
    <!-- Modal Header -->
    <div>
      <div
        class="flex items-center justify-between px-4 py-[10px] bg-neutral-20"
      >
        <div class="flex items-center gap-3">
          <h5 class="body-normal font-medium text-neutral-500">Filter</h5>
        </div>
        <button @click="toggle">
          <i
            class="pi pi-times text-neutral-500 text-xs hover:bg-primary-50 p-2 hover:rounded-full"
          />
        </button>
      </div>
    </div>
    <div class="p-4">
      <div v-for="(item, index) in items" :key="index" class="mb-4">
        <div class="mb-2 flex justify-between items-center">
          <label class="body-small">{{ item.label }} </label>
          <p
            @click="handleClear(index)"
            class="body-small text-primary-500 cursor-pointer"
          >
            Clear
          </p>
        </div>
        <FormSelect
          v-if="item.field === 'Select'"
          v-model="selectedValues[index]"
          optionLabel="name"
          item-value="value"
          placeholder="All"
          required
          class="h-[36px]"
          :options="item.options"
          :value="selectedValues[index]"
          name="field"
          @change="handleSelectChange(index)"
        />
      </div>

      <!-- Modal Footer -->
      <div class="flex items-center gap-4 pt-4 border-t border-neutral-100">
        <div class="w-full">
          <CommonButton
            title="Reset"
            bgColor="bg-neutral-30"
            textColor="text-neutral-500 font-semibold text-sm h-8"
            type="button"
            @click="resetModal"
          />
        </div>

        <div class="w-full">
          <CommonButton
            title="Apply"
            bgColor="bg-primary-300 text-neutral-500 h-8"
            textColor="font-semibold text-sm"
            type="submit"
            :loading="isLoading"
            @click="onPrimaryAction"
          />
        </div>
      </div>
    </div>
  </OverlayPanel>
</template>

<script setup>
import { ref } from "vue";
import FormSelect from "~/components/common/input/FormSelect.vue";

const op = ref(null);
const selectedValues = ref([]); // Store selected values for each item
const isLoading = ref(false);

// Define props for the component
const props = defineProps({
  items: {
    type: Array,
    required: true,
  },
});

// Emit events for toggling the menu and reset
const emit = defineEmits(["toggle", "reset"]);

// Define the toggle function
const toggle = (event) => {
  op.value.toggle(event);
  emit("toggle", event);
};

// Handle select change
const handleSelectChange = (index) => {
  console.log("Selected value:", selectedValues.value);
};

// Handle clear button click
const handleClear = (index) => {
  selectedValues.value[index] = null; // Reset the selected value
};

// Reset all filters
const resetModal = () => {
  selectedValues.value = []; // Reset all selected values
  emit("reset"); // Emit reset event
  op.value.hide(); // Hide the menu after reset
};

// Apply filters
const onPrimaryAction = () => {
  const filteredValues = [...selectedValues.value];
  emit("toggle", filteredValues);
  op.value.hide(); // Hide menu after applying
};

// Expose the toggle function to the parent component
defineExpose({
  toggle,
});
</script>

Basic Implementation

<template>
  <!-- Filter Button -->
  <button
    class="h-[48px] px-5 rounded-[8px] flex justify-center items-center bg-none border border-neutral-100 text-neutral-200 font-normal text-sm hover:bg-primary-75 hover:border-primary-75"
    @click="toggle"
    aria-haspopup="true"
    aria-controls="overlay_menu"
  >
    <span class="mr-2">
      <BaseCustomIcon name="filter" />
    </span>
    <p>Filter</p>
  </button>

  <!-- Filter Menu Component -->
  <ModalFilterMenu
    ref="reusableMenu"
    :items="menuItems"
    @toggle="handleMenuToggle"
  />
</template>

<script setup>
import { ref } from "vue";
import ModalFilterMenu from "~/components/ModalFilterMenu.vue";

// Reference to the filter menu
const reusableMenu = ref();

// Define menu items
const menuItems = ref([
  {
    label: "Staff",
    field: "Select",
    options: [
      { name: "Pending", value: "Pending" },
      { name: "Completed", value: "Completed" },
    ],
  },
  {
    label: "Feature",
    field: "Select",
    options: [
      { name: "Credit", value: "Credit" },
      { name: "Debit", value: "Debit" },
    ],
  },
]);

// Function to toggle the menu
const toggle = (event) => {
  reusableMenu.value.toggle(event);
};

// Handle menu toggle event
const handleMenuToggle = (event) => {
  console.log("Menu toggled", event);
};
</script>