npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cloud-ru/ds-drawer

v1.2.1

Published

Пакет выезжающих панелей — компоненты Drawer и DrawerCustom с едиными токенами позиции и ширины.

Readme

Drawer

@cloud-ru/ds-drawer — Пакет выезжающих панелей — компоненты Drawer и DrawerCustom с едиными токенами позиции и ширины.

Пакет @cloud-ru/ds-drawer предоставляет два компонента для боковых/нижних панелей: готовую сборку Drawer с пресетной шапкой, телом и футером и низкоуровневый DrawerCustom для ручной композиции.

  • Drawer — готовая панель с шапкой, прокручиваемым телом и опциональным футером. Покрывает 90% сценариев.
  • DrawerCustom — низкоуровневая версия без предопределённой структуры: собирайте из DrawerCustom.Header, .Body, .Footer или собственной разметки.

Установка

pnpm add @cloud-ru/ds-drawer
import { Drawer, DrawerCustom, POSITION, WIDTH } from '@cloud-ru/ds-drawer'

Смотри также

  • Modal — центрированное модальное окно для блокирующих подтверждений.
  • Popover — всплывающий слой рядом с триггером.

Drawer

Панель, выезжающая со стороны экрана — пресетная шапка, прокручиваемое тело и опциональный футер.

Панель, выезжающая со стороны экрана — для дополнительного контекста, фильтров, форм и пошаговых сценариев. Drawer собирает шапку (медиа, заголовок, подзаголовок, back-button, слот после заголовка), прокручиваемое тело и опциональный футер. Для ручной композиции используйте DrawerCustom.

Когда использовать

  • Форма, фильтры или детали, которые не помещаются в основной поток и требуют отдельного контекста.
  • Пошаговый сценарий с кнопкой «назад» в шапке — возвращение между экранами без потери контекста.
  • Нижний лист (bottom sheet) для компактных действий и подтверждений на мобильных устройствах.
  • Стек из двух-трёх связанных панелей через nestedDrawer.

Когда не нужен: критическое подтверждение, блокирующее остальной интерфейс (берите Modal), всплывающий поповер рядом с элементом (Popover), тост-уведомление (не блокирует UI).

Примеры использования

Базовое использование

Контролируемое open/onClose, footer из ButtonGroup.

import { Button, ButtonGroup } from '@cloud-ru/ds-button';
import { Drawer } from '@cloud-ru/ds-drawer';
import { useState } from 'react';

export function Basic() {
  const [open, setOpen] = useState(false);
  const close = () => setOpen(false);

  return (
    <>
      <Button label='Открыть' appearance='primary' view='filled' onClick={() => setOpen(true)} />
      <Drawer
        open={open}
        position='right'
        onClose={close}
        title='Заголовок'
        subtitle='Короткое пояснение сценария'
        content='Основной контент тела. Сюда помещается форма, предупреждение или подробный текст.'
        footer={
          <ButtonGroup
            primaryAction={{ label: 'Продолжить', view: 'filled', onClick: close }}
            secondaryAction={{ label: 'Отмена', appearance: 'neutral', view: 'outline', onClick: close }}
          />
        }
      />
    </>
  );
}

Критическое действие

Critical primary, neutral outline secondary.

import { Button, ButtonGroup } from '@cloud-ru/ds-button';
import { Drawer } from '@cloud-ru/ds-drawer';
import { useState } from 'react';

export function WithFooter() {
  const [open, setOpen] = useState(false);
  const close = () => setOpen(false);

  return (
    <>
      <Button label='Удалить…' appearance='critical' view='outline' onClick={() => setOpen(true)} />
      <Drawer
        open={open}
        position='right'
        onClose={close}
        title='Удалить запись'
        subtitle='Действие необратимо.'
        content='После подтверждения запись и все её ссылки исчезнут из списка.'
        footer={
          <ButtonGroup
            primaryAction={{
              label: 'Удалить',
              appearance: 'critical',
              view: 'filled',
              onClick: close,
            }}
            secondaryAction={{
              label: 'Отмена',
              appearance: 'neutral',
              view: 'outline',
              onClick: close,
            }}
          />
        }
      />
    </>
  );
}

С медиа-слотом

media рендерится над шапкой на всю ширину панели.

import { Button } from '@cloud-ru/ds-button';
import { Drawer } from '@cloud-ru/ds-drawer';
import { useState } from 'react';

