Skip to content

Ecosystem

Context Menu

useContextMenu manages open/close state for a right-click context menu. Pair it with the <ContextMenu> component (or your own UI) to render the actual menu.

Basic Usage

tsx
import { useContextMenu, ContextMenu, Grid } from "@hobom-grid/react";

function App() {
  const contextMenu = useContextMenu();

  return (
    <div style={{ position: "relative" }}>
      <Grid
        ...
        renderCell={(cell, { interactionState }) => (
          <div
            style={{ width: "100%", height: "100%" }}
            onContextMenu={(e) => {
              e.preventDefault();
              contextMenu.openMenu(e.clientX, e.clientY, [
                {
                  kind: "label",
                  text: `Cell (${cell.rowIndex}, ${cell.colIndex})`,
                },
                { kind: "separator" },
                {
                  kind: "action",
                  label: "Copy",
                  shortcut: "Ctrl+C",
                  icon: "📋",
                  onSelect: () => navigator.clipboard.writeText("..."),
                },
                {
                  kind: "action",
                  label: "Edit",
                  icon: "✏️",
                  onSelect: () => editing.startEdit(cell.rowIndex, cell.colIndex),
                },
              ]);
            }}
          />
        )}
      />

      {/* Renders the menu at the stored position */}
      {contextMenu.menuState && (
        <ContextMenu
          state={contextMenu.menuState}
          onClose={contextMenu.closeMenu}
        />
      )}
    </div>
  );
}

Auto-close Behaviour

The hook automatically closes the menu when:

  • The user presses Escape
  • The user clicks anywhere outside an element marked with data-hobom-context-menu

Item Types

ts
// Section label (non-interactive)
{ kind: "label"; text: string }

// Horizontal separator
{ kind: "separator" }

// Clickable action
{
  kind: "action";
  label: string;
  shortcut?: string;   // displayed on the right (e.g. "Ctrl+C")
  icon?: string;       // emoji or short text shown before label
  disabled?: boolean;
  onSelect: () => void;
}

useContextMenu API

PropertyTypeDescription
menuStateContextMenuState | nullCurrent menu position and items, or null when closed
openMenu(x, y, items)functionOpen the menu at the given client coordinates
closeMenu()functionClose the menu

<ContextMenu> Props

PropTypeDescription
stateContextMenuStateThe menu state from useContextMenu
onClose() => voidCalled when the user selects an item or clicks outside

CSV Export

useCsvExport generates an RFC 4180-compliant CSV and triggers a browser download.

Basic Usage

tsx
import { useCsvExport, useClientRowModel } from "@hobom-grid/react";

type Employee = { name: string; age: number; salary: number };

const COLUMNS = [
  { label: "Name", getValue: (r: Employee) => r.name },
  { label: "Age", getValue: (r: Employee) => r.age },
  {
    label: "Salary",
    getValue: (r: Employee) => r.salary,
    formatValue: (v: unknown) => `$${(v as number).toLocaleString()}`,
  },
];

function App({ data }: { data: Employee[] }) {
  const rowModel = useClientRowModel(data);
  const csvExport = useCsvExport({ columns: COLUMNS });

  return (
    <button
      onClick={() => {
        const rows = Array.from({ length: rowModel.rowCount }, (_, i) => rowModel.getRow(i));
        csvExport.exportCsv(rows, "employees.csv");
      }}
    >
      Export CSV
    </button>
  );
}

CSV Formatting

  • The output is UTF-8 with a BOM prefix (\uFEFF) for correct Excel compatibility.
  • Lines are separated by \r\n (CRLF) per RFC 4180.
  • Values containing commas, double-quotes, or newlines are automatically quoted.
  • Double-quotes inside values are escaped as "".

CsvColumnDef Options

OptionTypeRequiredDescription
labelstringColumn header written to the first CSV row
getValue(row: TRow) => unknownExtract the raw value from a row
formatValue(v: unknown) => stringSerialise the value. Default: String(v ?? "")

exportCsv Signature

ts
exportCsv(rows: ReadonlyArray<TRow>, filename?: string): void
// filename defaults to "export.csv"

Exporting a Subset

You can export filtered or selected rows only:

tsx
// Only selected rows
const selectedRowData = data.filter((r) => rowSel.isSelected(r.id));
csvExport.exportCsv(selectedRowData, "selected.csv");

// Only filtered rows (from useClientRowModel)
const filteredRows = Array.from({ length: rowModel.rowCount }, (_, i) => rowModel.getRow(i));
csvExport.exportCsv(filteredRows);