# @hiyve/react-ui

UI components for Hiyve video conferencing -- video grid, video tiles, control bar, device selector, audio monitor, participant list, sidebar, waiting room, and join token.

## Installation

```bash
npx @hiyve/cli login
npm install @hiyve/react-ui
```

### Peer Dependencies

```bash
npm install react react-dom @mui/material @mui/icons-material @emotion/react @emotion/styled @hiyve/react @hiyve/rtc-client @hiyve/utilities
```

## Quick Start

```tsx
import { HiyveProvider, useConnection, useRoom } from '@hiyve/react';
import { VideoGrid, ControlBar } from '@hiyve/react-ui';

function App() {
  const generateToken = async () => {
    const res = await fetch('/api/room-token', { method: 'POST' });
    const { roomToken } = await res.json();
    return roomToken;
  };

  return (
    <HiyveProvider generateRoomToken={generateToken}>
      <VideoRoom />
    </HiyveProvider>
  );
}

function VideoRoom() {
  const { joinRoom } = useConnection();
  const { isInRoom } = useRoom();

  if (!isInRoom) {
    return <button onClick={() => joinRoom('my-room', 'user-123')}>Join</button>;
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}>
      <VideoGrid
        localVideoElementId="local-video"
        localUserName="Me"
        layout="grid"
        style={{ flex: 1 }}
      />
      <ControlBar
        showAudioToggle
        showVideoToggle
        showScreenShare
        showSettings
        showLeaveButton
        onLeave={() => console.log('Left room')}
      />
    </div>
  );
}
```

## Components

### Video

| Component | Description |
|-----------|-------------|
| `VideoGrid` | Auto-layout container with grid, speaker, and sidebar modes |
| `VideoTile` | Remote participant video stream with status overlays |
| `LocalVideoTile` | Local user video with flip, zoom, timer, and recording indicator |

### Control Bar

| Component | Description |
|-----------|-------------|
| `ControlBar` | Full media control bar with device menus, recording, layout, and leave |
| `MediaControls` | Audio, video, and screen share toggle buttons |
| `DeviceMenu` | Camera, microphone, and speaker selection dropdown |
| `LayoutMenu` | Layout mode selector (grid, speaker, sidebar) |
| `RecordingMenu` | Recording, streaming, and intelligence mode controls |
| `CameraMenu` | Camera management dropdown for primary and additional cameras |
| `LeaveButton` | Leave room with optional confirmation dialog |
| `IntelligenceSettings` | AI intelligence configuration panel |

### Device Selection

| Component | Description |
|-----------|-------------|
| `DeviceSelector` | Dropdown menus for camera, microphone, and speaker selection |
| `DevicePreview` | Live camera/microphone preview with device selection and audio level |

### Audio

| Component | Description |
|-----------|-------------|
| `AudioLevelMonitor` | Real-time audio level visualization (bars, waveform, meter) |
| `GainControl` | Volume/gain slider with mute button |

### Participants

| Component | Description |
|-----------|-------------|
| `ParticipantList` | Room participant list with status indicators and owner controls |

### Sidebar

| Component | Description |
|-----------|-------------|
| `Sidebar` | Collapsible tabbed sidebar for organizing content panels |

### Waiting Room

| Component | Description |
|-----------|-------------|
| `WaitingRoomSetup` | Toggle switch for hosts to enable/disable the waiting room |
| `WaitingRoomAdmittance` | Host controls for admitting or rejecting waiting participants |
| `WaitingRoomGuest` | Waiting screen shown to guests pending admission |
| `WaitForHostScreen` | Waiting screen shown when a guest joins before the host |

### Connecting Screen

| Component | Description |
|-----------|-------------|
| `ConnectingScreen` | Centered spinner with contextual status message during room creation or joining |

### Join Form

| Component | Description |
|-----------|-------------|
| `JoinForm` | Room creation and joining form with role selection, device preview, and optional waiting room toggle |

### Join Token

