# Cell Tooltips and Popovers

Enhance your spreadsheet with custom tooltips and expandable cell content. Display additional information, rich media, or interactive components when users hover over or click on cells.

## Overview

The spreadsheet provides two main ways to display additional cell content:

* **Tooltips** (`getTooltipContent`): Show hover tooltips with custom content
* **Expandable Content** (`getCellExpandContent`): Display rich content in a popover when cells are expanded

## Cell Tooltips

Display custom tooltips when users hover over cells using the `getTooltipContent` callback.

### Basic Usage

```tsx
import { SpreadsheetProvider, CanvasGrid } from "@rowsncolumns/spreadsheet";

function SpreadsheetWithTooltips() {
  const getTooltipContent = (
    sheetId: number,
    rowIndex: number,
    columnIndex: number
  ) => {
    // Return undefined for no tooltip
    if (rowIndex === 0 || columnIndex === 0) {
      return undefined;
    }

    // Return JSX for custom tooltip
    return (
      <div className="p-2">
        <strong>Cell:</strong> {cellToAddress({ rowIndex, columnIndex })}
        <br />
        <strong>Sheet:</strong> {sheetId}
      </div>
    );
  };

  return (
    <SpreadsheetProvider>
      <CanvasGrid
        sheetId={1}
        getTooltipContent={getTooltipContent}
        // ... other props
      />
    </SpreadsheetProvider>
  );
}
```

### Function Signature

```typescript
type GetTooltipContent = (
  sheetId: number,
  rowIndex: number,
  columnIndex: number
) => React.ReactNode | undefined;
```

### Return Values

* **React.ReactNode**: Display tooltip with custom content
* **undefined**: No tooltip for this cell

### Examples

#### Display Cell Metadata

```tsx
const getTooltipContent = (sheetId, rowIndex, columnIndex) => {
  const cellData = getCellData(sheetId, rowIndex, columnIndex);

  if (!cellData) return undefined;

  return (
    <div className="text-sm">
      <div className="font-semibold mb-1">Cell Information</div>
      {cellData.note && (
        <div className="mb-1">
          <strong>Note:</strong> {cellData.note}
        </div>
      )}
      {cellData.ue?.fv && (
        <div>
          <strong>Formula:</strong> {cellData.ue.fv}
        </div>
      )}
    </div>
  );
};
```

#### Show Validation Rules

```tsx
const getTooltipContent = (sheetId, rowIndex, columnIndex) => {
  const validation = getDataValidation(sheetId, rowIndex, columnIndex);

  if (!validation) return undefined;

  return (
    <div className="p-2 bg-yellow-50 border border-yellow-200 rounded">
      <div className="font-semibold text-yellow-800">Validation Rule</div>
      <div className="text-sm text-yellow-700 mt-1">
        {validation.condition?.type}: {validation.condition?.values?.join(", ")}
      </div>
    </div>
  );
};
```

#### Display Error Messages

```tsx
const getTooltipContent = (sheetId, rowIndex, columnIndex) => {
  const cellData = getCellData(sheetId, rowIndex, columnIndex);

  if (cellData?.ev?.ev) {
    return (
      <div className="p-2 bg-red-50 border border-red-200 rounded">
        <div className="font-semibold text-red-800">Formula Error</div>
        <div className="text-sm text-red-700 mt-1">
          {cellData.ev.ev.name ?? cellData.ev.ev.message}
        </div>
      </div>
    );
  }

  return undefined;
};
```

## Expandable Cell Content

Display rich, interactive content when users expand cells using the `getCellExpandContent` callback.

### Basic Usage

```tsx
function SpreadsheetWithExpandableContent() {
  const getCellExpandContent = () => {
    return (
      <div className="p-4 max-w-md">
        <h3 className="font-bold mb-2">Additional Information</h3>
        <p className="text-sm text-gray-700">
          This is expandable content that appears when the cell is expanded.
          You can include any React component here.
        </p>
      </div>
    );
  };

  return (
    <SpreadsheetProvider>
      <CanvasGrid
        sheetId={1}
        getCellExpandContent={getCellExpandContent}
        // ... other props
      />
    </SpreadsheetProvider>
  );
}
```

### Function Signature

```typescript
type GetCellExpandContent = () => React.ReactNode;
```

### Advanced Examples

#### Rich Text Editor

```tsx
const getCellExpandContent = () => {
  return (
    <div className="overflow-auto max-h-96 max-w-2xl p-4">
      <div className="prose">
        <h3>Project Description</h3>
        <p>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.
          Detailed information about this cell can be displayed here.
        </p>
        <ul>
          <li>Feature A</li>
          <li>Feature B</li>
          <li>Feature C</li>
        </ul>
      </div>
    </div>
  );
};
```