export function WithMedia() {
  const [open, setOpen] = useState(false);
  const close = () => setOpen(false);

  return (
    <>
      <Button label='Открыть онбординг' appearance='primary' view='filled' onClick={() => setOpen(true)} />
      <Drawer
        open={open}
        position='right'
        width='m'
        onClose={close}
        media={
          <div
            style={{
              height: 200,
              background: 'linear-gradient(135deg, rgb(102 126 234), rgb(118 75 162))',
            }}
          />
        }
        title='Добро пожаловать'
        subtitle='Кратко о том, что изменилось в этой версии.'
        content='Список ключевых улучшений и ссылки на подробности могут размещаться в теле.'
      />
    </>
  );
}

Bottom sheet

position="bottom" + heightAuto — высота по контенту.

import { Button } from '@cloud-ru/ds-button';
import { Drawer } from '@cloud-ru/ds-drawer';
import { useState } from 'react';

export function HeightAuto() {
  const [open, setOpen] = useState(false);
  const close = () => setOpen(false);

  return (
    <>
      <Button label='Открыть bottom sheet' appearance='primary' view='filled' onClick={() => setOpen(true)} />
      <Drawer
        open={open}
        position='bottom'
        heightAuto
        onClose={close}
        title='Bottom sheet'
        subtitle='Высота рассчитывается по контенту'
        content='Подходит для компактных подтверждений на мобильных устройствах.'
      />
    </>
  );
}

Вложенный Drawer

Родитель сдвигается влево при открытии дочернего.

import { Button } from '@cloud-ru/ds-button';
import { Drawer } from '@cloud-ru/ds-drawer';
import { useState } from 'react';

export function NestedDrawer() {
  const [outerOpen, setOuterOpen] = useState(false);
  const [innerOpen, setInnerOpen] = useState(false);

  const closeAll = () => {
    setInnerOpen(false);
    setOuterOpen(false);
  };

  return (
    <>
      <Button label='Открыть родительский' appearance='primary' view='filled' onClick={() => setOuterOpen(true)} />
      <Drawer
        open={outerOpen}
        position='right'
        width='m'
        onClose={closeAll}
        title='Родительский Drawer'
        subtitle='При открытии вложенного — родитель сдвигается влево.'
        content={
          <Button label='Открыть вложенный' appearance='primary' view='outline' onClick={() => setInnerOpen(true)} />
        }
        nestedDrawer={
          <Drawer
            open={innerOpen}
            position='right'
            width='s'
            onClose={() => setInnerOpen(false)}
            title='Вложенный Drawer'
            subtitle='Кнопка «назад» возвращает к родителю'
            onBackButtonClick={() => setInnerOpen(false)}
            content='Вложенный Drawer рендерится через проп nestedDrawer родителя.'
          />
        }
      />
    </>
  );
}

Props

DrawerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | additionalButton | BottomSheetActionButton | — | Дополнительная (третья) кнопка — пропсы Button (дефолт view='simple', appearance='neutral'). | | approveButton | BottomSheetActionButton | — | Основная кнопка действия — пропсы Button (дефолт view='filled', appearance='primary'). | | cancelButton | BottomSheetActionButton | — | Кнопка отмены — объект пропсов Button (по умолчанию view='outline', appearance='neutral'). | | children | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | | | className | string | — | CSS-класс для элемента с контентом CSS-класс | | closeOnPopstate | boolean | — | Закрывать дровер при перемещении по истории браузера | | container | string \| HTMLElement | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body | | content | ReactNode | — | Содержимое body (альтернатива children). | | contentRef | Ref<HTMLElement> | — | Ссылка на скроллируемый контейнер контента (например, для дозагрузки по скроллу). | | data-test-id | string | — | | | disableMotions | boolean | false | Отключить анимации | | footer | (ReactElement<any, string \| JSXElementConstructor<any>> & (string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<...> \| ReactPortal \| null)) | — | Футер Произвольный футер. Приоритетнее approveButton / cancelButton / additionalButton. | | footerActionsOrientation | "horizontal" | "vertical" | 'horizontal' | Ориентация кнопок футера. Применяется только при двух кнопках; игнорируется при заданном footer. | | heightAuto | boolean | false | Высота панели по контенту (только при position: "top" \| "bottom"). | | media | ReactNode | — | Медиа-контент | | nestedDrawer | DrawerProps | — | Вложенный Drawer | | onBackButtonClick | (() => void) | — | Callback клика на back-кнопку (слева в шапке). Наличие callback'а авто-рендерит Button view='function' icon={<ArrowLeftSVG />}. | | onClose | () => void | — | Колбэк закрытия | | onSnapIndexChange | ((snapIndex: number) => void) | — | Callback изменения активного snap'а (пересечение swipe-границы или click по UI). Не вызывается при программной смене controlled snapIndex. | | open | boolean | — | Управление состоянием показан/не показан. | | position | "bottom" | "left" | "right" | "top" | — | Расположение | | rootClassName | string | — | CSS-класс для корневого элемента | | safeArea | boolean | true | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом на .content через env(safe-area-inset-*): на устройстве без выреза/индикатора (и на desktop) inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). | | showBlackout | boolean | true | Отображение темной подложки | | showButtonClosed | boolean | true | Отображение кнопки закрытия в шапке дровера | | slotAfterTitle | ReactNode | — | Slot справа от title (например, QuestionTooltip из @cloud-ru/ds-tooltip). | | slotSecondTitle | ReactNode | — | Slot под подзаголовком — типично SearchBar, SegmentControl или Filter. Рендерится на обеих поверхностях: secondWrapper в мастере bottomSheet, subHeadlineWrapper в мастере window (modal / drawer). | | snapIndex | number | — | Controlled-индекс активного snap'а. Если задан, sheet всегда находится на этом snap'е; swipe-up/down вызывают onSnapIndexChange, но не меняют позицию сами — consumer должен передать новое значение. | | snapPoints | SnapPoint | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту undefined sheet height: auto с одним snap'ом по высоте контента. Пример: [0.5, 1] — sheet открывается на половину экрана, drag вверх раскрывает до full-viewport; drag вниз ниже 0.5 ведёт к закрытию. Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на стороне потребителя): - строго по возрастанию: индекс 0 — самая компактная позиция, последний — top / expanded; - значения должны резолвиться в различные высоты (['50%', 0.5] на типичном вьюпорте дадут одну высоту → дубль-индекс будет недостижим свайпом); - 'fit-content' имеет смысл только как ЕДИНСТВЕННЫЙ snap (без snapPoints); внутри массива фиксированных позиций его «контентная» высота не определена. | | subtitle | ReactNode | — | Текстовая строка-подзаголовок под title. | | swipeEnabled | boolean | true | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. При swipeEnabled=false snap-point по-прежнему можно переключить через controlled snapIndex prop'ом. | | title | ReactNode | — | Заголовок. Типографика зависит от поверхности: title-l на sheet, headline-s на window (modal/drawer). | | truncate | { title?: number; subtitle?: number; } \| undefined | — | Усечение строковых title/subtitle через TruncateString (число строк). Применяется только когда задано — по умолчанию текст не усекается. Актуально для window-поверхности (modal/drawer), где длинный заголовок иначе переносится на несколько строк. | | width | Width | 's' | Ширина (только при position: "left" | "right") | | withDividers | boolean | false | Только mobile: разделители между шапкой/контентом/футером sheet'а. |

Related types

DrawerProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | additionalButton | BottomSheetActionButton | — | Дополнительная (третья) кнопка — пропсы Button (дефолт view='simple', appearance='neutral'). | | approveButton | BottomSheetActionButton | — | Основная кнопка действия — пропсы Button (дефолт view='filled', appearance='primary'). | | cancelButton | BottomSheetActionButton | — | Кнопка отмены — объект пропсов Button (по умолчанию view='outline', appearance='neutral'). | | children | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | | | className | string \| undefined | — | CSS-класс для элемента с контентом CSS-класс | | closeOnPopstate | boolean \| undefined | — | Закрывать дровер при перемещении по истории браузера | | container | string \| HTMLElement \| undefined | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body | | content | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | Содержимое body (альтернатива children). | | contentRef | Ref<HTMLElement> \| undefined | — | Ссылка на скроллируемый контейнер контента (например, для дозагрузки по скроллу). | | data-test-id | string \| undefined | — | | | disableMotions | boolean \| undefined | — | Отключить анимации | | footer | (ReactElement<any, string \| JSXElementConstructor<any>> & (string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null)) \| undefined | — | Футер Произвольный футер. Приоритетнее approveButton / cancelButton / additionalButton. | | footerActionsOrientation | "horizontal" | "vertical" | — | Ориентация кнопок футера. Применяется только при двух кнопках; игнорируется при заданном footer. | | heightAuto | boolean \| undefined | — | Высота панели по контенту (только при position: "top" \| "bottom"). | | media | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | Медиа-контент | | nestedDrawer | DrawerProps | — | Вложенный Drawer | | onBackButtonClick | (() => void) \| undefined | — | Callback клика на back-кнопку (слева в шапке). Наличие callback'а авто-рендерит Button view='function' icon={<ArrowLeftSVG />}. | | onClose | () => void | — | Колбэк закрытия | | onSnapIndexChange | ((snapIndex: number) => void) \| undefined | — | Callback изменения активного snap'а (пересечение swipe-границы или click по UI). Не вызывается при программной смене controlled snapIndex. | | open | boolean | — | Управление состоянием показан/не показан. | | position | "bottom" | "left" | "right" | "top" | — | Расположение | | rootClassName | string \| undefined | — | CSS-класс для корневого элемента | | safeArea | boolean \| undefined | — | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом на .content через env(safe-area-inset-*): на устройстве без выреза/индикатора (и на desktop) inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). | | showBlackout | boolean \| undefined | — | Отображение темной подложки | | showButtonClosed | boolean \| undefined | — | Отображение кнопки закрытия в шапке дровера | | slotAfterTitle | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | Slot справа от title (например, QuestionTooltip из @cloud-ru/ds-tooltip). | | slotSecondTitle | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | Slot под подзаголовком — типично SearchBar, SegmentControl или Filter. Рендерится на обеих поверхностях: secondWrapper в мастере bottomSheet, subHeadlineWrapper в мастере window (modal / drawer). | | snapIndex | number \| undefined | — | Controlled-индекс активного snap'а. Если задан, sheet всегда находится на этом snap'е; swipe-up/down вызывают onSnapIndexChange, но не меняют позицию сами — consumer должен передать новое значение. | | snapPoints | SnapPoint | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту undefined sheet height: auto с одним snap'ом по высоте контента. Пример: [0.5, 1] — sheet открывается на половину экрана, drag вверх раскрывает до full-viewport; drag вниз ниже 0.5 ведёт к закрытию. Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на стороне потребителя): - строго по возрастанию: индекс 0 — самая компактная позиция, последний — top / expanded; - значения должны резолвиться в различные высоты (['50%', 0.5] на типичном вьюпорте дадут одну высоту → дубль-индекс будет недостижим свайпом); - 'fit-content' имеет смысл только как ЕДИНСТВЕННЫЙ snap (без snapPoints); внутри массива фиксированных позиций его «контентная» высота не определена. | | subtitle | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | Текстовая строка-подзаголовок под title. | | swipeEnabled | boolean \| undefined | — | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. При swipeEnabled=false snap-point по-прежнему можно переключить через controlled snapIndex prop'ом. | | title | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | Заголовок. Типографика зависит от поверхности: title-l на sheet, headline-s на window (modal/drawer). | | truncate | { title?: number; subtitle?: number; } \| undefined | — | Усечение строковых title/subtitle через TruncateString (число строк). Применяется только когда задано — по умолчанию текст не усекается. Актуально для window-поверхности (modal/drawer), где длинный заголовок иначе переносится на несколько строк. | | width | Width | — | Ширина (только при position: "left" | "right") | | withDividers | boolean \| undefined | — | Только mobile: разделители между шапкой/контентом/футером sheet'а. |

  • Position = "bottom" | "left" | "right" | "top"

  • Width = "l" | "m" | "s"

Адаптивность

Drawer — адаптивный компонент с переключением поверхности (surface-swap). Раскладку он берёт из AdaptiveProvider (контекст @cloud-ru/ds-adaptive); публичный API единый для обеих платформ:

  • desktop (по умолчанию) — панель, выезжающая со стороны экрана (left/right/top/bottom).
  • mobile — контент рендерится в BottomSheet из @cloud-ru/ds-bottom-sheet (панель снизу со свайпом для закрытия).

Верстайте под desktop и поставьте один <AdaptiveProvider> в корне приложения — mobile-поверхность включается автоматически (desktop-first). Пропа layoutType у компонента нет: источник раскладки — только контекст.

Как форсировать платформу

Форс — только контекстом, не пропом:

  • Поддерево — вложенный провайдер:
    import { AdaptiveProvider } from '@cloud-ru/ds-adaptive'
    
    <AdaptiveProvider layoutType='mobile'>
      <Drawer open={open} onClose={close} content={…} />
    </AdaptiveProvider>
  • Отдельный компонент — withLayoutType (module-scope, сахар над провайдером):
    import { withLayoutType } from '@cloud-ru/ds-adaptive'
    import { Drawer } from '@cloud-ru/ds-drawer'
    
    const MobileDrawer = withLayoutType(Drawer, 'mobile')

