드로어

드로어 (Drawer)

페이지 위에 덮여 옆에서 슬라이드되는 패널 컴포넌트예요. 정보나 작업 묶음을 담으며, 현재 페이지를 떠나지 않고도 같은 맥락 안에서 작업을 효율적으로 처리할 수 있게 해 줘요.

출처: 문서

본문

언제 사용하나요 (When To Use)

Drawer는 보통 페이지 위에 겹쳐 놓고 옆에서 슬라이드되는 패널이에요. 정보나 작업 묶음을 담아요. 사용자가 현재 페이지를 떠나지 않고도 Drawer와 상호작용할 수 있어서, 같은 맥락 안에서 작업을 더 효율적으로 끝낼 수 있어요.

  • Form을 사용해 정보 묶음을 만들거나 수정할 때
  • 하위 작업(subtask)을 처리할 때. 하위 작업이 Popover로는 너무 무겁고, 하위 작업을 메인 작업의 맥락 안에 유지하고 싶을 때 Drawer가 매우 유용해요.
  • 같은 Form을 여러 곳에서 사용해야 할 때

개발자를 위한 참고사항

5.17.0부터는 Spin을 통해 loading prop을 제공했어요. 하지만 5.18.0 버전부터 이 디자인 오류를 수정해 Spin을 Skeleton으로 교체했고, loading prop의 타입도 boolean 타입만 받도록 변경했어요.

예시 (Examples)

기본 (Basic)

기본 드로어예요.

import React, { useState } from 'react';
import { Button, Drawer } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);

  const showDrawer = () => {
    setOpen(true);
  };

  const onClose = () => {
    setOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showDrawer}>
        Open
      </Button>
      <Drawer
        title="Basic Drawer"
        closable={{ 'aria-label': 'Close Button' }}
        onClose={onClose}
        open={open}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Drawer>
    </>
  );
};

export default App;

위치 커스터마이즈 (Custom Placement)

Drawer는 화면의 어느 가장자리에서도 나타날 수 있어요.

import React, { useState } from 'react';
import type { DrawerProps, RadioChangeEvent } from 'antd';
import { Button, Drawer, Radio, Space } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [placement, setPlacement] = useState<DrawerProps['placement']>('left');

  const showDrawer = () => {
    setOpen(true);
  };

  const onClose = () => {
    setOpen(false);
  };

  const onChange = (e: RadioChangeEvent) => {
    setPlacement(e.target.value);
  };

  return (
    <>
      <Space>
        <Radio.Group value={placement} onChange={onChange}>
          <Radio value="top">top</Radio>
          <Radio value="right">right</Radio>
          <Radio value="bottom">bottom</Radio>
          <Radio value="left">left</Radio>
        </Radio.Group>
        <Button type="primary" onClick={showDrawer}>
          Open
        </Button>
      </Space>
      <Drawer
        title="Basic Drawer"
        placement={placement}
        closable={false}
        onClose={onClose}
        open={open}
        key={placement}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Drawer>
    </>
  );
};

export default App;

크기 조절 (Resizable)

사용자가 가장자리를 드래그해 Drawer의 너비나 높이를 조절할 수 있는 리사이즈 가능한 드로어예요.

import React, { useState } from 'react';
import type { DrawerProps, RadioChangeEvent } from 'antd';
import { Button, Drawer, Radio, Space } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [placement, setPlacement] = useState<DrawerProps['placement']>('right');
  const [size, setSize] = useState(256);

  const onChange = (e: RadioChangeEvent) => {
    setSize(256);
    setPlacement(e.target.value);
  };

  return (
    <>
      <Space style={{ marginBottom: 16 }}>
        <Radio.Group
          value={placement}
          onChange={onChange}
          options={['top', 'right', 'bottom', 'left'].map((pos) => ({
            label: pos,
            value: pos,
          }))}
        />
        <Button type="primary" onClick={() => setOpen(true)}>
          Open Drawer
        </Button>
      </Space>
      <div>Current size: {size}px</div>
      <Drawer
        title="Resizable Drawer"
        placement={placement}
        onClose={() => setOpen(false)}
        open={open}
        key={placement}
        size={size}
        resizable={{
          onResize: (newSize) => setSize(newSize),
        }}
      >
        <p>Drag the edge to resize the drawer</p>
        <p>Current size: {size}px</p>
      </Drawer>
    </>
  );
};

