---
url: https://nsh.dev/ru/elements/hover-card
description: "Компонент HoverCard: превью того, на что указывает его триггер. Показывается, пока указатель находится на триггере, а оформление задаётся приложением."
demo: https://nsh.dev/play/index.html#el-hover-card
---

# HoverCard

Карточка при наведении показывает превью того, на что указывает её триггер, не покидая страницу: профиль за упоминанием, страницу за ссылкой, котировку за тикером. На десктопах и в десктопных браузерах она открывается, когда указатель задержался на триггере в течение `openDelay` (600 мс) или когда на триггер пришёл фокус клавиатуры. Она остаётся открытой, пока указатель перемещается с триггера на карточку и обратно, а также пока фокус клавиатуры находится на триггере или на ссылке либо кнопке в карточке, так что всем в карточке можно пользоваться и указателем, и клавиатурой. Она закрывается через `closeDelay` (300 мс) после того, как указатель покинул и триггер, и карточку, а фокуса нет ни там, ни там. Escape и нажатие вне карточки закрывают её сразу. На телефонах и планшетах нажатие на триггер открывает и закрывает карточку, а нажатие вне её закрывает.

`HoverCard` из [`nsh/base`](https://nsh.dev/translations/ru/modules/nsh-base.md) сам ничего не рисует: триггер — ваш элемент, всплывающее окно и его содержимое тоже ваши.

```rust
use nsh_base::hover_card;
use nsh_core::prelude::*;

fn mention() -> impl IntoElement {
    hover_card()
        .offset(px(8.0))
        .popup_class("w-72 gap-2 rounded-xl border border-border bg-popover p-4 shadow-lg")
        .trigger(text("@ada").text_color(rgb(0x2563eb)))
        .child(text("Ada Lovelace").font_weight(FontWeight::SEMIBOLD))
        .child(text("Writes the first programs for the Analytical Engine."))
}
```

```tsx
import { Text } from "nsh";
import { HoverCard } from "nsh/base";

function Mention() {
  return (
    <HoverCard
      offset={8}
      popupClass="w-72 gap-2 rounded-xl border border-border bg-popover p-4 shadow-lg"
      trigger={<Text class="text-blue-600">@ada</Text>}
    >
      <Text class="font-semibold">Ada Lovelace</Text>
      <Text>Writes the first programs for the Analytical Engine.</Text>
    </HoverCard>
  );
}
```

Триггер сохраняет свою роль, свои нажатия и внешний вид: view с `role="link"` и собственным `onPress` по-прежнему делает то, что делает при нажатии, а карточка лишь добавляет к нему превью. Содержимое создаётся при каждом открытии карточки, поэтому оно может загружать показываемые данные именно в этот момент. На телефонах и планшетах, где карточку открывает нажатие, триггер сообщает программам чтения с экрана, открыта ли его карточка. В [Toolbar](https://nsh.dev/translations/ru/elements/toolbar.md) триггер может быть одним из его элементов, а то, что показывает карточка, — нет: её кнопки и ссылки сохраняют собственные позиции Tab, а `disabled` панели инструментов не трогает их.

<a id="delays"></a>

## Задержки

`openDelay` не даёт карточке мелькнуть, когда указатель просто пересекает триггер по пути в другое место; `closeDelay` даёт указателю время преодолеть промежуток между триггером и карточкой. Оба значения задаются в миллисекундах. Указатель и задержки отслеживаются нативно, поэтому пока указатель задержался на триггере или перемещается между ним и карточкой, ваш код не выполняется — до тех пор, пока карточка не откроется или не закроется. `openDelay`, равный `Infinity`, оставляет открытие за приложением (и за нажатием на сенсорном экране); `closeDelay`, равный `Infinity`, оставляет закрытие за Escape, нажатием вне карточки и приложением.

```tsx
<HoverCard openDelay={300} closeDelay={150} trigger={<Text>AAPL</Text>}>
  <Quote symbol="AAPL" />
</HoverCard>
```

<a id="open-state"></a>

## Состояние открытия

Без `open` карточка хранит собственное состояние, начиная с `defaultOpen`. С `open` состоянием владеет приложение: меняйте его в `onOpenChange({ open, reason })`, который сообщает о каждом запросе на открытие или закрытие вместе с причиной: `"hover"` (указатель), `"focus"` (фокус клавиатуры), `"triggerPress"` (нажатие на сенсорном экране), `"pressOutside"` или `"cancel"` (Escape или «назад»).

```tsx
const open = signal(false);

<HoverCard open={open()} onOpenChange={(e) => open.set(e.open)} trigger={<Text>@ada</Text>}>
  <Profile id="ada" />
</HoverCard>
```

<a id="entrance-and-exit"></a>

## Появление и исчезновение

Карточка появляется и исчезает с вашими переходами, как и [Popover](https://nsh.dev/translations/ru/elements/popover.md). `popupStartingClass` — это вид всплывающего окна в его первом кадре, а классы `transition-*` из `popupClass` ведут его к собственному виду. `popupEndingClass` — его вид после закрытия: пока эти переходы выполняются, карточка остаётся, не принимая указатель, затем исчезает, а за этим следует `onClose`. Если снова задержаться на триггере, пока карточка исчезает, она вернётся. Когда пользователь просит уменьшить анимацию, карточка появляется и исчезает мгновенно.

```tsx
<HoverCard
  popupClass="rounded-xl bg-popover p-4 shadow-lg transition-all duration-150 ease-out"
  popupStartingClass="opacity-0 scale-95"
  popupEndingClass="opacity-0 scale-95"
  trigger={<Text>@ada</Text>}
>
  <Profile id="ada" />
</HoverCard>
```

По умолчанию она располагается под триггером (`placement`, `align`), с другой стороны — если места нет, и остаётся внутри безопасной области. `onPosition({ placement, bounds, available })` сообщает, где она оказалась.

<a id="props"></a>

## Свойства

| Свойство | Rust | Тип | По умолчанию | Описание |
|---|---|---|---|---|
| `open` | `.open(..)` | `boolean` | нет | Открыта ли карточка; управляется приложением. |
| `defaultOpen` | `.default_open(..)` | `boolean` | `false` | Открыта ли карточка изначально, без `open`. |
| `openDelay` | `.open_delay(..)` | `number` | `600` | Миллисекунды, в течение которых указатель задерживается на триггере или фокус остаётся на нём, прежде чем карточка откроется; `Infinity`: никогда сама по себе. |
| `closeDelay` | `.close_delay(..)` | `number` | `300` | Миллисекунды, в течение которых указатель находится вне триггера и карточки, а фокуса нет ни в одном из них, прежде чем карточка закроется; `Infinity`: никогда сама по себе. |
| `placement` | `.placement(..)` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Сторона триггера, с которой располагается карточка. |
| `align` | `.align(..)` | `"start" \| "center" \| "end"` | `"center"` | Как она выравнивается относительно триггера вдоль этой стороны. |
| `offset` | `.offset(..)` | `number` | `0` | Зазор между триггером и карточкой. |
| `margin` | `.margin(..)` | `number` | `0` | Расстояние, которое она держит от краёв безопасной области. |
| `popupClass`, `popupStyle` | `.popup_class(..)`, `.popup_style(..)` | `string`, `StyleProp<ViewStyle>` | нет | Вид всплывающего окна — карточки, в которой находится содержимое. |
| `popupStartingClass`, `popupStartingStyle` | `.popup_starting_class(..)`, `.popup_starting_style(..)` | `string`, `StyleProp<ViewStyle>` | нет | Вид всплывающего окна в его первом кадре поверх его собственного: от него запускаются переходы. |
| `popupEndingClass`, `popupEndingStyle` | `.popup_ending_class(..)`, `.popup_ending_style(..)` | `string`, `StyleProp<ViewStyle>` | нет | Вид всплывающего окна после закрытия поверх его собственного: он сохраняется, пока выполняются переходы. |
| `trigger` | `.trigger(..)` | `JSX.Element` | нет | То, превью чего показывается; карточка позиционируется относительно него. |
| `children` | `.child(..)` | `JSX.Element` | нет | Содержимое, создаваемое при каждом открытии. |

<a id="events"></a>

## События

| Событие | Rust | Описание |
|---|---|---|
| `onOpenChange` | `.on_open_change(\|e\| ..)` | Запрашивает открытие или закрытие с указанием причины: `"hover"`, `"focus"`, `"triggerPress"`, `"pressOutside"` или `"cancel"`. |
| `onOpen` | `.on_open(\|\| ..)` | Карточка показана. |
| `onClose` | `.on_close(\|\| ..)` | Карточка исчезла, после завершения выхода; также когда её закрыло приложение. |
| `onPosition` | `.on_position(\|e\| ..)` | Всплывающее окно размещено: `placement` (сторона, на которой оно оказалось), `bounds` (его рамка), `available` (место на этой стороне). |

<a id="accessibility"></a>

## Доступность

Карточка при наведении добавляет возможности своему триггеру: триггер сохраняет свою роль и подпись, а то, что он открывает или делает при нажатии, остаётся прежним. Пользователи клавиатуры получают карточку, когда фокус задерживается на триггере, а Escape закрывает её. Сама карточка не забирает фокус, поэтому Tab переходит дальше с триггера, как и раньше. Ссылки и кнопки в карточке получают фокус, как и везде, и карточка остаётся открытой, пока фокус находится на одной из них; всё важное в карточке должно быть также доступно со страницы, на которую ведёт триггер.
