VUE

useSortable Composable

Reactive array sorting with multiple sort criteria and direction control

Vue3ComposablesSortingArraysReactive

Code

import { ref, computed, type Ref } from 'vue';

type SortDirection = 'asc' | 'desc';
type SortKey<T> = keyof T | ((item: T) => any);

interface SortConfig<T> {
  key: SortKey<T>;
  direction: SortDirection;
}

export function useSortable<T>(items: Ref<T[]>) {
  const sortConfig = ref<SortConfig<T> | null>(null);

  // Get value from key or function
  const getValue = (item: T, key: SortKey<T>) => {
    __TOKEN_41__ (typeof key === 'function') {
      return key(item);
    }
    return item[key];
  };

  // Sorted items computed property
  const sortedItems = computed(() => {
    __TOKEN_46__ (!sortConfig.value || items.value.length === 0) {
      return [...items.value];
    }

    const { key, direction } = sortConfig.value;
    return [...items.value].sort((a, b) => {
      const valueA = getValue(a, key);
      const valueB = getValue(b, key);

      // Handle different types
      __TOKEN_52__ (typeof valueA === 'string' && typeof valueB === 'string') {
        return direction === 'asc' 
          ? valueA.localeCompare(valueB) 
          : valueB.localeCompare(valueA);
      }
      __TOKEN_56__ (typeof valueA === 'number' && typeof valueB === 'number') {
        return direction === 'asc' ? valueA - valueB : valueB - valueA;
      }
      __TOKEN_60__ (valueA instanceof Date && valueB instanceof Date) {
        return direction === 'asc' ? valueA.getTime() - valueB.getTime() : valueB.getTime() - valueA.getTime();
      }
      // Fallback for other types
      return 0;
    });
  });

  // Set sort configuration
  const setSort = (key: SortKey<T>, direction?: SortDirection) => {
    // Toggle direction if same key
    __TOKEN_66__ (sortConfig.value?.key === key) {
      sortConfig.value.direction = sortConfig.value.direction === 'asc' ? 'desc' : 'asc';
    } else {
      sortConfig.value = {
        key,
        direction: direction || 'asc'
      };
    }
  };

  // Reset sorting
  const resetSort = () => {
    sortConfig.value = null;
  };

  // Toggle sort direction for current key
  const toggleSortDirection = () => {
    __TOKEN_70__ (sortConfig.value) {
      sortConfig.value.direction = sortConfig.value.direction === 'asc' ? 'desc' : 'asc';
    }
  };

  return {
    sortedItems,
    sortConfig,
    setSort,
    resetSort,
    toggleSortDirection
  };
}

// Usage example
// const items = ref([{ name: 'Banana', price: 1.2 }, { name: 'Apple', price: 0.8 }]);
// const { sortedItems, setSort } = useSortable(items);
// setSort('name'); // Sort by name ascending
// setSort('price', 'desc'); // Sort by price descending