Drawer

Drawer

출처: 문서

본문

때로는 Dialog가 항상 요구를 만족시키지는 못해요. 예를 들어 엄청나게 큰 폼이 있거나 약관 같은 것을 표시할 공간이 필요하다면요. Drawer는 Dialog와 거의 동일한 API를 갖지만, 다른 사용자 경험을 제공해요. 보통 옆쪽에서 슬라이드로 들어오는 패널 UI가 필요할 때 사용해요.

TIP

모든 컴포넌트에서 v-model이 네이티브로 지원되므로 visible.sync는 deprecated 되었어요. v-model="visibilityBinding"으로 현재 drawer의 표시 여부를 제어하세요.

기본 사용 (Basic Usage)

여러 방향에서 임시 drawer를 불러낼 수 있어요. Dialog처럼 Drawer에도 표시 여부를 제어하려면 model-value를 설정해야 해요. boolean 타입이에요. Drawer는 title, body, footer 세 부분으로 이루어져요. title은 named slot이고 title이라는 속성으로도 설정할 수 있으며 기본값은 빈 문자열이에요. body 부분은 Drawer의 메인 영역으로서 사용자가 정의한 내용을 담아요. 열릴 때 Drawer는 기본적으로 브라우저 창의 30% 크기로 오른쪽 모서리에서 왼쪽으로 확장돼요. direction과 size 속성을 설정해 그 기본 동작을 바꿀 수 있어요. 이 showcase는 before-close API를 어떻게 사용하는지도 보여줘요. 자세한 내용은 Attributes 섹션을 확인하세요.

<template>
  <div class="demo-drawer">
    <el-radio-group v-model="direction">
      <el-radio value="ltr">left to right</el-radio>
      <el-radio value="rtl">right to left</el-radio>
      <el-radio value="ttb">top to bottom</el-radio>
      <el-radio value="btt">bottom to top</el-radio>
    </el-radio-group>
    <el-button @click="drawer = true">open</el-button>
    <el-drawer
      v-model="drawer"
      :direction="direction"
      :before-close="handleClose"
    >
      <template #header>
        <h4>set title by slot</h4>
      </template>
      <template #footer>
        <div style="flex: auto">
          <el-button @click="cancelClick">cancel</el-button>
          <el-button type="primary" @click="confirmClick">confirm</el-button>
        </div>
      </template>
      <p>Drawer content</p>
    </el-drawer>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'

import type { DrawerProps } from 'element-plus'

const drawer = ref(false)
const drawer2 = ref(false)
const direction = ref('rtl')
const radio1 = ref('Option 1')

const handleClose = (done: () => void) => {
  ElMessageBox.confirm('Are you sure you want to close this?')
    .then(() => {
      done()
    })
    .catch(() => {
      // catch error
    })
}

function cancelClick() {
  drawer2.value = false
}

function confirmClick() {
  ElMessageBox.confirm(`Are you confirm to chose ${radio1.value} ?`)
    .then(() => {
      drawer2.value = false
    })
    .catch(() => {
      // catch error
    })
}
</script>

제목 없이 (No Title)

더 이상 제목이 필요하지 않을 때 drawer에서 제거할 수 있어요. withHeader 속성을 false로 설정하면 drawer에서 제목을 제거할 수 있어요. 그러면 drawer가 화면에서 더 많은 공간을 차지할 수 있어요. 접근성이 필요하다면 title 속성을 꼭 설정하세요.

커스텀 내용 (Customized Content)

Dialog처럼 Drawer도 다양한 상호작용을 표시하는 데 사용할 수 있어요. 중첩된 테이블이나 폼을 넣을 수 있어요.

<template>
  <div>
    <el-button @click="table = true">Open Drawer with nested table</el-button>
    <el-button @click="dialog = true">Open Drawer with nested form</el-button>
  </div>
</template>

<script setup lang="ts">
import { reactive, ref } from 'vue'
import { ElMessageBox } from 'element-plus'

const formLabelWidth = '80px'
let timer

const table = ref(false)
const dialog = ref(false)
const loading = ref(false)

const form = reactive({
  name: '',
  region: '',
  date1: '',
  date2: '',
  delivery: false,
  type: [],
  resource: '',
  desc: '',
})

const gridData = [
  {
    date: '2016-05-02',
    name: 'Peter Parker',
    address: 'Queens, New York City',
  },
  {
    date: '2016-05-04',
    name: 'Peter Parker',
    address: 'Queens, New York City',
  },
  {
    date: '2016-05-01',
    name: 'Peter Parker',
    address: 'Queens, New York City',
  },
  {
    date: '2016-05-03',
    name: 'Peter Parker',
    address: 'Queens, New York City',
  },
]

const onClick = () => {
  loading.value = true
  setTimeout(() => {
    loading.value = false
    dialog.value = false
  }, 400)
}

const handleClose = (done) => {
  if (loading.value) {
    return
  }
  ElMessageBox.confirm('Do you want to submit?')
    .then(() => {
      loading.value = true
      timer = setTimeout(() => {
        done()
        // 动画关闭需要一定的时间
        setTimeout(() => {
          loading.value = false
        }, 400)
      }, 2000)
    })
    .catch(() => {
      // catch error
    })
}

const cancelForm = () => {
  loading.value = false
  dialog.value = false
  clearTimeout(timer)
}
</script>

커스텀 헤더 (Customized Header)