| Component | Description |
|-----------|-------------|
| `InviteLinkDialog` | Dialog for creating and sharing invite links |
| `JoinWithTokenForm` | Form for joining a room via an invite link |

### General Purpose

| Component | Description |
|-----------|-------------|
| `UserAvatar` | Avatar with initials fallback, deterministic background color, and optional online status badge |
| `EmptyState` | Centered placeholder with icon, heading, description, and optional action for empty lists or sections |
| `StatCard` | Metric display card with title, value, optional icon, and compact inline variant |

## Hooks

| Hook | Description |
|------|-------------|
| `useDeviceManagement` | Device enumeration, selection, and change detection |
| `useMediaControls` | Audio/video toggle and screen share state |
| `useRecordingFeatures` | Recording, streaming, and intelligence mode state and actions |
| `useSavedDevices` | Persist and restore device selections across sessions |
| `useAutoHide(options)` | Auto-hide behavior with activity detection, animation styles, and pause/resume |

#### useAutoHide

Manages auto-hide behavior based on user activity. After a configurable timeout of inactivity (mouse, touch, keyboard), the hook returns styles that animate the target element out of view. Supports nested pause/resume for menus and dropdowns.

```tsx
import { useAutoHide } from '@hiyve/react-ui';
import type { UseAutoHideOptions } from '@hiyve/react-ui';

function MyToolbar() {
  const { isVisible, wrapperStyle, contentStyle, pauseAutoHide, resumeAutoHide } = useAutoHide({
    timeout: 3000,
    animationDuration: 300,
    animationEasing: 'ease-in-out',
    onVisibilityChange: (visible) => console.log('Toolbar visible:', visible),
  });

  return (
    <div style={wrapperStyle}>
      <div style={contentStyle}>
        <button
          onMouseEnter={pauseAutoHide}
          onMouseLeave={resumeAutoHide}
        >
          Menu
        </button>
      </div>
    </div>
  );
}
```

| Param | Type | Description |
|-------|------|-------------|
| `timeout` | `number \| undefined` | Milliseconds of inactivity before hiding. `0` or `undefined` disables auto-hide |
| `animationDuration` | `number` | Duration of the show/hide animation in milliseconds |
| `animationEasing` | `string` | CSS easing function for the animation |
| `onVisibilityChange` | `(visible: boolean) => void` | Optional callback fired when visibility changes |

| Return | Type | Description |
|--------|------|-------------|
| `isVisible` | `boolean` | Whether the element is currently visible |
| `wrapperStyle` | `CSSProperties` | Apply to the outer wrapper (animates height collapse) |
| `contentStyle` | `CSSProperties` | Apply to the inner content (overflow hidden + opacity fade) |
| `pauseAutoHide` | `() => void` | Pause the auto-hide timer (supports nesting) |
| `resumeAutoHide` | `() => void` | Resume the auto-hide timer (supports nesting) |

## Usage Examples

### Speaker Layout for Presentations

```tsx
<VideoGrid
  localVideoElementId="local-video"
  localUserName="Me"
  layout="speaker"
  styles={{ filmstripHeight: 150 }}
/>
```

### Recording and Intelligence Features (Owner Only)

```tsx
<ControlBar
  showRecordingMenu
  showLayoutSelector
  showHandRaise
  onRecordingStarted={(options) => console.log('Recording started', options)}
  onRecordingStopped={() => console.log('Recording stopped')}
  onIntelligenceModeStarted={(config) => console.log('Intelligence started', config)}
  onIntelligenceModeStopped={() => console.log('Intelligence stopped')}
  onLeave={handleLeave}
/>
```

### Pre-Join Device Preview

```tsx
import { DevicePreview } from '@hiyve/react-ui';
import type { SelectedDevices } from '@hiyve/react-ui';

function PreJoinLobby({ onJoin }: { onJoin: (devices: SelectedDevices) => void }) {
  const [devices, setDevices] = useState<SelectedDevices>({});

  return (
    <DevicePreview
      onDeviceChange={setDevices}
      onReady={() => onJoin(devices)}
      width={480}
      height={360}
    />
  );
}
```