#### Image Gallery

```tsx
const getCellExpandContent = () => {
  const images = [
    "/images/chart1.png",
    "/images/chart2.png",
    "/images/chart3.png",
  ];

  return (
    <div className="p-4 max-w-4xl">
      <h3 className="font-bold mb-4">Related Charts</h3>
      <div className="grid grid-cols-3 gap-4">
        {images.map((src, index) => (
          <img
            key={index}
            src={src}
            alt={`Chart ${index + 1}`}
            className="rounded shadow-lg"
          />
        ))}
      </div>
    </div>
  );
};
```

#### Interactive Form

```tsx
const getCellExpandContent = () => {
  const [notes, setNotes] = useState("");

  return (
    <div className="p-4 max-w-md">
      <h3 className="font-bold mb-2">Add Notes</h3>
      <textarea
        className="w-full border rounded p-2 mb-2"
        rows={4}
        value={notes}
        onChange={(e) => setNotes(e.target.value)}
        placeholder="Enter your notes here..."
      />
      <button
        className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
        onClick={() => console.log("Save notes:", notes)}
      >
        Save
      </button>
    </div>
  );
};
```

#### Data Visualization

```tsx
import { Chart } from "react-chartjs-2";

const getCellExpandContent = () => {
  const chartData = {
    labels: ["Jan", "Feb", "Mar", "Apr", "May"],
    datasets: [
      {
        label: "Sales",
        data: [12, 19, 3, 5, 2],
        backgroundColor: "rgba(75, 192, 192, 0.2)",
        borderColor: "rgba(75, 192, 192, 1)",
      },
    ],
  };

  return (
    <div className="p-4 max-w-2xl">
      <h3 className="font-bold mb-4">Sales Trend</h3>
      <Chart type="line" data={chartData} />
    </div>
  );
};
```

## Cell-Specific Content

Customize content based on the cell being expanded:

```tsx
import { useSpreadsheet } from "@rowsncolumns/spreadsheet";

function SpreadsheetWithDynamicContent() {
  const { activeCell } = useSpreadsheet();

  const getCellExpandContent = useCallback(() => {
    const cellData = getCellData(
      activeSheetId,
      activeCell.rowIndex,
      activeCell.columnIndex
    );

    // Different content based on cell value
    if (cellData?.fv?.includes("Task")) {
      return <TaskDetailsPanel cellData={cellData} />;
    }

    if (cellData?.fv?.includes("User")) {
      return <UserProfilePanel cellData={cellData} />;
    }

    // Default content
    return (
      <div className="p-4">
        <p>No additional information available</p>
      </div>
    );
  }, [activeCell, activeSheetId]);

  return (
    <CanvasGrid
      getCellExpandContent={getCellExpandContent}
      // ... other props
    />
  );
}
```

## Complete Example

