JGuang

抽屉

Drawer · Mantine Core

交互预览
npm install @mantine/core@9.7.1 @mantine/hooks@9.7.1

抽屉

Mantine Core 的独立组件,请不要替换为 JGuang UI/Base UI 的同名 API。

安装

npm install @mantine/core@9.7.1 @mantine/hooks@9.7.1

入口引入 @mantine/core/styles.css,并在根组件使用 MantineProvider;通过 forceColorScheme 同步深浅色主题。

使用与验证

在左侧选择组件,在预览中操作它的状态;使用“查看代码”检查真实源码。安装后执行类型检查和构建,再验证键盘焦点、手机布局与深浅色主题。

来源

https://github.com/mantinedev/mantine/tree/f0e2bc6d28b0f0b8aaefb5b859937bf77f539df7 MIT 许可证;不包括付费内容。下方提供上游英文参考内容,中文使用指南已适配;上游详细参考暂保留英文。


版本与兼容性

固定的 Mantine Core 9.7.1 要求 React 和 React DOM 满足 ^19.2.0,并配套 @mantine/hooks@9.7.1。此条件来自实际 npm 包的 peerDependencies。React 18 或其他不兼容版本不能强行安装;先向项目负责人说明升级影响并确认方案,不使用 --force/--legacy-peer-deps 掩盖冲突。入口的原生 Provider 开启 theme.respectReducedMotion,并用 forceColorScheme 控制深浅色。

适用场景

在侧滑面板中展示补充内容。

本地示例使用的原生参数

以下参数由本地、经过类型检查的 JSX 示例提取,按所属组件区分。不是全部属性清单;完整签名和继承关系见下方固定源码参考。

组件参数说明
Button.onClick本地点击处理,应按需替换为明确的业务操作。
Drawer.opened受控区域是否打开。
Drawer.onClose请求关闭,应更新宿主的打开状态。
Drawer.title当前组件的标题内容。
Drawer.closeButtonProps原生关闭控件参数,包括其无障碍名称。

交互与状态检查

当前场景:默认。切换支持的禁用、错误或只读场景后检查真实行为;无此原生状态的组件不会被强行添加统一属性。验证键盘焦点和退出方式,修改值后按原生回调签名连接业务状态。

业务接入边界

在隔离的 iframe 内保留原生 Portal。真实应用需要验证遮罩层级、Escape 关闭和焦点恢复。

宿主配置

先安装固定版本 @mantine/core 和 @mantine/hooks,在入口引入 @mantine/core/styles.css,并使用 MantineProvider。示例的 theme 和 variant 是 JGuang 编写的宿主参数,不是所有原生组件都接受的统一 API;通过 forceColorScheme 同步应用主题。源码面板中的原生实现仅供阅读,使用 npm 导出,不要单独复制内部实现。此批只覆盖 Core,不包括日期、图表和表单扩展包。上游详细说明保留英文,不宣称全文中文化。

可运行用法

以下代码使用资源原生 API;不是把其他设计系统的同名组件替换进来。

import { useState } from 'react';
import * as M from '@mantine/core';
import '@mantine/core/styles.css';


function ComponentPreview({ variant = 'Default' }: { variant?: string }) {
const [open, setOpen] = useState(false);
const content = <M.Text>Build something beautiful / 构建美好的界面</M.Text>;
return <><M.Button onClick={() => setOpen(true)}>Open drawer</M.Button><M.Drawer opened={open} onClose={() => setOpen(false)} title="Details" closeButtonProps={{ "aria-label": "Close drawer / 关闭抽屉" }}>{content}</M.Drawer></>;
}
export default function Example({ variant = 'Default', theme = 'light' }: { variant?: string; theme?: 'light' | 'dark' }) {
return <M.MantineProvider forceColorScheme={theme} theme={{ respectReducedMotion: true }}><ComponentPreview variant={variant} /></M.MantineProvider>;
}

可用状态与示例

默认

示例遵循 Mantine 原生 API。支持多状态的输入组件可将示例中的 variant 改为 Disabled、Invalid 或 Read only;这些只是示例标签,不是新增的组件属性。改造时保留组件要求的数据、受控值、store 和复合子组件结构。

Usage

Position