export default App;

로딩 (Loading)

Drawer의 로딩 상태를 설정해요.

import React from 'react';
import { Button, Drawer } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = React.useState<boolean>(false);
  const [loading, setLoading] = React.useState<boolean>(true);

  const showLoading = () => {
    setOpen(true);
    setLoading(true);

    // Simple loading mock. You should add cleanup logic in real world.
    setTimeout(() => {
      setLoading(false);
    }, 2000);
  };

  return (
    <>
      <Button type="primary" onClick={showLoading}>
        Open Drawer
      </Button>
      <Drawer
        closable
        destroyOnHidden
        title={<p>Loading Drawer</p>}
        placement="right"
        open={open}
        loading={loading}
        onClose={() => setOpen(false)}
      >
        <Button type="primary" style={{ marginBottom: 16 }} onClick={showLoading}>
          Reload
        </Button>
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Drawer>
    </>
  );
};

export default App;

추가 동작 (Extra Actions)

Ant Design에서는 추가 동작을 드로어의 모서리에 배치해요. extra prop으로 설정할 수 있어요.

import React, { useState } from 'react';
import { Button, Drawer, Radio, Space } from 'antd';
import type { DrawerProps, RadioChangeEvent } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [placement, setPlacement] = useState<DrawerProps['placement']>('right');

  const showDrawer = () => {
    setOpen(true);
  };

  const onChange = (e: RadioChangeEvent) => {
    setPlacement(e.target.value);
  };

  const onClose = () => {
    setOpen(false);
  };

  return (
    <>
      <Space>
        <Radio.Group value={placement} onChange={onChange}>
          <Radio value="top">top</Radio>
          <Radio value="right">right</Radio>
          <Radio value="bottom">bottom</Radio>
          <Radio value="left">left</Radio>
        </Radio.Group>
        <Button type="primary" onClick={showDrawer}>
          Open
        </Button>
      </Space>
      <Drawer
        title="Drawer with extra actions"
        placement={placement}
        size={500}
        onClose={onClose}
        open={open}
        extra={
          <Space>
            <Button onClick={onClose}>Cancel</Button>
            <Button type="primary" onClick={onClose}>
              OK
            </Button>
          </Space>
        }
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Drawer>
    </>
  );
};

export default App;

현재 DOM에 렌더링

현재 DOM에 렌더링하기. 커스텀 컨테이너는 getContainer를 확인하세요.

참고: v5에서는 style과 className props가 Modal 컴포넌트와 맞춰 Drawer 패널로 이동했어요. 원래 style, className props는 rootStyle, rootClassName으로 대체됐어요.

getContainer가 DOM 노드를 반환할 때는 rootStyle을 { position: 'absolute' }로 수동 설정해야 해요. #41951 참고.

import React, { useState } from 'react';
import { Button, Drawer, theme } from 'antd';

const App: React.FC = () => {
  const { token } = theme.useToken();
  const [open, setOpen] = useState(false);

  const showDrawer = () => {
    setOpen(true);
  };

  const onClose = () => {
    setOpen(false);
  };

  const containerStyle: React.CSSProperties = {
    position: 'relative',
    height: 200,
    padding: 48,
    overflow: 'hidden',
    background: token.colorFillAlter,
    border: `${token.lineWidth}px ${token.lineType} ${token.colorBorderSecondary}`,
    borderRadius: token.borderRadiusLG,
  };

  return (
    <div style={containerStyle}>
      Render in this
      <div style={{ marginTop: 16 }}>
        <Button type="primary" onClick={showDrawer}>
          Open
        </Button>
      </div>
      <Drawer
        title="Basic Drawer"
        placement="right"
        closable={false}
        onClose={onClose}
        open={open}
        getContainer={false}
      >
        <p>Some contents...</p>
      </Drawer>
    </div>
  );
};

