# media-seek-indicator

A temporary visual indicator for keyboard and gesture seeking

## Import

```ts
import '@videojs/html/ui/seek-indicator';
import '@videojs/html/ui/seek-indicator-value';
```

## Anatomy

```html
<media-seek-indicator>
  <media-seek-indicator-value></media-seek-indicator-value>
</media-seek-indicator>
```

## Behavior

`<media-seek-indicator>` displays feedback for seek actions emitted by a [`<media-hotkey>`](https://videojs.org/docs/framework/html/reference/components/hotkey) or [`<media-gesture>`](https://videojs.org/docs/framework/html/reference/components/gesture) in the same player `<media-container>`. It does not react to current-time changes or seek commands invoked directly through the player store.

| Action | Direction | Value |
| --- | --- | --- |
| `seekStep` | The sign of `value` determines `forward` or `backward` | The absolute number of seconds, such as `10s` |
| `seekToPercent` | The target percentage is compared with the current time | The formatted current time when the action occurred |

Rapid `seekStep` actions in the same direction accumulate while the indicator is open. For example, two 10-second forward actions display `20s`. Changing direction or using `seekToPercent` starts a new display. During a rapid sequence, another full step is not added when it would pass the start or duration boundary.

For `seekToPercent`, pass a percentage from 0 to 100 as `value`. A `<media-hotkey>` with `keys="0-9"` can omit `value`; the pressed digit maps to 0%, 10%, and so on through 90%.

The indicator closes after `closeDelay`, which defaults to 800 milliseconds.

The element remains in the document with `hidden` until the next handled seek action.

## Styling

| Attribute | Values | Description |
| --- | --- | --- |
| `data-open` | Present / absent | Present while the indicator is open |
| `data-direction` | `"forward"` \| `"backward"` | Direction of the handled seek |
| `data-starting-style` | Present / absent | Present during the open transition |
| `data-ending-style` | Present / absent | Present during the close transition |

Position the root from `data-direction` and use the transition attributes for entry and exit styles.

```css
media-seek-indicator[data-direction="backward"] {
  left: 1rem;
}

media-seek-indicator[data-direction="forward"] {
  right: 1rem;
}

media-seek-indicator[data-starting-style],
media-seek-indicator[data-ending-style] {
  opacity: 0;
}
```

## Accessibility

`<media-seek-indicator>` is visual feedback and does not create a live region. Keep the same seek operation available through keyboard-operable controls, and pair it with [`<media-status-announcer>`](https://videojs.org/docs/framework/html/reference/components/status-announcer) when changes should be announced to screen readers. Do not make `<media-seek-indicator-value>` a live region because rapid seek input would produce repeated announcements.

## Examples

### Basic Usage

Focus the player, then press the left or right arrow key to seek by ten seconds. Press a digit from 0 through 9 to seek to a percentage of the duration.

**index.html**

```html
<video-player>
  <media-container class="html-seek-indicator-basic" tabindex="0">
    <video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoplay muted playsinline loop></video>
    <p class="html-seek-indicator-basic__instructions">Focus the player · ←/→: seek 10s · 0–9: seek to percent</p>
    <media-seek-indicator class="html-seek-indicator-basic__indicator" aria-hidden="true">
      <media-seek-indicator-value class="html-seek-indicator-basic__value"></media-seek-indicator-value>
    </media-seek-indicator>
    <media-hotkey keys="ArrowLeft" action="seekStep" value="-10"></media-hotkey>
    <media-hotkey keys="ArrowRight" action="seekStep" value="10"></media-hotkey>
    <media-hotkey keys="0-9" action="seekToPercent"></media-hotkey>
  </media-container>
</video-player>
```

**index.css**

```css
.html-seek-indicator-basic {
  position: relative;
}

.html-seek-indicator-basic:focus-visible {
  outline: 3px solid #60a5fa;
  outline-offset: 2px;
}

.html-seek-indicator-basic video {
  display: block;
  width: 100%;
}

.html-seek-indicator-basic__instructions {
  position: absolute;
  top: 10px;
  left: 50%;
  padding: 6px 10px;
  margin: 0;
  color: white;
  text-align: center;
  background: rgb(0 0 0 / 70%);
  border-radius: 4px;
  translate: -50%;
}

.html-seek-indicator-basic__indicator {
  position: absolute;
  top: 50%;
  left: 50%;
  display: grid;
  min-width: 72px;
  padding: 16px;
  color: white;
  pointer-events: none;
  background: rgb(0 0 0 / 72%);
  border-radius: 9999px;
  place-items: center;
  transform: translate(-50%, -50%);
  transition:
    opacity 160ms ease-in-out,
    scale 160ms ease-in-out;
}

.html-seek-indicator-basic__indicator::before {
  font-size: 24px;
  line-height: 1;
  content: "↔";
}

.html-seek-indicator-basic__indicator[data-direction="backward"] {
  right: auto;
  left: 16px;
  transform: translateY(-50%);
}

.html-seek-indicator-basic__indicator[data-direction="backward"]::before {
  content: "↶";
}

.html-seek-indicator-basic__indicator[data-direction="forward"] {
  right: 16px;
  left: auto;
  transform: translateY(-50%);
}

.html-seek-indicator-basic__indicator[data-direction="forward"]::before {
  content: "↷";
}

.html-seek-indicator-basic__indicator[data-starting-style],
.html-seek-indicator-basic__indicator[data-ending-style] {
  opacity: 0;
  scale: 0.85;
}

.html-seek-indicator-basic__value {
  font-variant-numeric: tabular-nums;
}
```

**index.ts**

```ts
import '@videojs/html/video/player';
import '@videojs/html/ui/container';
import '@videojs/html/ui/hotkey';
import '@videojs/html/ui/seek-indicator';
import '@videojs/html/ui/seek-indicator-value';
```

## API Reference

### media-seek-indicator

#### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `closeDelay` (attribute `close-delay`) | `number` | — | Delay in milliseconds before the indicator closes. |

#### State

State is reflected as data attributes for CSS styling.

| Property | Type | Description |
| --- | --- | --- |
| `transitionStarting` | `boolean` | Whether the open transition is in progress. |
| `transitionEnding` | `boolean` | Whether the close transition is in progress. |
| `open` | `boolean` | Whether the indicator is open. |
| `generation` | `number` | Increments each time a seek input action updates the indicator. |
| `direction` | `'forward' \| 'backward' \| null` | Direction of the seek, or `null` when the target does not change the current time. |
| `count` | `number` | Number of same-direction seek steps accumulated in the current display. |
| `seekTotal` | `number` | Absolute number of seconds accumulated from seek-step actions. |
| `value` | `string \| null` | Accumulated seek-step label, or `null` for percentage seeks. |
| `currentTime` | `string` | Formatted current time captured when the input action occurred. |

#### Data attributes

| Attribute | Type | Description |
| --- | --- | --- |
| `data-open` | — | Present while the indicator is open. |
| `data-direction` | `'forward' \| 'backward' \| null` | Direction of the seek as `"forward"` or `"backward"`. |
| `data-starting-style` | — | Present during the open transition. |
| `data-ending-style` | — | Present during the close transition. |

### media-seek-indicator-value

---

HTML documentation: https://videojs.org/docs/framework/html/llms.txt
All documentation: https://videojs.org/llms.txt