Drawer can be placed on left (default), top, right and bottom. Control the drawer position with the position prop, for example <Drawer position="top" />.

Offset

Set the offset prop to change the drawer offset from the edge of the viewport:

Customize overlay

Drawer uses the Overlay component. You can set any props that Overlay supports with overlayProps:

Sizes

You can change the drawer width/height (depends on position) by setting the size prop to a predefined size or any valid width, for example, size="55%" or size={200}:

import { Drawer } from '@mantine/core';

function Demo() {
  return (
    <Drawer position="right" size="xl" opened onClose={() => {}}>
      {/* Drawer content */}
    </Drawer>
  );
}

Remove header

To remove the header, set withCloseButton={false}

Drawer with scroll

Usage with ScrollArea

Change transition

Drawer is built with the Transition component. Use the transitionProps prop to customize any Transition properties:

onExitTransitionEnd and onEnterTransitionEnd

The onExitTransitionEnd and onEnterTransitionEnd props can be used to run code after the exit/enter transition is finished. For example, this is useful when you want to clear data after the drawer is closed:

Initial focus

Drawer uses FocusTrap to trap focus. Add the data-autofocus attribute to the element that should receive initial focus.

If you do not want to focus any elements when the drawer is opened, use the FocusTrap.InitialFocus component to create a visually hidden element that will receive initial focus:

If you do not add the data-autofocus attribute and do not use FocusTrap.InitialFocus, the drawer will focus the first focusable element inside it which is usually the close button.

Control behavior

The following props can be used to control Drawer behavior. In most cases it is not recommended to turn these features off – it will make the component less accessible.

  • trapFocus – determines whether focus should be trapped inside drawer
  • closeOnEscape – determines whether the drawer should be closed when Escape key is pressed
  • closeOnClickOutside – determines whether the drawer should be closed when user clicks on the overlay
  • returnFocus – determines whether focus should be returned to the element that was focused before the drawer was opened

react-remove-scroll settings

Drawer uses react-remove-scroll package to lock scroll. You can pass props down to the RemoveScroll component with removeScrollProps:

import { Drawer } from '@mantine/core';

function Demo() {
  return (
    <Drawer
      removeScrollProps={{ allowPinchZoom: true }}
      opened
      onClose={() => {}}
    >
      {/* Drawer content */}
    </Drawer>
  );
}

Change close icon

Use closeButtonProps to customize close button:

Compound components

You can use the following compound components to have full control over the Drawer rendering:

  • Drawer.Root – context provider
  • Drawer.Overlay – render Overlay
  • Drawer.Content – main drawer element, should include all drawer content
  • Drawer.Header – sticky header, usually contains Drawer.Title and Drawer.CloseButton
  • Drawer.Title – h2 element, aria-labelledby of Drawer.Content is pointing to this element, usually is rendered inside Drawer.Header
  • Drawer.CloseButton – close button, usually rendered inside Drawer.Header
  • Drawer.Body – a place for main content, aria-describedby of Drawer.Content is pointing to this element

Drawer.Stack

Use Drawer.Stack component to render multiple drawers at the same time. Drawer.Stack keeps track of opened drawers, manages z-index values, focus trapping and closeOnEscape behavior. Drawer.Stack is designed to be used with useDrawersStack hook.

Differences from using multiple Drawer components:

  • Drawer.Stack manages z-index values – drawers that are opened later will always have higher z-index value disregarding their order in the DOM
  • Drawer.Stack disables focus trap and Escape key handling for all drawers except the one that is currently opened
  • Drawers that are not currently opened are present in the DOM but are hidden with opacity: 0 and pointer-events: none
  • Only one overlay is rendered at a time

Note that Drawer.Stack can only be used with Drawer component. Components built with Drawer.Root and other compound components are not compatible with Drawer.Stack.

useDrawersStack hook

useDrawersStack hook provides an easy way to control multiple drawers at the same time. It accepts an array of unique drawers ids and returns an object with the following properties:

interface UseDrawersStackReturnType<T extends string> {
  // Current opened state of each drawer
  state: Record<T, boolean>;

  // Opens drawer with the given id
  open: (id: T) => void;

  // Closes drawer with the given id
  close: (id: T) => void;

  // Toggles drawer with the given id
  toggle: (id: T) => void;