export default App;

드로어 안에서 폼 제출

Drawer 안에서 제출 버튼과 함께 Form을 사용해요.

import React, { useState } from 'react';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Col, DatePicker, Drawer, Form, Input, Row, Select, Space } from 'antd';
import type { InputProps } from 'antd';

const UrlInput: React.FC<InputProps> = (props) => {
  return (
    <Space.Compact>
      <Space.Addon>http://</Space.Addon>
      <Input style={{ width: '100%' }} {...props} />
      <Space.Addon>.com</Space.Addon>
    </Space.Compact>
  );
};

const App: React.FC = () => {
  const [open, setOpen] = useState(false);

  const showDrawer = () => {
    setOpen(true);
  };

  const onClose = () => {
    setOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showDrawer} icon={<PlusOutlined />}>
        New account
      </Button>
      <Drawer
        title="Create a new account"
        size={720}
        onClose={onClose}
        open={open}
        styles={{
          body: {
            paddingBottom: 80,
          },
        }}
        extra={
          <Space>
            <Button onClick={onClose}>Cancel</Button>
            <Button onClick={onClose} type="primary">
              Submit
            </Button>
          </Space>
        }
      >
        <Form layout="vertical" requiredMark={false}>
          <Row gutter={16}>
            <Col span={12}>
              <Form.Item
                name="name"
                label="Name"
                rules={[{ required: true, message: 'Please enter user name' }]}
              >
                <Input placeholder="Please enter user name" />
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item
                name="url"
                label="Url"
                rules={[{ required: true, message: 'Please enter url' }]}
              >
                <UrlInput placeholder="Please enter url" />
              </Form.Item>
            </Col>
          </Row>
          <Row gutter={16}>
            <Col span={12}>
              <Form.Item
                name="owner"
                label="Owner"
                rules={[{ required: true, message: 'Please select an owner' }]}
              >
                <Select
                  placeholder="Please select an owner"
                  options={[
                    { label: 'Xiaoxiao Fu', value: 'xiao' },
                    { label: 'Maomao Zhou', value: 'mao' },
                  ]}
                />
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item
                name="type"
                label="Type"
                rules={[{ required: true, message: 'Please choose the type' }]}
              >
                <Select
                  placeholder="Please choose the type"
                  options={[
                    { label: 'private', value: 'private' },
                    { label: 'public', value: 'public' },
                  ]}
                />
              </Form.Item>
            </Col>
          </Row>
          <Row gutter={16}>
            <Col span={12}>
              <Form.Item
                name="approver"
                label="Approver"
                rules={[{ required: true, message: 'Please choose the approver' }]}
              >
                <Select
                  placeholder="Please choose the approver"
                  options={[
                    { label: 'Jack Ma', value: 'jack' },
                    { label: 'Tom Liu', value: 'tom' },
                  ]}
                />
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item
                name="dateTime"
                label="DateTime"
                rules={[{ required: true, message: 'Please choose the dateTime' }]}
              >
                <DatePicker.RangePicker
                  style={{ width: '100%' }}
                  getPopupContainer={(trigger) => trigger.parentElement!}
                />
              </Form.Item>
            </Col>
          </Row>
          <Row gutter={16}>
            <Col span={24}>
              <Form.Item
                name="description"
                label="Description"
                rules={[
                  {
                    required: true,
                    message: 'please enter url description',
                  },
                ]}
              >
                <Input.TextArea rows={4} placeholder="please enter url description" />
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </Drawer>
    </>
  );
};

export default App;

프리뷰 드로어 (Preview drawer)

목록의 항목처럼 객체의 상세 정보를 Drawer로 빠르게 미리 볼 때 사용해요.

import React, { useState } from 'react';
import { Avatar, Col, Divider, Drawer, List, Row } from 'antd';
import { createStyles } from 'antd-style';