```tsx
import React, { useState, useCallback } from "react";
import {
  SpreadsheetProvider,
  CanvasGrid,
  Sheet,
} from "@rowsncolumns/spreadsheet";
import {
  useSpreadsheetState,
  SheetData,
  CellData,
} from "@rowsncolumns/spreadsheet-state";
import { cellToAddress } from "@rowsncolumns/utils";

function EnhancedSpreadsheet() {
  const [sheets, setSheets] = useState<Sheet[]>([
    { sheetId: 1, rowCount: 100, columnCount: 26, title: "Data" }
  ]);
  const [sheetData, setSheetData] = useState<SheetData<CellData>>({});

  const {
    activeCell,
    activeSheetId,
    selections,
    getCellData,
    onChangeActiveCell,
    onChangeSelections,
  } = useSpreadsheetState({
    sheets,
    sheetData,
    onChangeSheets: setSheets,
    onChangeSheetData: setSheetData,
  });

  // Tooltip handler
  const getTooltipContent = useCallback(
    (sheetId: number, rowIndex: number, columnIndex: number) => {
      // Skip headers
      if (rowIndex === 0 || columnIndex === 0) return undefined;

      const cellData = getCellData(sheetId, rowIndex, columnIndex);

      if (!cellData) return undefined;

      const address = cellToAddress({ rowIndex, columnIndex });

      return (
        <div className="p-3 bg-white shadow-lg rounded-lg border">
          <div className="text-xs text-gray-500 mb-1">{address}</div>
          {cellData.note && (
            <div className="text-sm border-t pt-2 mt-2">
              <strong className="text-gray-700">Note:</strong>
              <div className="text-gray-600 mt-1">{cellData.note}</div>
            </div>
          )}
          {cellData.hyperlink && (
            <div className="text-sm border-t pt-2 mt-2">
              <strong className="text-blue-700">Link:</strong>
              <a
                href={cellData.hyperlink}
                className="text-blue-600 hover:underline ml-1"
              >
                {cellData.hyperlink}
              </a>
            </div>
          )}
        </div>
      );
    },
    [getCellData]
  );

  // Expandable content handler
  const getCellExpandContent = useCallback(() => {
    const cellData = getCellData(
      activeSheetId,
      activeCell.rowIndex,
      activeCell.columnIndex
    );

    return (
      <div className="overflow-auto max-h-96 max-w-2xl p-6 bg-white rounded-lg">
        <h2 className="text-xl font-bold mb-4">Cell Details</h2>

        <div className="space-y-4">
          <div>
            <strong className="text-gray-700">Location:</strong>
            <span className="ml-2">
              {cellToAddress({
                rowIndex: activeCell.rowIndex,
                columnIndex: activeCell.columnIndex,
              })}
            </span>
          </div>

          {cellData?.fv && (
            <div>
              <strong className="text-gray-700">Value:</strong>
              <div className="mt-1 p-3 bg-gray-50 rounded font-mono text-sm">
                {cellData.fv}
              </div>
            </div>
          )}

          {cellData?.ue?.fv && (
            <div>
              <strong className="text-gray-700">Formula:</strong>
              <div className="mt-1 p-3 bg-blue-50 rounded font-mono text-sm">
                {cellData.ue.fv}
              </div>
            </div>
          )}

          <div className="border-t pt-4">
            <button className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
              Edit Cell
            </button>
          </div>
        </div>
      </div>
    );
  }, [activeCell, activeSheetId, getCellData]);

  return (
    <SpreadsheetProvider>
      <CanvasGrid
        sheetId={activeSheetId}
        activeCell={activeCell}
        selections={selections}
        getCellData={getCellData}
        onChangeActiveCell={onChangeActiveCell}
        onChangeSelections={onChangeSelections}
        getTooltipContent={getTooltipContent}
        getCellExpandContent={getCellExpandContent}
      />
    </SpreadsheetProvider>
  );
}

export default EnhancedSpreadsheet;
```

## Styling

### Tooltip Styling

Tooltips automatically position themselves to avoid going off-screen. Style them using standard CSS classes:

```tsx
const getTooltipContent = () => (
  <div className="p-3 bg-gray-900 text-white rounded-lg shadow-xl max-w-xs">
    <div className="text-sm">Your tooltip content</div>
  </div>
);
```

### Popover Styling

Expandable content appears in a popover with scrolling support:

```tsx
const getCellExpandContent = () => (
  <div className="overflow-auto max-h-96 max-w-4xl p-6">
    {/* Scrollable content */}
  </div>
);
```

## Use Cases

### Data Annotations

Show additional context or metadata for cells containing important data.

### Error Explanations

Display helpful error messages and suggestions when formulas fail.

### Rich Content Preview

Preview images, charts, or formatted text without cluttering the grid.

### Interactive Dialogs

Provide forms or interactive elements for complex data entry.

### Audit Information

Display who last edited a cell and when.

## Performance Considerations

1. **Memoize Callbacks**: Use `useCallback` to prevent unnecessary re-renders
2. **Conditional Rendering**: Return `undefined` when no tooltip is needed
3. **Lazy Loading**: Load heavy content only when the popover is opened
4. **Limit Complexity**: Keep tooltip content simple for smooth hover interactions

## Best Practices

1. **Keep Tooltips Concise**: Display only essential information in tooltips
2. **Use Popovers for Rich Content**: Save complex content for expandable popovers
3. **Avoid Heavy Computations**: Don't perform expensive calculations in tooltip callbacks
4. **Accessibility**: Ensure tooltips and popovers work with keyboard navigation
5. **Consistent Styling**: Match your application's design system

## Troubleshooting

### Tooltips Not Showing

* Verify `getTooltipContent` returns valid JSX or undefined (not null)
* Check that the function is properly passed to CanvasGrid
* Ensure there are no JavaScript errors in the console

### Popovers Not Opening

* Confirm `getCellExpandContent` is defined
* Check that the cell expansion trigger is working (usually double-click or icon)
* Verify the content isn't too large or causing layout issues

### Performance Issues

* Use `React.memo` for complex tooltip components
* Implement lazy loading for heavy content
* Consider debouncing tooltip display


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.rowsncolumns.app/configuration/features/cell-tooltips-and-popovers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