  // Closes all drawers within the stack
  closeAll: () => void;

  // Returns props for drawer with the given id
  register: (id: T) => {
    opened: boolean;
    onClose: () => void;
    stackId: T;
  };
}

Example of using useDrawersStack with Drawer component:

import { Drawer, useDrawersStack } from '@mantine/core';

function Demo() {
  const stack = useDrawersStack(['first', 'second']);

  return (
    <>
      <Drawer {...stack.register('first')}>First</Drawer>
      <Drawer {...stack.register('second')}>Second</Drawer>
      <Button onClick={() => stack.open('first')}>Open first</Button>
    </>
  );
}

Fixed elements offset

Drawer component uses react-remove-scroll package to lock scroll. To properly size these elements add a className to them (documentation):

import { RemoveScroll } from '@mantine/core';

function Demo() {
  return (
    <>
      <div className={RemoveScroll.classNames.fullWidth}>
        width: 100%
      </div>
      <div className={RemoveScroll.classNames.zeroRight}>
        right: 0
      </div>
    </>
  );
}

Accessibility

Drawer component follows WAI-ARIA recommendations on accessibility.

Set title props to make component accessible, will add aria-labelledby to the content element:

import { Drawer } from '@mantine/core';

function Demo() {
  return <Drawer title="Drawer label" opened onClose={() => {}} />;
}

To set close button aria-label use closeButtonProps:

import { Drawer } from '@mantine/core';

function Demo() {
  return (
    <Drawer
      closeButtonProps={{ 'aria-label': 'Close drawer' }}
      opened
      onClose={() => {}}
    />
  );
}

源码参数参考

以下实现签名和类型从本地固定版本源码生成。保留可选属性、原生继承类型、联合分支和 never 限制;不会将其他设计系统的 API 混入。forwardRef 实现中的 ref 属于 React 引用,不是新增业务属性。此区域是原始类型参考,不是上游说明的全文翻译。

Drawer(_props)
export type DrawerStylesNames = DrawerRootStylesNames;

export type DrawerCssVariables = DrawerRootCssVariables;

export interface DrawerProps extends DrawerRootProps {
  /** Drawer title */
  title?: React.ReactNode;

  /** If set, the overlay is displayed @default true */
  withOverlay?: boolean;

  /** Props passed down to the `Overlay` component, can be used to configure opacity, `background-color`, styles and other properties */
  overlayProps?: ModalBaseOverlayProps;

  /** Drawer content */
  children?: React.ReactNode;

  /** If set, the close button is displayed @default true */
  withCloseButton?: boolean;

  /** Props passed down to the close button */
  closeButtonProps?: ModalBaseCloseButtonProps;

  /** Id of the drawer in the `Drawer.Stack` */
  stackId?: string;
}

export type DrawerFactory = Factory<{
  props: DrawerProps;
  ref: HTMLDivElement;
  stylesNames: DrawerStylesNames;
  vars: DrawerCssVariables;
  staticComponents: {
    Root: typeof DrawerRoot;
    Overlay: typeof DrawerOverlay;
    Content: typeof DrawerContent;
    Body: typeof DrawerBody;
    Header: typeof DrawerHeader;
    Title: typeof DrawerTitle;
    CloseButton: typeof DrawerCloseButton;
    Stack: typeof DrawerStack;
  };
}>;

原生与外部类型

以下外部类型仍由 React 或资源自身依赖定义,没有手工展开为虚构属性。实际可用类型以安装版本的 TypeScript 检查为准。

Factory<{
  props: DrawerProps;
  ref: HTMLDivElement;
  stylesNames: DrawerStylesNames;
  vars: DrawerCssVariables;
  staticComponents: {
    Root: typeof DrawerRoot;
    Overlay: typeof DrawerOverlay;
    Content: typeof DrawerContent;
    Body: typeof DrawerBody;
    Header: typeof DrawerHeader;
    Title: typeof DrawerTitle;
    CloseButton: typeof DrawerCloseButton;
    Stack: typeof DrawerStack;
  };
}>
DrawerRootProps
React.ReactNode
ModalBaseOverlayProps
ModalBaseCloseButtonProps
HTMLDivElement
DrawerRootStylesNames
DrawerRootCssVariables