const useStyles = createStyles((props) => {
  const { css, cssVar } = props;
  return {
    descriptionItem: css`
      margin-bottom: ${cssVar.marginXS};
      color: ${cssVar.colorTextLabel};
      font-size: ${cssVar.fontSize};
      line-height: ${cssVar.lineHeight};
    `,
    profileTitle: css`
      display: block;
      margin-bottom: ${cssVar.margin};
      color: ${cssVar.colorTextHeading};
      font-size: ${cssVar.fontSizeLG};
      line-height: ${cssVar.lineHeight};
    `,
    label: css`
      display: inline-block;
      margin-inline-end: ${cssVar.marginXS};
      color: ${cssVar.colorTextHeading};
    `,
  };
});

interface DescriptionItemProps {
  title: string;
  content: React.ReactNode;
}

const DescriptionItem: React.FC<DescriptionItemProps> = (props) => {
  const { title, content } = props;
  const { styles } = useStyles();
  return (
    <div className={styles.descriptionItem}>
      <p className={styles.label}>{title}:</p>
      {content}
    </div>
  );
};

const App: React.FC = () => {
  const { styles } = useStyles();
  const [open, setOpen] = useState(false);

  const showDrawer = () => {
    setOpen(true);
  };

  const onClose = () => {
    setOpen(false);
  };

  return (
    <>
      <List
        bordered
        dataSource={[
          { id: 1, name: 'Lily' },
          { id: 2, name: 'Lily' },
        ]}
        renderItem={(item) => (
          <List.Item
            key={item.id}
            actions={[
              <a onClick={showDrawer} key={`a-${item.id}`}>
                View Profile
              </a>,
            ]}
          >
            <List.Item.Meta
              avatar={
                <Avatar src="https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png" />
              }
              title={<a href="https://ant.design/index-cn">{item.name}</a>}
              description="Progresser XTech"
            />
          </List.Item>
        )}
      />
      <Drawer size={640} placement="right" closable={false} onClose={onClose} open={open}>
        <p className={styles.profileTitle} style={{ marginBottom: 24 }}>
          User Profile
        </p>
        <p className={styles.profileTitle}>Personal</p>
        <Row>
          <Col span={12}>
            <DescriptionItem title="Full Name" content="Lily" />
          </Col>
          <Col span={12}>
            <DescriptionItem title="Account" content="[email protected]" />
          </Col>
        </Row>
        <Row>
          <Col span={12}>
            <DescriptionItem title="City" content="HangZhou" />
          </Col>
          <Col span={12}>
            <DescriptionItem title="Country" content="China🇨🇳" />
          </Col>
        </Row>
        <Row>
          <Col span={12}>
            <DescriptionItem title="Birthday" content="February 2,1900" />
          </Col>
          <Col span={12}>
            <DescriptionItem title="Website" content="-" />
          </Col>
        </Row>
        <Row>
          <Col span={24}>
            <DescriptionItem
              title="Message"
              content="Make things as simple as possible but no simpler."
            />
          </Col>
        </Row>
        <Divider />
        <p className={styles.profileTitle}>Company</p>
        <Row>
          <Col span={12}>
            <DescriptionItem title="Position" content="Programmer" />
          </Col>
          <Col span={12}>
            <DescriptionItem title="Responsibilities" content="Coding" />
          </Col>
        </Row>
        <Row>
          <Col span={12}>
            <DescriptionItem title="Department" content="XTech" />
          </Col>
          <Col span={12}>
            <DescriptionItem title="Supervisor" content={<a>Lin</a>} />
          </Col>
        </Row>
        <Row>
          <Col span={24}>
            <DescriptionItem
              title="Skills"
              content="C / C + +, data structures, software engineering, operating systems, computer networks, databases, compiler theory, computer architecture, Microcomputer Principle and Interface Technology, Computer English, Java, ASP, etc."
            />
          </Col>
        </Row>
        <Divider />
        <p className={styles.profileTitle}>Contacts</p>
        <Row>
          <Col span={12}>
            <DescriptionItem title="Email" content="[email protected]" />
          </Col>
          <Col span={12}>
            <DescriptionItem title="Phone Number" content="+86 181 0000 0000" />
          </Col>
        </Row>
        <Row>
          <Col span={24}>
            <DescriptionItem
              title="GitHub"
              content={
                <a
                  href="https://github.com/ant-design/ant-design"
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  github.com/ant-design/ant-design
                </a>
              }
            />
          </Col>
        </Row>
      </Drawer>
    </>
  );
};

