# 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.userEnteredValue && (
        <div>
          <strong>Formula:</strong> {cellData.userEnteredValue.formulaValue}
        </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?.effectiveValue?.errorValue) {
    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.effectiveValue.errorValue}
        </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?.formattedValue?.includes("Task")) {
      return <TaskDetailsPanel cellData={cellData} />;
    }

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

          {cellData?.userEnteredValue?.formulaValue && (
            <div>
              <strong className="text-gray-700">Formula:</strong>
              <div className="mt-1 p-3 bg-blue-50 rounded font-mono text-sm">
                {cellData.userEnteredValue.formulaValue}
              </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