Платформенные пропы

Часть пропов управляет геометрией desktop-панели и на mobile молча игнорируется (у BottomSheet своя поверхность снизу). Таблица синхронизирована с type-level JSDoc у DrawerProps.

| Пропы | desktop | mobile | |-------|---------|--------| | position, width, heightAuto, nestedDrawer | используется | игнорируется | | title, subtitle, slotAfterTitle, onBackButtonClick, media, content, footer | используется | используется | | open, onClose, showBlackout, container, closeOnPopstate, className, rootClassName | используется | используется |

Подробнее о модели адаптивности — Adaptive.

DrawerCustom

Низкоуровневая сборка Drawer — полный контроль над разметкой через Header / Body / Footer субкомпоненты.

DrawerCustom — низкоуровневая версия Drawer, которая не диктует структуру содержимого. Вы сами компонуете шапку, тело и футер из субкомпонентов DrawerCustom.Header, .Body, .Footer или собственной разметки.

Используйте DrawerCustom, когда стандартной шапки из Drawer недостаточно — например, нужна своя раскладка заголовка с несколькими действиями, кастомный футер с группами кнопок или нестандартный порядок секций.

Когда использовать

  • Стандартная шапка / футер из Drawer не подходят — нужна своя разметка.
  • Сложная раскладка нескольких секций внутри одной панели.
  • Кастомные слоты (например, фиксированный поиск между шапкой и телом).

Во всех остальных случаях предпочтительнее Drawer — он дешевле в поддержке и даёт консистентные отступы.

Анатомия

Position

Сторона, с которой выезжает панель: right — стандартный side-panel (по умолчанию), left — для навигации и фильтров, top/bottom — для уведомлений и bottom-sheets на мобильных.

Width

Предустановленная ширина панели для position: left | right: s — для узких форм и фильтров, m — дефолт, l — для сложных форм и просмотрщиков. Также принимает число или строку CSS для точного контроля.

Примеры использования

Ручная композиция

Header + Body + Footer собираются вручную.

import { Button } from '@cloud-ru/ds-button';
import { DrawerCustom } from '@cloud-ru/ds-drawer';
import { useState } from 'react';

export function CustomComposition() {
  const [open, setOpen] = useState(false);
  const close = () => setOpen(false);

  return (
    <>
      <Button label='Открыть custom drawer' appearance='primary' view='filled' onClick={() => setOpen(true)} />
      <DrawerCustom open={open} position='right' width='s' onClose={close}>
        <DrawerCustom.Header title='Ручная композиция' subtitle='Header + Body + Footer собираются вручную.' />
        <DrawerCustom.Body
          content={
            <div style={{ padding: 24 }}>
              <p>Тело Drawer собирается из произвольной разметки.</p>
              <p>Скролл включается автоматически при большом содержимом.</p>
            </div>
          }
        />
        <DrawerCustom.Footer>
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <Button label='Закрыть' appearance='neutral' view='outline' onClick={close} />
            <Button label='Подтвердить' appearance='primary' view='filled' onClick={close} />
          </div>
        </DrawerCustom.Footer>
      </DrawerCustom>
    </>
  );
}

Props

DrawerCustomProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | | | className | string | — | CSS-класс для элемента с контентом | | closeOnPopstate | boolean | — | Закрывать дровер при перемещении по истории браузера | | container | string \| HTMLElement | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body | | data-test-id | string | — | | | disableMotions | boolean | false | Отключить анимации | | footer | ReactElement<any, string \| JSXElementConstructor<any>> | — | Футер | | heightAuto | boolean | false | Высота панели по контенту (только при position: "top" \| "bottom"). | | nestedDrawer | DrawerCustomProps | — | Вложенный Drawer | | onClose | () => void | — | Колбэк закрытия | | open | boolean | — | Управление состоянием показан/не показан. | | position | "bottom" | "left" | "right" | "top" | — | Расположение | | push | boolean \| PushConfig | — | Смещение при открытии "вложенного" компонента | | resizable | { min: number; max?: number; default?: number; onResize?: ((width: number) => void) \| undefined; onResizeEnd?: ((width: number) => void) \| undefined; draggerTooltip?: string \| undefined; } \| undefined | 's' | Ширина (только при position: "left" | "right") | | rootClassName | string | — | CSS-класс для корневого элемента | | safeArea | boolean | true | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом на .content через env(safe-area-inset-*): на устройстве без выреза/индикатора (и на desktop) inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). | | showBlackout | boolean | true | Отображение темной подложки | | showButtonClosed | boolean | true | Отображение кнопки закрытия в шапке дровера | | snapPoints | SnapPoint | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту undefined sheet height: auto с одним snap'ом по высоте контента. Пример: [0.5, 1] — sheet открывается на половину экрана, drag вверх раскрывает до full-viewport; drag вниз ниже 0.5 ведёт к закрытию. Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на стороне потребителя): - строго по возрастанию: индекс 0 — самая компактная позиция, последний — top / expanded; - значения должны резолвиться в различные высоты (['50%', 0.5] на типичном вьюпорте дадут одну высоту → дубль-индекс будет недостижим свайпом); - 'fit-content' имеет смысл только как ЕДИНСТВЕННЫЙ snap (без snapPoints); внутри массива фиксированных позиций его «контентная» высота не определена. | | swipeEnabled | boolean | true | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. При swipeEnabled=false snap-point по-прежнему можно переключить через controlled snapIndex prop'ом. | | width | Width | 's' | Ширина (только при position: "left" | "right") |