export default App;

다단계 드로어 (Multi-level drawer)

기존 드로어 위에 새 드로어를 열어 여러 분기 작업을 처리해요.

import React, { useState } from 'react';
import { Button, Drawer } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [childrenDrawer, setChildrenDrawer] = useState(false);

  const showDrawer = () => {
    setOpen(true);
  };

  const onClose = () => {
    setOpen(false);
  };

  const showChildrenDrawer = () => {
    setChildrenDrawer(true);
  };

  const onChildrenDrawerClose = () => {
    setChildrenDrawer(false);
  };

  return (
    <>
      <Button type="primary" onClick={showDrawer}>
        Open drawer
      </Button>
      <Drawer title="Multi-level drawer" size={520} closable={false} onClose={onClose} open={open}>
        <Button type="primary" onClick={showChildrenDrawer}>
          Two-level drawer
        </Button>
        <Drawer
          title="Two-level Drawer"
          size={320}
          closable={false}
          onClose={onChildrenDrawerClose}
          open={childrenDrawer}
        >
          This is two-level drawer
        </Drawer>
      </Drawer>
    </>
  );
};

export default App;

사전 설정 크기 (Preset size)

Drawer의 기본 너비(또는 높이)는 378px이고, 사전 설정된 큰 크기 736px가 있어요.

import React, { useState } from 'react';
import { Button, Drawer, Radio, Space } from 'antd';
import type { DrawerProps } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);
  const [size, setSize] = useState<DrawerProps['size']>();

  const onClose = () => {
    setOpen(false);
  };

  return (
    <>
      <Space style={{ marginBottom: 16 }}>
        <Radio.Group
          value={size}
          onChange={(e) => setSize(e.target.value)}
          options={[
            { label: 'Large Size (736px)', value: 'large' },
            { label: 'Default Size (378px)', value: 'default' },
            { label: 256, value: 256 },
            { label: '500px', value: '500px' },
            { label: '50%', value: '50%' },
            { label: '20vw', value: '20vw' },
          ]}
        />
      </Space>
      <Button type="primary" onClick={() => setOpen(true)}>
        Open Drawer
      </Button>
      <Drawer
        title={`${size} Drawer`}
        placement="right"
        size={size}
        onClose={onClose}
        open={open}
        extra={
          <Space>
            <Button onClick={onClose}>Cancel</Button>
            <Button type="primary" onClick={onClose}>
              OK
            </Button>
          </Space>
        }
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Some contents...</p>
      </Drawer>
    </>
  );
};

export default App;

마스크 (mask)

마스크 효과예요.

import React, { useState } from 'react';
import { Button, Drawer, Space } from 'antd';

type MaskType = 'blur' | 'dimmed' | 'none';
type DrawerConfig = {
  type: MaskType;
  mask: boolean | { blur: boolean };
  title: string;
};

const drawerList: DrawerConfig[] = [
  { type: 'blur', mask: { blur: true }, title: 'blur' },
  { type: 'dimmed', mask: true, title: 'Dimmed mask' },
  { type: 'none', mask: false, title: 'No mask' },
];
const App: React.FC = () => {
  const [open, setOpen] = useState<false | MaskType>(false);

  const showDrawer = (type: MaskType) => {
    setOpen(type);
  };

  const onClose = () => {
    setOpen(false);
  };

  return (
    <Space wrap>
      {drawerList.map((item) => (
        <React.Fragment key={item.type}>
          <Button
            onClick={() => {
              showDrawer(item.type);
            }}
          >
            {item.title}
          </Button>
          <Drawer
            title={item.title}
            placement="right"
            mask={item.mask}
            onClose={onClose}
            open={open === item.type}
          >
            <p>Some contents...</p>
            <p>Some contents...</p>
            <p>Some contents...</p>
          </Drawer>
        </React.Fragment>
      ))}
    </Space>
  );
};

