Ctrl K
ComunidadGuías…

src/components/crm/sort-header.tsx

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"use client";

import Link from "next/link";
import { ArrowDownIcon, ArrowUpIcon } from "lucide-react";
import { ColumnGrip, useColumnStyle } from "@/components/lists/resizable-columns";
import { cn } from "@/lib/utils";

/**
 * A column heading: its width, its grab handle, and its sort link.
 *
 * It renders the cell rather than sitting inside one. That looks like a
 * component doing two jobs and is the only arrangement that works: the width
 * comes from a client context the drag handle writes to, and the pages that
 * build these headers are server components, so a `<div style={width}>` in the
 * page could never follow a drag. Making the heading itself the client cell
 * puts the header and the body on one source of truth.
 *
 * `href` is optional, because a column that does not sort still needs a width
 * and a handle. The href is built by the page from the filters it is already
 * holding, so sorting a filtered list keeps the filters — the bug this shape
 * exists to prevent is a fresh query string that silently throws them away.
 */
export function SortHeader({
  href,
  label,
  active = false,
  desc = false,
  col,
  className,
  align,
}: {
  href?: string;
  label: string;
  active?: boolean;
  desc?: boolean;
  /** Its key in the width catalogue. Omit for a column that cannot be dragged. */
  col?: string;
  className?: string;
  align?: "right";
}) {
  const style = useColumnStyle(col ?? "");
  const inner = (
    <>
      {label}
      {active && (desc ? <ArrowDownIcon className="size-3" /> : <ArrowUpIcon className="size-3" />)}
    </>
  );
  return (
    <div className={cn("relative", className)} style={style}>
      {col && <ColumnGrip column={col} />}
      {href ? (
        <Link
          href={href}
          className={cn(
            "hover:text-foreground flex items-center gap-1 transition-colors",
            align === "right" && "justify-end",
            active && "text-foreground",
          )}
          aria-sort={active ? (desc ? "descending" : "ascending") : "none"}
        >
          {inner}
        </Link>
      ) : (
        <span className={cn("flex items-center gap-1", align === "right" && "justify-end")}>
          {inner}
        </span>
      )}
    </div>
  );
}