---
url: https://nsh.dev/ru/elements/sheet
description: "Компонент Sheet: панель у края экрана поверх подложки, закрывается нажатием на подложку, клавишей Escape и кнопкой «Назад», стилизуется приложением."
demo: https://nsh.dev/play/index.html#el-sheet
---

# Sheet

Sheet — это панель у края экрана поверх подложки, которая закрывает всё остальное: фильтры, панель «Поделиться», боковое меню. Нажатие на подложку, Escape, кнопка «Назад» на Android и жест выхода VoiceOver закрывают её. Она показывается поверх всего в своём окне, одинаково на любом устройстве, и ничего не рисует сама: панель и подложка принадлежат приложению.

```rust
use nsh_base::{SheetEvent, sheet};
use nsh_core::prelude::*;

fn filters(open: Signal<bool>) -> impl IntoElement {
    sheet()
        .open(move || Some(open.get()))
        .on_open_change(move |e: &SheetEvent| {
            if let SheetEvent::OpenChange(next, _) = e {
                open.set(*next);
            }
        })
        .child(
            view()
                .gap_3()
                .p_6()
                .rounded_t(px(16.0))
                .bg(white())
                .child(text("Filters").font_weight(FontWeight::SEMIBOLD))
                .child(view().on_press(move || open.set(false)).child(text("Done"))),
        )
}
```

```tsx
import { signal, Text, View } from "nsh";
import { Sheet } from "nsh/base";

function Filters() {
  const open = signal(false);
  return (
    <>
      <View onPress={() => open.set(true)}><Text>Filters</Text></View>
      <Sheet
        open={open()}
        onOpenChange={(e) => open.set(e.open)}
        popupClass="gap-3 rounded-t-2xl bg-white p-6"
        backdropClass="bg-black/40"
      >
        <Text class="font-semibold">Filters</Text>
        <View onPress={() => open.set(false)}><Text>Done</Text></View>
      </Sheet>
    </>
  );
}
```

`Sheet` берётся из [`nsh/base`](https://nsh.dev/translations/ru/modules/nsh-base.md). Его содержимое создаётся заново при каждом открытии. `side` размещает его у `"bottom"` (по умолчанию) или `"top"` края экрана, по всей ширине экрана, либо у `"left"` или `"right"` края, по всей высоте экрана; размер поперёк определяется самой панелью: по её содержимому или через `popupClass`.

Анимация выезда следует за измеренным размером панели, пока содержимое создаётся или растёт во время появления. Ленивое содержимое и асинхронные обновления размера используют общий прогресс анимации; панель останавливается в своём текущем размере.

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

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

Без `open` sheet хранит собственное состояние, начиная с `defaultOpen`. С `open` состоянием владеет приложение: изменяйте его в `onOpenChange({ open, reason })`, который сообщает о каждом запросе на закрытие вместе с причиной (`"backdropPress"`, `"cancel"`, `"swipe"`). `onOpen` и `onClose` отслеживают его появление и скрытие независимо от того, кто открыл или закрыл sheet.

`closeOnEscape={false}` оставляет sheet открытым при Escape и «Назад», `closeOnBackdropPress={false}` — при нажатиях на подложку; `onCancel` всё равно получает эти события, чтобы можно было сначала спросить.

<a id="dragging-a-bottom-sheet"></a>

## Перетаскивание нижней панели

Задайте `closeOnSwipe`, чтобы нижняя панель следовала за перетаскиванием вниз. Подложка плавно гаснет по мере смещения; если отпустить после смещения более чем на 30% высоты панели или при движении вниз со скоростью более 800 точек в секунду, будет запрошено закрытие с причиной `"swipe"`. Более короткое перетаскивание плавно возвращает панель на место. Управляющий владелец может оставить `open` равным true, чтобы вернуть панель после запроса на закрытие. При включённом уменьшении анимации отпускание и программное открытие и закрытие завершаются мгновенно; прямое перетаскивание по-прежнему следует за пальцем.

Панель подстраивается под содержимое, включая короткие меню; используйте `popupStyle` или высоту дочернего элемента, чтобы ограничить более высокое содержимое. Маркер (handle) приложение рисует само: это может быть любое представление внутри панели. На iOS вложенный вертикальный `Scroll` или `List` поглощает перетаскивание до своей верхней границы, а оставшееся движение вниз передаёт панели в рамках того же жеста. На Android, на десктопах и в вебе перетаскивание маркера или содержимого вне прокручиваемой области использует их нативную поддержку панорамирования; вложенные прокручиваемые области сохраняют обычное для своей платформы разрешение конфликтов жестов. Если задать `closeOnSwipe` равным false, панель остаётся неподвижной, а смонтированное содержимое и позиция прокрутки сохраняются. Остальные края сохраняют прежнее поведение.

<a id="native-sheets"></a>

## Нативные sheet

Sheet рисуется NSH в окне приложения и оформляется приложением. Для собственной представляемой платформой панели (page sheet на iOS) используйте [Dialog](https://nsh.dev/translations/ru/elements/dialog.md) с `presentation="pageSheet"`.

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

## Свойства

| Свойство | Rust | Тип | По умолчанию | Описание |
|---|---|---|---|---|
| `open` | `.open(..)` | `boolean` | нет | Открыт, управляется приложением. |
| `defaultOpen` | `.default_open(..)` | `boolean` | `false` | Изначально открыт, без `open`. |
| `side` | `.side(..)` | `"top" \| "bottom" \| "left" \| "right"` | `"bottom"` | Край, у которого он расположен. |
| `closeOnEscape` | `.close_on_escape(..)` | `boolean` | `true` | Escape и кнопка «Назад» на Android закрывают его. |
| `closeOnBackdropPress` | `.close_on_backdrop_press(..)` | `boolean` | `true` | Нажатие на подложку закрывает его. |
| `closeOnSwipe` | `.close_on_swipe(..)` | `boolean` | `false` | Нижняя панель следует за перетаскиванием вниз и закрывается при достаточном смещении. |
| `popupClass`, `popupStyle` | `.popup_class(..)`, `.popup_style(..)` | `string`, `StyleProp<ViewStyle>` | нет | Внешний вид панели. |
| `backdropClass`, `backdropStyle` | `.backdrop_class(..)`, `.backdrop_style(..)` | `string`, `StyleProp<ViewStyle>` | нет | Внешний вид подложки, например полупрозрачный цвет. |
| `children` | `.child(..)` | `JSX.Element` | нет | Содержимое. |

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

## События

| Событие | Rust | Описание |
|---|---|---|
| `onOpenChange` | `.on_open_change(\|e\| ..)` | Запрос на закрытие с причиной: `"backdropPress"`, `"cancel"` или `"swipe"`. |
| `onOpen` | `.on_open(\|\| ..)` | Sheet появился: он полностью отображён. |
| `onClose` | `.on_close(\|\| ..)` | Sheet скрылся: он полностью убран. Также срабатывает, когда его закрыло приложение. |
| `onCancel` | `.on_cancel(\|\| ..)` | Пользователь попытался закрыть его (Escape или «Назад», нажатие на подложку, включённый свайп), независимо от того, закрылся он или нет. |

В вебе встраивание через `dialogs: "viewport"` показывает sheet и его подложку поверх страницы-хоста, включая iframe того же источника. Для фреймов с другого источника используйте [`hostPlayerFrame`](https://nsh.dev/translations/ru/docs/web.md#embed-it-in-a-page).