export default App;

닫기 버튼 위치 (Closable placement)

닫기 버튼 위치를 end로 커스터마이즈할 수 있는 드로어예요. 기본값은 start예요.

import React, { useState } from 'react';
import { Button, Drawer } from 'antd';

const App: React.FC = () => {
  const [open, setOpen] = useState(false);

  const showDrawer = () => {
    setOpen(true);
  };

  const onClose = () => {
    setOpen(false);
  };

  return (
    <>
      <Button type="primary" onClick={showDrawer}>
        Open
      </Button>
      <Drawer
        title="Drawer Closable Placement"
        closable={{ placement: 'end' }}
        onClose={onClose}
        open={open}
      >
        <p>Some contents...</p>
        <p>Some contents...</p>
        <p>Take a look at the top-right corner...</p>
      </Drawer>
    </>
  );
};

export default App;

커스텀 시맨틱 DOM 스타일 (Custom semantic dom styling)

classNames와 styles로 객체나 함수를 넘겨 Drawer의 시맨틱 DOM 스타일을 커스터마이즈할 수 있어요.

import React, { useState } from 'react';
import { Button, Drawer, Flex } from 'antd';
import type { DrawerProps, GetProp } from 'antd';
import { createStaticStyles } from 'antd-style';

const lineStyle: React.CSSProperties = {
  lineHeight: '28px',
};

const sharedContent = (
  <>
    <div style={lineStyle}>
      Following the Ant Design specification, we developed a React UI library antd that contains a
      set of high quality components and demos for building rich, interactive user interfaces.
    </div>
    <div style={lineStyle}>🌈 Enterprise-class UI designed for web applications.</div>
    <div style={lineStyle}>📦 A set of high-quality React components out of the box.</div>
    <div style={lineStyle}>🛡 Written in TypeScript with predictable static types.</div>
    <div style={lineStyle}>⚙️ Whole package of design resources and development tools.</div>
    <div style={lineStyle}>🌍 Internationalization support for dozens of languages.</div>
    <div style={lineStyle}>🎨 Powerful theme customization in every detail.</div>
  </>
);

const classNames = createStaticStyles(
  ({ css }): NonNullable<GetProp<DrawerProps, 'classNames', 'Return'>> => ({
    root: css`
      border-radius: 10px;
      padding: 10px;
    `,
  }),
);

const styles: DrawerProps['styles'] = {
  mask: {
    backgroundImage: `linear-gradient(to top, #18181b 0, rgba(21, 21, 22, 0.2) 100%)`,
  },
};

const stylesFn: DrawerProps['styles'] = (info): GetProp<DrawerProps, 'styles', 'Return'> => {
  if (info.props.footer) {
    return {
      header: {
        padding: 16,
      },
      body: {
        padding: 16,
      },
      footer: {
        padding: '16px 10px',
        backgroundColor: '#fafafa',
      },
    };
  }
};

const App: React.FC = () => {
  const [drawerOpen, setDrawerOpen] = useState(false);
  const [drawerFnOpen, setDrawerFnOpen] = useState(false);

  const sharedProps: DrawerProps = {
    classNames,
    size: 500,
  };

  const footer: React.ReactNode = (
    <Flex gap="medium" justify="flex-end">
      <Button
        onClick={() => setDrawerFnOpen(false)}
        styles={{ root: { borderColor: '#ccc', color: '#171717', backgroundColor: '#fff' } }}
      >
        Cancel
      </Button>
      <Button
        type="primary"
        styles={{ root: { backgroundColor: '#171717' } }}
        onClick={() => setDrawerOpen(true)}
      >
        Submit
      </Button>
    </Flex>
  );

  return (
    <Flex gap="medium">
      <Button onClick={() => setDrawerOpen(true)}>Open Style Drawer</Button>
      <Button type="primary" onClick={() => setDrawerFnOpen(true)}>
        Open Function Drawer
      </Button>
      <Drawer
        {...sharedProps}
        footer={null}
        title="Custom Style Drawer"
        styles={styles}
        open={drawerOpen}
        onClose={() => setDrawerOpen(false)}
      >
        {sharedContent}
      </Drawer>
      <Drawer
        {...sharedProps}
        footer={footer}
        title="Custom Function drawer"
        styles={stylesFn}
        mask={{ enabled: true, blur: true }}
        open={drawerFnOpen}
        onClose={() => setDrawerFnOpen(false)}
      >
        {sharedContent}
      </Drawer>
    </Flex>
  );
};

