# TruncatedCell

The TruncatedCell component is designed to display text that may be too long for its container. It automatically detects when text is truncated and provides a popover to show the full content.

## Features

- **Flexible Content**: Accepts plain text or rich ReactNode content
- **Automatic Truncation Detection**: Uses scrollWidth vs clientWidth comparison to detect if content is truncated
- **Responsive**: Re-checks truncation on resize using ResizeObserver for better performance
- **Controlled Popover**: Manages popover state programmatically for better UX
- **Eye Icon Hint**: Displays an eye icon hint only when content is truncated, with smooth fade-in animation
- **Button Visibility Control**: Optional `showButton` prop to control eye button visibility
- **Custom Popover Styling**: `popoverClassName` prop for custom popover content styling
- **Keyboard Accessibility**: Full keyboard navigation support (Enter/Space to open popover)
- **Screen Reader Support**: Proper ARIA labels and roles for accessibility
- **Empty State**: Shows "-" with muted styling when no content is provided
- **Custom Empty Fallback**: Supports a custom placeholder for empty values
- **Customizable Styling**: Supports custom CSS classes for container, cell, and popover
- **Dialog Title**: Optional title for the popover header
- **Custom Content**: Full JSX support for custom popover content
- **Hover Effects**: Smooth underline on content hover and button appearance
- **Scrollable Content**: Long content in popover is scrollable with max height
- **DX-first API**: Rich children work out of the box, and `text` is only needed when you want explicit popover/accessibility text

## Usage

```tsx
import { TruncatedCell } from "laif-ds";

// Basic usage with string content
<TruncatedCell text="Short text" />

// Long text that will be truncated
<TruncatedCell
  text="This is a very long text that will be truncated because it exceeds the container width"
  wrapperClassName="w-56"
/>

// String children also work without text
<TruncatedCell wrapperClassName="w-56">
  This is a string content
</TruncatedCell>

// Rich content works without duplicating text
<TruncatedCell wrapperClassName="w-56">
  <div className="flex items-center gap-2">
    <Avatar src="/avatar.jpg" />
    <div className="min-w-0">
      <p className="font-semibold">John Doe</p>
      <p className="text-sm text-gray-600">john@example.com</p>
    </div>
  </div>
</TruncatedCell>

// Provide text when you want explicit popover or accessibility content
<TruncatedCell
  text="John Doe - john@example.com"
  title="User details"
  wrapperClassName="w-56"
>
  <UserSummary />
</TruncatedCell>

// With custom styling and title
<TruncatedCell
  text="Custom styled truncated text"
  title="Full Content"
  wrapperClassName="w-40"
  className="max-w-40"
/>

// With custom popover content and styling
<TruncatedCell
  text="Text with custom popover content"
  title="Custom Content"
  popoverContent={
    <div className="space-y-3">
      <p>Custom JSX content here</p>
      <button>Action Button</button>
    </div>
  }
  popoverClassName="max-w-md"
  showButton={true}
/>

// Hide the eye hint
<TruncatedCell
  text="Text without eye button"
  showButton={false}
/>

// Empty text
<TruncatedCell text="" />

// Custom empty fallback
<TruncatedCell text="" emptyFallback="No value" />
```

## Props

| Prop               | Type        | Default     | Description                                                                  |
| ------------------ | ----------- | ----------- | ---------------------------------------------------------------------------- |
| `children`         | `ReactNode` | `undefined` | Content to display                                                           |
| `text`             | `string`    | `undefined` | Optional text used for truncation detection, accessibility, and popover copy |
| `title`            | `string`    | `undefined` | Optional title for the popover dialog                                        |
| `popoverContent`   | `ReactNode` | `undefined` | Custom popover content (JSX)                                                 |
| `wrapperClassName` | `string`    | `""`        | Preferred CSS classes for the wrapper container                              |
| `className`        | `string`    | `""`        | CSS classes for the cell content                                             |
| `popoverClassName` | `string`    | `""`        | CSS classes for the popover content                                          |
| `showButton`       | `boolean`   | `true`      | Flag used to show/hide the eye hint                                          |
| `emptyFallback`    | `ReactNode` | `"-"`       | Custom placeholder rendered when no content is available                     |

## Behavior

### Truncation Detection

- Component monitors the text element's scrollWidth vs clientWidth
- If scrollWidth > clientWidth, the text is considered truncated
- Detection runs on mount and on resize
- When `text` is not provided, the component also extracts `textContent` from the rendered node to power the default popover content

### Visual States

1. **Normal State**: Text is displayed with truncation (CSS `truncate`)
2. **Truncated State**: Text is truncated + eye icon hint appears
3. **Empty State**: Shows "-" when text is empty/undefined
4. **Popover State**: Opens when there is content to inspect, and the eye icon hint appears only when the rendered value is truncated

### Styling

- Container: `flex max-w-60 gap-2` + custom classes
- Text container: `flex min-w-0 flex-1`
- Text element: `w-full min-w-0 truncate whitespace-nowrap`
- Popover: `w-96` with `whitespace-pre-wrap` for multiline support

### Content Types

The TruncatedCell component supports two main usage patterns:

#### 1. String Content (Simple)

```tsx
// Direct text prop
<TruncatedCell text="Simple text content" />

// String children (text prop optional)
<TruncatedCell wrapperClassName="w-56">
  String content as children
</TruncatedCell>
```

#### 2. ReactNode Content (Advanced)