header 슬롯을 사용해 제목이 표시되는 영역을 커스터마이즈할 수 있어요. 접근성을 유지하려면 이 슬롯을 사용하면서도 title 속성을 함께 사용하거나, titleId 슬롯 속성으로 어떤 요소를 drawer 제목으로 읽어야 할지 지정해야 해요.

크기 조절 가능한 Drawer (Resizable Drawer) 2.11.0

가장자리 부분을 드래그해 보세요. resizable을 true로 설정하면 크기 조절이 가능해요.

중첩 Drawer (Nested Drawer)

Dialog처럼 Drawer도 여러 레이어로 가질 수 있어요. 다른 레이어에 여러 Drawer가 필요하다면 append-to-body 속성을 true로 설정해야 해요.

<template>
  <el-button @click="drawer = true">open</el-button>
  <el-drawer
    v-model="drawer"
    title="I'm outer Drawer"
    :before-close="handleClose"
  >
    <el-button @click="innerDrawer = true">Click me!</el-button>
    <el-drawer
      v-model="innerDrawer"
      title="I'm inner Drawer"
      :before-close="handleClose"
    >
      <p>This is inner drawer</p>
    </el-drawer>
  </el-drawer>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ElMessageBox } from 'element-plus'

const drawer = ref(false)
const innerDrawer = ref(false)

const handleClose = (done: () => void) => {
  ElMessageBox.confirm('You still have unsaved data, proceed?')
    .then(() => {
      done()
    })
    .catch(() => {
      // catch error
    })
}
</script>

modal을 false로 설정하면 drawer의 modal(오버레이)을 숨길 수 있어요. 2.11.7 버전부터 modal-penetrable 속성이 추가되었는데, 이 속성으로 modal을 관통(penetrable)하게 할 수 있어요.

TIP

Drawer 안의 내용은 지연 렌더링되어야 해요. 즉 Drawer 안의 내용은 초기 렌더링 성능에 영향을 미치지 않아요. 그래서 DOM 조작은 ref를 통하거나 open 이벤트가 발생한 후에 수행해야 해요.

TIP

Drawer는 destroy-on-close라는 API를 제공해요. 이 플래그 변수는 Drawer가 닫힌 후 Drawer 안의 자식 내용을 파괴할지 여부를 나타내요. Drawer가 열릴 때마다 mounted 라이프사이클이 호출되도록 해야 한다면 이 API를 사용할 수 있어요.

API

Attributes

Name Description Type Default
model-value / v-model Should Drawer be displayed boolean false
append-to-body Controls should Drawer be inserted to DocumentBody Element, nested Drawer must assign this param to true boolean false
append-to 2.8.0 which element the Drawer appends to. Will override append-to-body CSSSelector / HTMLElement body
lock-scroll whether scroll of body is disabled while Drawer is displayed boolean true
before-close If set, closing procedure will be halted Function —
close-on-click-modal whether the Drawer can be closed by clicking the mask boolean true
close-on-press-escape Indicates whether Drawer can be closed by pressing ESC boolean true
open-delay Time(milliseconds) before open number 0
close-delay Time(milliseconds) before close number 0
destroy-on-close Indicates whether children should be destroyed after Drawer closed boolean false
modal Should show shadowing layer boolean true
modal-penetrable 2.11.7 whether the mask is penetrable. The modal attribute must be false. boolean false
direction Drawer's opening direction enum rtl
resizable 2.11.0 enable resizable feature for Drawer boolean false
show-close Should show close button at the top right of Drawer boolean true
size Drawer's size, if Drawer is horizontal mode, it effects the width property, otherwise it effects the height property, when size is number type, it describes the size by unit of pixels; when size is string type, it should be used with x% notation, other wise it will be interpreted to pixel unit number / string 30%
title Drawer's title, can also be set by named slot, detailed descriptions can be found in the slot form string —
with-header Flag that controls the header section's existence, default to true, when withHeader set to false, both title attribute and title slot won't work boolean true
modal-class Extra class names for shadowing layer string —
header-class 2.9.3 custom class names for header wrapper string —
body-class 2.9.3 custom class names for body wrapper string —
footer-class 2.9.3 custom class names for footer wrapper string —
z-index set z-index number —
header-aria-level a11y header's aria-level attribute string 2
custom-class deprecated Extra class names for Drawer string —

WARNING

custom-class는 deprecated 되었고 2.3.0에서 제거될 예정이에요. class를 사용해 주세요.

Events

Name Description Type
open Triggered before Drawer opening animation begins Function
opened Triggered after Drawer opening animation ended Function
close Triggered before Drawer closing animation begins Function
closed Triggered after Drawer closing animation ended Function
open-auto-focus triggers after Drawer opens and content focused Function
close-auto-focus triggers after Drawer closed and content focused Function
resize-start 2.11.8 Triggered when resizing starts (when resizable is enabled) Function
resize 2.11.8 Triggered while resizing (when resizable is enabled) Function
resize-end 2.11.8 Triggered when resizing ends (when resizable is enabled) Function

Slots

Name Description
default Drawer's Content
header Drawer header section; Replacing this removes the title, but does not remove the close button.
footer Drawer footer Section
title deprecated Works the same as the header slot. Use that instead.

WARNING

title은 deprecated 되었고 3.0.0에서 제거될 예정이에요. header를 사용해 주세요.

Exposes

Name Description
handleClose In order to close Drawer, this method will call before-close.

더 알아보기 (Learn more)