export default App;

API

공통 props 참조: Common props

속성 설명 타입 기본값 버전 전역 설정
afterOpenChange Drawer 전환 시 애니메이션이 끝난 뒤의 콜백 function(open) - ×
bodyStyle Drawer 본문의 스타일, styles.body를 대신 사용하세요 CSSProperties - - ×
className Drawer 패널의 className 설정. 최상위 DOM 스타일은 rootClassName을 사용 string - 5.7.0
classNames Drawer 컴포넌트 내 각 시맨틱 구조에 대한 클래스를 커스터마이즈. 객체 또는 함수를 지원 Record<SemanticDOM, string> | (info: { props })=> Record<SemanticDOM, string> - 5.10.0
closable 닫기 버튼 표시 여부. 위치는 placement로 설정 boolean | { closeIcon?: React.ReactNode; disabled?: boolean; placement?: 'start' | 'end' } true placement: 5.28.0 5.15.0, placement: 6.1.1
contentWrapperStyle Drawer 래퍼의 스타일, styles.wrapper를 대신 사용하세요 CSSProperties - - ×
destroyOnClose Drawer를 닫을 때 하위 컴포넌트를 마운트 해제할지 여부, destroyOnHidden을 대신 사용하세요 boolean false ×
destroyOnHidden Drawer를 닫을 때 하위 컴포넌트를 마운트 해제할지 여부 boolean false 5.25.0 ×
drawerStyle Drawer 패널의 스타일, styles.section을 대신 사용하세요 CSSProperties - - ×
extra 모서리의 추가 동작 영역 ReactNode - 4.17.0 ×
footer Drawer의 푸터 ReactNode - ×
footerStyle Drawer 푸터의 스타일, styles.footer를 대신 사용하세요 CSSProperties - - ×
forceRender Drawer 컴포넌트를 강제로 사전 렌더링 boolean false ×
focusable Drawer에서 포커스 관리를 위한 설정 { trap?: boolean, focusTriggerAfterClose?: boolean } - 6.2.0 6.4.0
getContainer Drawer를 마운트할 노드와 표시 창 HTMLElement | () => HTMLElement | Selectors | false body ×
headerStyle Drawer 헤더 부분의 스타일, styles.header를 대신 사용하세요 CSSProperties - ×
height placement가 top 또는 bottom일 때 Drawer 대화상자의 높이, size를 대신 사용하세요 string | number 378 ×
keyboard esc 키로 닫기를 지원할지 여부 boolean true ×
loading Skeleton 표시 boolean false 5.17.0 ×
mask 마스크 효과 boolean | { enabled?: boolean, blur?: boolean, closable?: boolean } true mask.closable: 6.3.0 6.0.0, mask.closable: 6.3.0
maskClosable 마스크(Drawer 바깥 영역)를 클릭해 Drawer를 닫을지 여부 boolean true ×
maskStyle Drawer 마스크의 스타일, styles.mask를 대신 사용하세요 CSSProperties - - ×
maxSize 리사이즈 가능할 때 최대 크기(placement에 따라 너비 또는 높이) number - 6.0.0 ×
open Drawer 대화상자의 표시 여부 boolean false ×
placement Drawer의 위치 top | right | bottom | left right ×
push 중첩 드로어의 밀기(push) 동작 boolean | { distance: string | number } { distance: 180 } 4.5.0+ ×
resizable 드래그로 리사이즈 가능하게 하기 boolean | ResizableConfig - boolean: 6.1.0 ×
rootStyle style과 달리 마스크를 포함하는 래퍼 요소의 스타일 CSSProperties - ×
size 드로어 사전 설정 크기, 기본 378px, large는 736px, 또는 커스텀 숫자 'default' | 'large' | number | string 'default' 4.17.0, string: 6.2.0 ×
style Drawer 패널의 스타일. 본문만 설정하려면 styles.body 사용 CSSProperties - 5.7.0
styles Drawer 컴포넌트 내 각 시맨틱 구조에 대한 인라인 스타일을 커스터마이즈. 객체 또는 함수를 지원 Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> - 5.10.0
title Drawer의 제목 ReactNode - ×
width Drawer 대화상자의 너비, size를 대신 사용하세요 string | number 378 ×
zIndex Drawer의 z-index number 1000 ×
onClose 사용자가 마스크, 닫기 버튼 또는 취소 버튼을 클릭했을 때 호출되는 콜백 지정 function(e) - ×
drawerRender 커스텀 drawer 콘텐츠 렌더링 (node: ReactNode) => ReactNode - 5.18.0 ×