### Audio Level Monitoring

```tsx
import { AudioLevelMonitor, GainControl } from '@hiyve/react-ui';

function AudioControls({ stream }: { stream: MediaStream }) {
  const [gain, setGain] = useState(100);

  return (
    <>
      <AudioLevelMonitor stream={stream} mode="bars" barCount={20} showPeak />
      <GainControl value={gain} onChange={setGain} label="Volume" showMuteButton />
    </>
  );
}
```

### Tabbed Sidebar

```tsx
import { Sidebar } from '@hiyve/react-ui';
import type { SidebarTab } from '@hiyve/react-ui';
import PeopleIcon from '@mui/icons-material/People';
import ChatIcon from '@mui/icons-material/Chat';

const tabs: SidebarTab[] = [
  { id: 'participants', label: 'Participants', icon: <PeopleIcon /> },
  { id: 'chat', label: 'Chat', icon: <ChatIcon />, badge: 3 },
];

function RoomSidebar() {
  const [activeTab, setActiveTab] = useState('participants');

  return (
    <Sidebar
      tabs={tabs}
      activeTab={activeTab}
      onTabChange={setActiveTab}
      renderContent={(tabId) => {
        switch (tabId) {
          case 'participants': return <ParticipantList localUserName="Me" />;
          case 'chat': return <ChatPanel />;
          default: return null;
        }
      }}
    />
  );
}
```

### Waiting Room (Host Side)

```tsx
import { WaitingRoomSetup, WaitingRoomAdmittance } from '@hiyve/react-ui';

function HostControls() {
  const [waitingEnabled, setWaitingEnabled] = useState(false);

  return (
    <>
      <WaitingRoomSetup enabled={waitingEnabled} onChange={setWaitingEnabled} />
      <WaitingRoomAdmittance variant="popover" anchorEl={anchorEl} open={open} />
    </>
  );
}
```

### Invite Links

```tsx
import { InviteLinkDialog } from '@hiyve/react-ui';

function ShareButton({ roomName }: { roomName: string }) {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Share Invite Link</button>
      <InviteLinkDialog
        roomName={roomName}
        open={open}
        onClose={() => setOpen(false)}
        onCopySuccess={() => toast.success('Link copied!')}
      />
    </>
  );
}
```

### Connecting Screen

```tsx
import { ConnectingScreen } from '@hiyve/react-ui';

function RoomLoader({ isCreating }: { isCreating: boolean }) {
  return (
    <ConnectingScreen
      isCreating={isCreating}
      labels={{ creatingMessage: 'Setting up your room...', joiningMessage: 'Entering room...' }}
      colors={{ spinner: 'secondary', background: '#1a1a2e' }}
    />
  );
}
```

### Join Form

```tsx
import { JoinForm } from '@hiyve/react-ui';

// Callback mode
function LobbyPage() {
  return (
    <JoinForm
      onCreateRoom={async (roomName, userName, options) => {
        await api.createRoom(roomName, userName, options);
      }}
      onJoinRoom={async (roomName, userName) => {
        await api.joinRoom(roomName, userName);
      }}
      showWaitingRoomToggle
      showDevicePreview
      storagePrefix="myapp"
      onError={(err) => console.error(err)}
    />
  );
}

// Auto-connect mode (inside HiyveProvider)
function AutoLobby() {
  return (
    <HiyveProvider generateRoomToken={getToken}>
      <JoinForm autoConnect storagePrefix="myapp" />
    </HiyveProvider>
  );
}
```

### Camera Management

`CameraMenu` provides a dropdown for managing the primary camera and adding additional cameras. It can be rendered standalone for custom control bar layouts.

