# Example: Multi-column sort cascade

Click a header to sort by it, shift-click others to build an ordered cascade — each sorted header grows a priority badge, and shift-clicking a middle key back to unsorted renumbers the rest.

Source: https://pretable.ai/examples/multi-column-sort.md

```tsx MultiColumnSortGrid.tsx
"use client";

import { useState, type ComponentProps } from "react";

import { PretableSurface } from "@pretable/react";

import { columns } from "./columns";
import { orders } from "./data";

const VIEWPORT_HEIGHT = 340;

export function MultiColumnSortGrid() {
  // The complete query is controlled so the ordered sort list can be echoed
  // below the grid, in lockstep with the priority badges the headers render
  // themselves. Omit both query props to let rows mode own sorting instead.
  const [query, setQuery] = useState<
    NonNullable<
      ComponentProps<typeof PretableSurface<(typeof orders)[number]>>["query"]
    >
  >({
    filters: [],
    sort: [],
    rowGroups: [],
  });

  return (
    <div>
      <p style={{ margin: "0 0 8px", fontSize: 13 }}>
        Click <strong>Status</strong> to sort by it. <kbd>Shift</kbd>+click{" "}
        <strong>Region</strong>, then <kbd>Shift</kbd>+click{" "}
        <strong>Total</strong> — each sorted header grows a priority badge. Now{" "}
        <kbd>Shift</kbd>+click <strong>Region</strong> twice more (desc → asc →
        removed) and watch <strong>Total</strong>&rsquo;s badge renumber from 3
        to 2.
      </p>
      <PretableSurface
        ariaLabel="Orders"
        columns={columns}
        getRowId={(row) => row.id}
        onQueryChange={setQuery}
        query={query}
        rows={orders}
        viewportHeight={VIEWPORT_HEIGHT}
      />
      <p style={{ margin: "8px 0 0", fontSize: 13 }}>
        Sort:{" "}
        <code>
          {query.sort.length > 0
            ? query.sort
                .map(
                  (entry, index) =>
                    `${index + 1}. ${entry.columnId} ${entry.direction}`,
                )
                .join(" · ")
            : "(unsorted)"}
        </code>
      </p>
    </div>
  );
}
```

```ts columns.ts
import type { PretableColumn } from "@pretable/react";

import type { Order } from "./data";

const usd = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
  maximumFractionDigits: 0,
});

export const columns: PretableColumn<Order>[] = [
  { id: "customer", header: "Customer" },
  { id: "region", header: "Region" },
  { id: "status", header: "Status" },
  {
    id: "total",
    header: "Total",
    type: "number",
    format: ({ value }) => usd.format(value as number),
  },
];
```

```ts data.ts
export interface Order {
  id: string;
  customer: string;
  region: string;
  status: string;
  total: number;
}

/**
 * Deliberately ties on `region` and `status` across several rows — a
 * single-column sort leaves those ties in source order, but adding `total`
 * as a second or third key breaks them, which is the whole point of a
 * shift-click cascade.
 */
export const orders: Order[] = [
  {
    id: "o1",
    customer: "Acme Co",
    region: "East",
    status: "Open",
    total: 4200,
  },
  {
    id: "o2",
    customer: "Bilt LLC",
    region: "West",
    status: "Shipped",
    total: 1800,
  },
  {
    id: "o3",
    customer: "Croma Inc",
    region: "East",
    status: "Open",
    total: 2600,
  },
  {
    id: "o4",
    customer: "Delta Bros",
    region: "Central",
    status: "Closed",
    total: 9100,
  },
  {
    id: "o5",
    customer: "Ester Group",
    region: "West",
    status: "Open",
    total: 3400,
  },
  {
    id: "o6",
    customer: "Foxglove",
    region: "East",
    status: "Shipped",
    total: 5300,
  },
  {
    id: "o7",
    customer: "Grove & Co",
    region: "Central",
    status: "Open",
    total: 1200,
  },
  {
    id: "o8",
    customer: "Halden Ltd",
    region: "West",
    status: "Shipped",
    total: 7600,
  },
  {
    id: "o9",
    customer: "Ionix",
    region: "East",
    status: "Closed",
    total: 3300,
  },
  {
    id: "o10",
    customer: "Juno Retail",
    region: "Central",
    status: "Open",
    total: 6700,
  },
  {
    id: "o11",
    customer: "Kestrel",
    region: "West",
    status: "Closed",
    total: 2100,
  },
  {
    id: "o12",
    customer: "Lumen Data",
    region: "Central",
    status: "Shipped",
    total: 4800,
  },
];
```