Related types

DrawerCustomProps

| Prop | Type | Default | Description | |------|------|---------|-------------| | children | string \| number \| boolean \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| null \| undefined | — | | | className | string \| undefined | — | CSS-класс для элемента с контентом | | closeOnPopstate | boolean \| undefined | — | Закрывать дровер при перемещении по истории браузера | | container | string \| HTMLElement \| undefined | — | Контейнер в котором будет рендерится Drawer. По-умолчанию - body | | data-test-id | string \| undefined | — | | | disableMotions | boolean \| undefined | — | Отключить анимации | | footer | ReactElement<any, string \| JSXElementConstructor<any>> \| undefined | — | Футер | | heightAuto | boolean \| undefined | — | Высота панели по контенту (только при position: "top" \| "bottom"). | | nestedDrawer | DrawerCustomProps | — | Вложенный Drawer | | onClose | () => void | — | Колбэк закрытия | | open | boolean | — | Управление состоянием показан/не показан. | | position | "bottom" | "left" | "right" | "top" | — | Расположение | | push | boolean \| PushConfig \| undefined | — | Смещение при открытии "вложенного" компонента | | resizable | { min: number; max?: number; default?: number; onResize?: (width: number) => void; onResizeEnd?: (width: number) => void; draggerTooltip?: string; } \| undefined | — | Ширина (только при position: "left" | "right") | | rootClassName | string \| undefined | — | CSS-класс для корневого элемента | | safeArea | boolean \| undefined | — | Резервировать ли место под iOS notch / home-indicator и Android nav-bar. Реализовано паддингом на .content через env(safe-area-inset-*): на устройстве без выреза/индикатора (и на desktop) inset = 0, поэтому никакого «лишнего» отступа не появляется; на notched-устройстве — ровно нужный. Верхний отступ добавляется только когда sheet раскрыт на полный вьюпорт (его верх под notch). | | showBlackout | boolean \| undefined | — | Отображение темной подложки | | showButtonClosed | boolean \| undefined | — | Отображение кнопки закрытия в шапке дровера | | snapPoints | SnapPoint | — | Массив фиксированных позиций sheet'а от меньшей к большей. По дефолту undefined sheet height: auto с одним snap'ом по высоте контента. Пример: [0.5, 1] — sheet открывается на половину экрана, drag вверх раскрывает до full-viewport; drag вниз ниже 0.5 ведёт к закрытию. Контракт массива (движок не сортирует и не дедуплицирует — порядок и различимость на стороне потребителя): - строго по возрастанию: индекс 0 — самая компактная позиция, последний — top / expanded; - значения должны резолвиться в различные высоты (['50%', 0.5] на типичном вьюпорте дадут одну высоту → дубль-индекс будет недостижим свайпом); - 'fit-content' имеет смысл только как ЕДИНСТВЕННЫЙ snap (без snapPoints); внутри массива фиксированных позиций его «контентная» высота не определена. | | swipeEnabled | boolean \| undefined | — | Включает swipe-down для закрытия / swipe-up для раскрытия на следующий snap-point. При swipeEnabled=false snap-point по-прежнему можно переключить через controlled snapIndex prop'ом. | | width | Width | — | Ширина (только при position: "left" | "right") |

  • Position = "bottom" | "left" | "right" | "top"

  • Width = "l" | "m" | "s"