```tsx
import { CameraMenu } from '@hiyve/react-ui';
import type { ActiveCamera, CameraMenuProps } from '@hiyve/react-ui';

<CameraMenu
  isVideoMuted={false}
  isLocalTileHidden={false}
  selectedVideoDevice={currentDeviceId}
  videoDevices={videoDevices}
  isChangingVideoDevice={false}
  onToggleVideo={handleToggleVideo}
  onToggleLocalTile={handleToggleLocalTile}
  onVideoDeviceChange={handleDeviceChange}
  activeCameras={activeCameras}
  availableDevicesForAdd={availableDevices}
  onAddCamera={(deviceId, label) => console.log('Add camera', deviceId, label)}
  onRemoveCamera={(id) => console.log('Remove camera', id)}
  onPhoneCameraRequested={() => console.log('Phone camera requested')}
  disabled={false}
  hasClient={true}
  labels={controlBarLabels}
  icons={controlBarIcons}
  colors={controlBarColors}
  styles={controlBarStyles}
/>
```

### Custom Layout Handler

```tsx
import { VideoGrid } from '@hiyve/react-ui';
import type { CustomLayoutHandler } from '@hiyve/react-ui';

const focusLayout: CustomLayoutHandler = (info) => {
  const positions: Record<string, any> = {};
  const dominant = info.dominantSpeaker;

  info.participants.forEach((p, i) => {
    if (p.userId === dominant) {
      positions[p.userId] = { x: 0, y: 0, width: info.availableWidth * 0.75, height: info.availableHeight };
    } else {
      const sideWidth = info.availableWidth * 0.25;
      const tileHeight = info.availableHeight / (info.totalCount - 1);
      positions[p.userId] = { x: info.availableWidth * 0.75, y: i * tileHeight, width: sideWidth, height: tileHeight };
    }
  });
  return positions;
};

<VideoGrid
  localVideoElementId="local-video"
  localUserName="Me"
  layout="custom"
  customLayoutHandler={focusLayout}
/>
```

## Customization

All components accept optional `labels`, `icons`, `colors`, and `styles` props for customization. Pass a partial object -- unspecified values use built-in defaults.

### Labels (i18n)

```tsx
import { defaultControlBarLabels, mergeControlBarLabels } from '@hiyve/react-ui';

// Partial override
<ControlBar
  labels={{
    mute: 'Couper le micro',
    unmute: 'Activer le micro',
    leave: 'Quitter',
    shareScreen: 'Partager',
  }}
/>

// Programmatic merge
const frenchLabels = mergeControlBarLabels({
  mute: 'Couper le micro',
  unmute: 'Activer le micro',
});
```

### Icons

```tsx
import { defaultControlBarIcons, mergeControlBarIcons } from '@hiyve/react-ui';
import MyMicIcon from './MyMicIcon';

<ControlBar icons={{ mute: <MyMicIcon /> }} />
```

### Colors

```tsx
import { defaultVideoGridColors, defaultVideoTileColors } from '@hiyve/react-ui';

<VideoGrid
  colors={{ dominantBorder: '#00ff88' }}
  tileColors={{
    ...defaultVideoTileColors,
    background: '#111133',
    dominantBorder: '#00ff88',
  }}
/>

<ControlBar
  colors={{
    background: 'rgba(0, 0, 0, 0.9)',
    buttonActive: '#0066cc',
    leaveButton: '#ff3333',
  }}
/>
```

### Styles

```tsx
<ControlBar
  styles={{
    gap: 12,
    padding: 16,
    buttonSize: 'large',
    borderRadius: 8,
  }}
/>

<VideoGrid
  styles={{
    padding: 16,
    gap: 8,
    transitionDuration: '0.5s',
  }}
/>
```

### Render Props

Advanced components support render props for custom rendering:

```tsx
<VideoGrid
  renderProps={{
    renderTile: (participant, defaultTile) => (
      <div className="custom-wrapper">{defaultTile}</div>
    ),
  }}
/>
```

### Available Default/Merge Exports

Each component area exports `default*` objects and `merge*` functions:

```tsx
// VideoGrid
import { defaultVideoGridColors, mergeVideoGridColors } from '@hiyve/react-ui';

// VideoTile
import { defaultVideoTileLabels, mergeVideoTileLabels } from '@hiyve/react-ui';
import { defaultVideoTileColors, mergeVideoTileColors } from '@hiyve/react-ui';

// ControlBar
import { defaultControlBarLabels, mergeControlBarLabels } from '@hiyve/react-ui';
import { defaultControlBarColors, mergeControlBarColors } from '@hiyve/react-ui';
import { defaultControlBarIcons, mergeControlBarIcons } from '@hiyve/react-ui';

// DeviceSelector / DevicePreview
import { defaultDeviceSelectorLabels, mergeDeviceSelectorLabels } from '@hiyve/react-ui';
import { defaultDevicePreviewLabels, mergeDevicePreviewLabels } from '@hiyve/react-ui';

// ParticipantList
import { defaultParticipantListLabels, mergeParticipantListLabels } from '@hiyve/react-ui';

// Sidebar
import { defaultSidebarLabels, mergeSidebarLabels } from '@hiyve/react-ui';
import { defaultSidebarColors, mergeSidebarColors } from '@hiyve/react-ui';

// WaitingRoom
import { defaultSetupLabels, mergeSetupLabels } from '@hiyve/react-ui';
import { defaultAdmittanceLabels, mergeAdmittanceLabels } from '@hiyve/react-ui';
import { defaultGuestLabels, mergeGuestLabels } from '@hiyve/react-ui';

// ConnectingScreen
import { defaultConnectingScreenLabels, mergeConnectingScreenLabels } from '@hiyve/react-ui';
import { defaultConnectingScreenColors, mergeConnectingScreenColors } from '@hiyve/react-ui';
import { defaultConnectingScreenStyles, mergeConnectingScreenStyles } from '@hiyve/react-ui';

// JoinForm
import { defaultJoinFormLabels, mergeJoinFormLabels } from '@hiyve/react-ui';
import { defaultJoinFormIcons, mergeJoinFormIcons } from '@hiyve/react-ui';
import { defaultJoinFormColors, mergeJoinFormColors } from '@hiyve/react-ui';
import { defaultJoinFormStyles, mergeJoinFormStyles } from '@hiyve/react-ui';

// JoinToken
import { defaultDialogLabels, mergeDialogLabels } from '@hiyve/react-ui';
import { defaultFormLabels, mergeFormLabels } from '@hiyve/react-ui';
```

## Device Persistence

Save and restore device selections across sessions. The default storage key is available as `DEFAULT_DEVICE_STORAGE_KEY` if you need to coordinate with other storage logic.

```tsx
import { useSavedDevices, saveDevices, loadDevices, clearSavedDevices } from '@hiyve/react-ui';

function DeviceSettings() {
  const savedDevices = useSavedDevices();
  // savedDevices = { videoInput?: string, audioInput?: string, audioOutput?: string }
}
```

## Features

- Video grid with grid, speaker, sidebar, and custom layout modes
- Smooth CSS transitions when layout changes
- Dominant speaker highlighting and hand raise badges
- Recording/streaming status indicators and timers
- Media controls with audio, video, and screen share toggles
- Device selection menus for camera, microphone, and speaker
- Live device preview with audio level monitoring
- Device persistence across sessions
- Real-time audio level visualization (bars, waveform, meter)
- Gain control with mute button
- Participant list with owner controls (remote mute, remove)
- Collapsible tabbed sidebar for content panels
- Waiting room with host setup, guest waiting screen, and admittance controls
- Invite link creation and token-based room joining
- Full i18n support through label overrides on every component
- Icon, color, and style theming on every component
- Render props for advanced customization
- Responsive layouts that adapt to container size

## Requirements

- Must be used inside a `HiyveProvider` from `@hiyve/react`
- React 18+
- MUI v5 or v6 (`@mui/material`, `@mui/icons-material`) with Emotion
- `@hiyve/rtc-client` and `@hiyve/utilities` as peer dependencies
- `@hiyve/react-intelligence` (optional) -- required only for `IntelligenceSettings`

## License

Proprietary - Hiyve SDK
