# AudioPlayer

A unified, premium media player designed for both quick audio previews and long-form podcasts. It supports embedded (Card) and global (Bar) layouts, featuring fluid transitions and intelligent responsiveness.

---

## Import

```tsx
import { AudioPlayer } from 'xertica-ui/media';
```

---

## Basic Usage

### Embedded with Auto-Float (Card)

Ideal for in-page content. The player automatically "floats" if the user scrolls away while listening.

```tsx
<AudioPlayer
  src="url/to/audio.mp3"
  title="Exclusive Interview"
  artist="Xertica News"
  variant="card"
/>
```

### Global Bar (Bar)

Ideal for continuous playback across the entire application.

```tsx
<AudioPlayer
  src="url/to/audio.mp3"
  title="Weekly Podcast"
  variant="bar"
  colorVariant="primary"
  isOpen={true}
  onClose={() => setOpen(false)}
/>
```

---

## Props

| Prop              | Type                     | Default     | Description                                                   |
| ----------------- | ------------------------ | ----------- | ------------------------------------------------------------- |
| `variant`         | `'card' \| 'bar'`        | `'card'`    | Determines if the player is an embedded card or a fixed bar.  |
| `colorVariant`    | `'default' \| 'primary'` | `'default'` | Visual style (`primary` is ideal for brand-focused podcasts). |
| `isOpen`          | `boolean`                | `true`      | (Bar only) Whether the player bar is open or closed.          |
| `enableAutoFloat` | `boolean`                | `true`      | (Card only) Whether to float when scrolling out of viewport.  |
| `title`           | `string`                 | —           | Track title.                                                  |
| `artist`          | `string`                 | —           | Author or subtitle.                                           |
| `src`             | `string`                 | —           | Audio file URL.                                               |
| `duration`        | `number`                 | —           | Total duration in seconds.                                    |

---

## Features and Behaviors

### Dynamic Responsiveness

The `AudioPlayer` doesn't just hide elements—it **reallocates** them. Depending on available screen space:

- **Large Screens (Desktop)**: All controls (volume, speed, download) are visible on the bar.
- **Medium/Small Screens**: Controls like volume and speed automatically move to the secondary options menu (`...`) to prevent visual clutter.

### Playback Speed

Supports speed adjustments: `0.5x`, `1x`, `1.5x`, and `2x`. The control adapts dynamically to the screen width.

### Layout Integration

The `bar` variant automatically detects the state of the **Sidebar** and **AI Assistant** via `LayoutContext`, adjusting its margins to ensure it never covers vital system elements.

---

## Headless Hook: `useAudioPlayer`

For advanced use cases where you need a fully custom player UI, use the `useAudioPlayer` headless hook directly:

```tsx
import { useAudioPlayer } from 'xertica-ui/hooks';

function MyCustomPlayer({ src }: { src: string }) {
  const {
    audioRef,
    containerRef,
    isPlaying,
    togglePlay,
    currentTime,
    duration,
    formatTime,
    onPlay,
    onPause,
    onEnded,
    onTimeUpdate,
    onLoadedMetadata,
  } = useAudioPlayer({ src, variant: 'card' });

  return (
    <div ref={containerRef}>
      <audio
        ref={audioRef}
        src={src}
        onPlay={onPlay}
        onPause={onPause}
        onEnded={onEnded}
        onTimeUpdate={onTimeUpdate}
        onLoadedMetadata={onLoadedMetadata}
      />
      <button onClick={togglePlay}>{isPlaying ? 'Pause' : 'Play'}</button>
      <span>
        {formatTime(currentTime)} / {formatTime(duration)}
      </span>
    </div>
  );
}
```

See [`hooks.md`](./hooks.md#useaudioplayer) for the complete `useAudioPlayer` API reference.

---

## Implementation Notes (v2.1.9+)

### Latest-Ref Pattern in mode-switch effect

The hook uses the **latest-ref pattern** to avoid stale closure problems when switching between floating and embedded modes:

```ts
const currentTimeRef = useRef(currentTime);
const isPlayingRef = useRef(isPlaying);

useEffect(() => {
  currentTimeRef.current = currentTime;
}, [currentTime]);
useEffect(() => {
  isPlayingRef.current = isPlaying;
}, [isPlaying]);

// mode-switch effect — reads from refs, not from state closure
useEffect(() => {
  const audio = audioRef.current;
  if (audio) {
    if (Math.abs(audio.currentTime - currentTimeRef.current) > 0.5)
      audio.currentTime = currentTimeRef.current;
    if (isPlayingRef.current) audio.play().catch(err => console.log('Playback interrupted:', err));
  }
}, [isFloating, variant]);
```

This eliminates the need for the `eslint-disable-next-line react-hooks/exhaustive-deps` comment that previously suppressed the stale-closure warning. When building a custom player with `useAudioPlayer`, you do not need to replicate this pattern — it is handled internally.

---

## AI Best Practices

> [!IMPORTANT]
>
> - **Branding Variants**: Use `colorVariant="primary"` for high-relevance or branded content (like Podcasts). Use the default style for utility audios.
> - **Manual Floating**: In Card mode, users can click the "Maximize" icon to manually force floating mode.
> - **Accessibility**: All buttons have appropriate ARIA labels. Always provide a descriptive `title` for each audio track.
> - **Custom UI**: Use `useAudioPlayer` from `xertica-ui/hooks` only when you need a fully custom player interface. For standard playback, always use `<AudioPlayer>` directly.