```tsx
// Rich children work without text
<TruncatedCell wrapperClassName="w-56">
  <div className="flex items-center gap-2">
    <Avatar src="/avatar.jpg" />
    <div className="min-w-0">
      <p className="font-semibold">John Doe</p>
      <p className="text-sm text-gray-600">john@example.com</p>
    </div>
  </div>
</TruncatedCell>

// Add text when you want explicit popover content
<TruncatedCell text="User profile - John Doe" wrapperClassName="w-56">
  <UserSummary />
</TruncatedCell>
```

### Choosing Between `children` and `text`

- Use `text` for the simplest plain-text case
- Use `children` when you need custom rendering inside the truncated area
- Add `text` together with `children` when the rendered content is visual or abbreviated and you want more descriptive text in the popover

```tsx
// Plain text
<TruncatedCell text="Contract pending signature" />

// Rich UI with automatic text extraction
<TruncatedCell wrapperClassName="w-56">
  <div>Complex content</div>
</TruncatedCell>

// Rich UI with explicit popover and a11y text
<TruncatedCell text="Complex content description" wrapperClassName="w-56">
  <div>Complex content</div>
</TruncatedCell>
```

### Custom Popover Content

The `popoverContent` prop allows you to override the default text display with custom JSX content:

- **Full Flexibility**: Any React components can be used as popover content
- **Backward Compatible**: When not provided, defaults to displaying the text with Typo component
- **Title Support**: Works seamlessly with the `title` prop for popover headers
- **Custom Styling**: Use `popoverClassName` to style the popover container
- **Button Control**: Use `showButton` to control eye hint visibility
- **Open Behavior**: Custom popover content can open even when the displayed value itself is not truncated

```tsx
<TruncatedCell
  text="User profile"
  title="User Details"
  popoverContent={
    <div className="space-y-2">
      <div className="flex items-center gap-2">
        <Avatar src="/avatar.jpg" />
        <div>
          <p className="font-semibold">John Doe</p>
          <p className="text-sm text-gray-600">john@example.com</p>
        </div>
      </div>
      <div className="flex gap-2">
        <Button size="sm">Edit</Button>
        <Button size="sm" variant="outline">
          View Profile
        </Button>
      </div>
    </div>
  }
  popoverClassName="max-w-md"
  showButton={true}
/>
```

### Button Visibility Control

The `showButton` prop gives you control over the eye hint visibility:

```tsx
// Show eye hint (default)
<TruncatedCell text="Long text that will be truncated" showButton={true} />

// Hide eye hint
<TruncatedCell text="Long text without eye hint" showButton={false} />
```

### Empty Fallback

```tsx
// Default placeholder
<TruncatedCell text="" />

// Custom placeholder
<TruncatedCell text="" emptyFallback="No value" />
```

### Popover Styling

The `popoverClassName` prop allows custom styling of the popover container:

```tsx
<TruncatedCell
  text="Custom styled popover"
  popoverClassName="max-w-md bg-blue-50 border-blue-200"
/>
```

## Technical Details

### Dependencies

- React hooks: `useState`, `useEffect`, `useRef`, `useCallback`
- Laif-DS components: `Popover`, `PopoverContent`, `PopoverTrigger`, `Typo`, `Icon`
- Utility: `cn` for class merging

### Performance Optimizations

- **ResizeObserver**: Uses modern ResizeObserver API for better performance than window resize events
- **useCallback**: Memoizes truncation check function to prevent unnecessary re-renders
- **Controlled State**: Manages popover state programmatically to avoid prop drilling
- **Cleanup**: Proper cleanup of observers and event listeners
- **Optimized Re-renders**: Only re-checks truncation when content changes

### Event Handling

- ResizeObserver for element-specific resize detection
- Fallback window resize listener for older browsers
- Click on the cell trigger opens the popover when available
- Keyboard support (Enter/Space keys)
- Hover effects with smooth transitions

### Accessibility Features

- **Semantic Trigger**: Uses a real `button` element as the interactive trigger
- **Keyboard Navigation**: Full support for Enter and Space keys
- **Screen Reader Labels**: Dynamic `aria-label` based on truncation state
- **Focus Management**: Native button focus behavior
- **Semantic HTML**: Popover content remains accessible even for multiline values

## Accessibility

- Text container has cursor pointer to indicate interactivity
- Hover state provides visual feedback
- Popover provides full text access for screen readers
- Eye icon provides a clear visual hint that more content is available

## Examples

### In a Table Cell

```tsx
const TableCell = ({ value }: { value: string }) => (
  <TruncatedCell text={value} wrapperClassName="w-56" className="py-2" />
);
```

### With Maximum Width Constraint

```tsx
<TruncatedCell
  text="Constrained width text that will truncate"
  wrapperClassName="w-32"
/>
```

### Multiline Text Support

```tsx
<TruncatedCell text="Line 1\nLine 2\nLine 3" className="max-w-48" />
```

## Comparison with Alternatives

### vs Tooltip

- **TruncatedCell**: Supports click-to-inspect content, and visually hints with the eye icon when text is actually truncated
- **Tooltip**: Always shows on hover regardless of truncation

### vs CSS text-overflow alone

- **TruncatedCell**: Provides access to full content
- **CSS only**: Content is inaccessible when truncated

### vs Expandable Text

- **TruncatedCell**: Compact, popover-based approach
- **Expandable**: Takes more space, inline expansion
