Skip to main content

Overview

The todo utilities module provides functions for manipulating todo lists, including filtering by completion status and importance, sorting by creation date, and merging reordered items with hidden items.

Constants

DELETE_HOLD_DURATION

const DELETE_HOLD_DURATION = 1500;
Duration in milliseconds to hold before deleting a todo item (1.5 seconds).

Types

Todo

interface Todo {
  id: string;
  text: string;
  completed: boolean;
  important: boolean;
  createdAt: number;
}

TodoFilters

interface TodoFilters {
  hideCompleted: boolean;
  importantOnly: boolean;
}

SortMode

type SortMode = "manual" | "oldest" | "newest";

Functions

applyFilters

function applyFilters(todos: Todo[], filters: TodoFilters): Todo[]
Filters todos based on completion status and importance. Parameters:
  • todos - Array of todo items
  • filters - Filter configuration object
Returns: Todo[] - Filtered array of todos Behavior:
  • When hideCompleted is true, removes completed todos
  • When importantOnly is true, keeps only important todos
  • Creates a new array (does not mutate input)
Example:
import { applyFilters } from "@/lib/todo-utils";
import type { Todo, TodoFilters } from "@/types/todo";

const todos: Todo[] = [
  { id: "1", text: "Task 1", completed: false, important: true, createdAt: 1 },
  { id: "2", text: "Task 2", completed: true, important: false, createdAt: 2 },
  { id: "3", text: "Task 3", completed: false, important: false, createdAt: 3 },
];

const filters: TodoFilters = {
  hideCompleted: true,
  importantOnly: false,
};

const filtered = applyFilters(todos, filters);
// Returns: [Task 1, Task 3]

applySorting

function applySorting(todos: Todo[], sortMode: SortMode): Todo[]
Sorts todos based on the specified sort mode. Parameters:
  • todos - Array of todo items
  • sortMode - Sort method: "manual", "oldest", or "newest"
Returns: Todo[] - Sorted array of todos Behavior:
  • "oldest" - Sorts by createdAt ascending (oldest first)
  • "newest" - Sorts by createdAt descending (newest first)
  • "manual" - Returns array as-is (preserves manual ordering)
  • Creates a new array when sorting (does not mutate input)
Example:
import { applySorting } from "@/lib/todo-utils";
import type { Todo, SortMode } from "@/types/todo";

const todos: Todo[] = [
  { id: "1", text: "First", completed: false, important: false, createdAt: 100 },
  { id: "2", text: "Second", completed: false, important: false, createdAt: 200 },
  { id: "3", text: "Third", completed: false, important: false, createdAt: 300 },
];

const newest = applySorting(todos, "newest");
// Returns: [Third, Second, First]

const oldest = applySorting(todos, "oldest");
// Returns: [First, Second, Third]

applyFiltersAndSorting

function applyFiltersAndSorting(
  todos: Todo[],
  filters: TodoFilters,
  sortMode: SortMode
): Todo[]
Applies both filtering and sorting in a single operation. Parameters:
  • todos - Array of todo items
  • filters - Filter configuration object
  • sortMode - Sort method
Returns: Todo[] - Filtered and sorted array Behavior:
  • First applies filters, then applies sorting
  • Equivalent to applySorting(applyFilters(todos, filters), sortMode)
Example:
import { applyFiltersAndSorting } from "@/lib/todo-utils";
import type { Todo, TodoFilters, SortMode } from "@/types/todo";

function TodoList() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [filters, setFilters] = useState<TodoFilters>({
    hideCompleted: false,
    importantOnly: false,
  });
  const [sortMode, setSortMode] = useState<SortMode>("manual");

  const displayedTodos = applyFiltersAndSorting(todos, filters, sortMode);

  return (
    <div>
      {displayedTodos.map((todo) => (
        <div key={todo.id}>{todo.text}</div>
      ))}
    </div>
  );
}

mergeReorderedWithHidden

function mergeReorderedWithHidden(
  reorderedTodos: Todo[],
  allTodos: Todo[],
  filters: TodoFilters
): Todo[]
Merges manually reordered todos with hidden todos, preserving the reordered positions. Parameters:
  • reorderedTodos - The visible todos in their new order
  • allTodos - Complete list of all todos
  • filters - Current filter configuration
Returns: Todo[] - Combined array with reordered items first, then hidden items Behavior:
  • Identifies which todos are hidden by current filters
  • Categorizes hidden todos into:
    • Completed items (if hideCompleted is true)
    • Non-important items (if importantOnly is true)
    • Important incomplete items (always appended)
  • Appends hidden todos after reordered todos
This function is essential for maintaining data integrity when using drag-and-drop reordering with active filters.
Example:
import { mergeReorderedWithHidden } from "@/lib/todo-utils";
import type { Todo, TodoFilters } from "@/types/todo";

function TodoList() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [filters, setFilters] = useState<TodoFilters>({
    hideCompleted: true,
    importantOnly: false,
  });

  const handleReorder = (reorderedTodos: Todo[]) => {
    // Merge reordered visible todos with hidden completed todos
    setTodos((prev) => mergeReorderedWithHidden(reorderedTodos, prev, filters));
  };

  return (
    <DraggableList items={displayedTodos} onReorder={handleReorder} />
  );
}

Complete Example

Here’s a complete implementation with filtering, sorting, and reordering:
import { useState } from "react";
import {
  applyFiltersAndSorting,
  mergeReorderedWithHidden,
  DELETE_HOLD_DURATION,
} from "@/lib/todo-utils";
import type { Todo, TodoFilters, SortMode } from "@/types/todo";

function TodoApp() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [filters, setFilters] = useState<TodoFilters>({
    hideCompleted: false,
    importantOnly: false,
  });
  const [sortMode, setSortMode] = useState<SortMode>("manual");

  // Get displayed todos with filters and sorting applied
  const displayedTodos = applyFiltersAndSorting(todos, filters, sortMode);
  const canReorder = sortMode === "manual";

  const handleReorder = (reorderedTodos: Todo[]) => {
    if (!canReorder) return;
    setTodos((prev) => mergeReorderedWithHidden(reorderedTodos, prev, filters));
  };

  const toggleFilter = (filterKey: keyof TodoFilters) => {
    setFilters((prev) => ({ ...prev, [filterKey]: !prev[filterKey] }));
  };

  return (
    <div>
      <div>
        <button onClick={() => toggleFilter("hideCompleted")}
          Hide Completed: {filters.hideCompleted ? "ON" : "OFF"}
        </button>
        <button onClick={() => toggleFilter("importantOnly")}>
          Important Only: {filters.importantOnly ? "ON" : "OFF"}
        </button>
        <select value={sortMode} onChange={(e) => setSortMode(e.target.value as SortMode)}>
          <option value="manual">Manual</option>
          <option value="oldest">Oldest First</option>
          <option value="newest">Newest First</option>
        </select>
      </div>

      <div>
        {displayedTodos.map((todo) => (
          <div key={todo.id}>
            {todo.text}
            {todo.completed && " ✓"}
            {todo.important && " ★"}
          </div>
        ))}
      </div>
    </div>
  );
}

Build docs developers (and LLMs) love