ResizableConfig

속성 설명 타입 기본값 버전
onResizeStart 리사이즈 시작 시 콜백 () => void - 6.0.0
onResize 리사이즈 중 콜백 (size: number) => void - 6.0.0
onResizeEnd 리사이즈 종료 시 콜백 () => void - 6.0.0

Semantic DOM

https://ant.design/components/drawer/semantic.md

Design Token

Component Token (Drawer)

토큰 이름 설명 타입 기본값
draggerSize 리사이즈 핸들의 크기 number 4
footerPaddingBlock 푸터의 세로 패딩 number 8
footerPaddingInline 푸터의 가로 패딩 number 16
zIndexPopup drawer의 z-index number 1000

Global Token

토큰 이름 설명 타입 기본값
borderRadiusSM SM 크기의 테두리 반경. 작은 크기 컴포넌트(Button, Input, Select 등 작은 입력 컴포넌트)에서 사용 number
colorBgElevated 팝업 레이어의 컨테이너 배경색. 다크 모드에서는 colorBgContainer보다 밝음. 예: modal, pop-up, menu 등 string
colorBgMask 마스크의 배경색. 마스크 아래 콘텐츠를 덮는 데 사용하며, Modal, Drawer, Image 등 컴포넌트가 이 토큰 사용 string
colorBgTextActive 활성 상태 텍스트의 배경색 제어 string
colorBgTextHover 호버 상태 텍스트의 배경색 제어 string
colorIcon 약한 동작. allowClear나 Alert 닫기 버튼 등 string
colorIconHover 약한 동작의 호버 색상. allowClear나 Alert 닫기 버튼 등 string
colorPrimary 브랜드 색상은 제품의 특성과 커뮤니케이션을 반영하는 가장 직접적인 시각 요소 중 하나 string
colorPrimaryBorder 메인 색 그라데이션의 획 색상. Slider 등 컴포넌트의 획에 사용 string
colorSplit 구분선의 색상으로 사용. colorBorderSecondary와 같지만 투명도를 가짐 string
colorText W3C 표준을 따르는 기본 텍스트 색상. 가장 어두운 중립 색 string
fontSizeLG 큰 글꼴 크기 number
fontWeightStrong 제목 컴포넌트(h1, h2, h3)나 선택된 항목의 글꼴 두께 제어 number
lineHeightLG 큰 텍스트의 줄 높이 number
lineType 기본 컴포넌트의 테두리 스타일 string
lineWidth 기본 컴포넌트의 테두리 너비 number
lineWidthFocus 컴포넌트가 포커스 상태일 때 라인의 너비 제어 number
marginXS 요소의 여백을 작은 크기로 제어 number
motionDurationMid 중간 속도 모션. 중간 요소 애니메이션 상호작용에 사용 string
motionDurationSlow 느린 속도 모션. 큰 요소 애니메이션 상호작용에 사용 string
padding 요소의 패딩 제어 number
paddingLG 요소의 큰 패딩 제어 number
paddingXS 요소의 매우 작은 패딩 제어 number

더 알아보기 (Learn more)