Skip to content
API 风格:

Drawer 서랍 ​

화면 가장자리에서 미끄러져 들어오는 측면 패널로, Dialog보다 가장자리에 가까운 일시적인 조작(상세, 필터, 마법사 단계)에 적합합니다. Dialog와 마찬가지로 role="dialog" + aria-modal="true"를 사용하며, 초점이 패널 안에 머물고 Esc와 오버레이 클릭으로 닫을 수 있습니다.

기본 사용법 ​

오른쪽 서랍
vue
<template>
  <LhButton @click="basic = true">서랍 열기</LhButton>
  <LhDrawer v-model="basic" title="주문 상세">
    <p>여기에 서랍 본문 내용을 넣습니다.</p>
  </LhDrawer>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const basic = ref(false)
</script>
vue
<script>
export default {
  data() {
    return { basic: false }
  },
}
</script>

<template>
  <LhButton @click="basic = true">서랍 열기</LhButton>
  <LhDrawer v-model="basic" title="주문 상세">
    <p>여기에 서랍 본문 내용을 넣습니다.</p>
  </LhDrawer>
</template>

네 방향 ​

right / left / top / bottom
vue
<script setup lang="ts">
import { ref } from 'vue'
const dirVisible = ref(false)
const direction = ref<'right' | 'left' | 'top' | 'bottom'>('right')
const size = ref('40%')
function openDir(d: 'right' | 'left' | 'top' | 'bottom') {
  direction.value = d
  dirVisible.value = true
}
</script>

<template>
  <!-- 오른쪽 서랍 열기 -->
  <LhButton @click="openDir('right')">right</LhButton>
  <!-- 왼쪽 서랍 열기 -->
  <LhButton @click="openDir('left')">left</LhButton>
  <!-- 상단 서랍 열기 -->
  <LhButton @click="openDir('top')">top</LhButton>
  <!-- 하단 서랍 열기 -->
  <LhButton @click="openDir('bottom')">bottom</LhButton>
  <!-- 지정한 방향으로 미끄러져 나옴 -->
  <LhDrawer v-model="dirVisible" :title="`${direction} 서랍`" :direction="direction" :size="size">
    <p>현재 방향: {{ direction }}</p>
  </LhDrawer>
</template>
vue
<script>
export default {
  data() {
    return { dirVisible: false, direction: 'right', size: '40%' }
  },
  methods: {
    openDir(d) {
      this.direction = d
      this.dirVisible = true
    },
  },
}
</script>

<template>
  <!-- 오른쪽 서랍 열기 -->
  <LhButton @click="openDir('right')">right</LhButton>
  <!-- 왼쪽 서랍 열기 -->
  <LhButton @click="openDir('left')">left</LhButton>
  <!-- 상단 서랍 열기 -->
  <LhButton @click="openDir('top')">top</LhButton>
  <!-- 하단 서랍 열기 -->
  <LhButton @click="openDir('bottom')">bottom</LhButton>
  <!-- 지정한 방향으로 미끄러져 나옴 -->
  <LhDrawer v-model="dirVisible" :title="`${direction} 서랍`" :direction="direction" :size="size">
    <p>현재 방향: {{ direction }}</p>
  </LhDrawer>
</template>

크기와 하단 커스터마이징 ​

size와 footer 슬롯
vue
<script setup lang="ts">
import { ref } from 'vue'

const sized = ref(false)
</script>

<template>
  <!-- 480px 너비 서랍 열기 -->
  <LhButton @click="sized = true">480px 너비 서랍</LhButton>
  <!-- 고정 크기 서랍, 하단은 조작 영역 -->
  <LhDrawer v-model="sized" title="필터 조건" :size="480">
    <LhInput placeholder="이름으로 필터" />
    <LhInput placeholder="태그로 필터" />
    <template #footer>
      <LhButton @click="sized = false">취소</LhButton>
      <LhButton type="primary" @click="sized = false">필터 적용</LhButton>
    </template>
  </LhDrawer>
</template>
vue
<script>
export default {
  data() {
    return { sized: false }
  },
}
</script>

<template>
  <!-- 480px 너비 서랍 열기 -->
  <LhButton @click="sized = true">480px 너비 서랍</LhButton>
  <!-- 고정 크기 서랍, 하단은 조작 영역 -->
  <LhDrawer v-model="sized" title="필터 조건" :size="480">
    <LhInput placeholder="이름으로 필터" />
    <LhInput placeholder="태그로 필터" />
    <template #footer>
      <LhButton @click="sized = false">취소</LhButton>
      <LhButton type="primary" @click="sized = false">필터 적용</LhButton>
    </template>
  </LhDrawer>
</template>

size에 숫자를 전달하면 px이고, 문자열은 백분율(예: '40%')을 지원합니다. 좌우 방향은 너비에, 상하 방향은 높이에 적용됩니다.

동작 스위치 ​

mask-closable로 오버레이 클릭 닫기를 금지, show-close로 오른쪽 위 닫기 버튼을 숨김, close-on-esc로 Esc 닫기를 비활성화, lock-scroll로 열렸을 때 페이지 스크롤을 잠그지 않음, teleport-target으로 마운트 컨테이너를 지정합니다.

동작 스위치
vue
<script setup lang="ts">
import { ref } from 'vue'

const adv1 = ref(false)
const adv2 = ref(false)
const adv3 = ref(false)
</script>

<template>
  <!-- '버튼으로만 닫기' 서랍 열기 -->
  <LhButton @click="adv1 = true">버튼으로만 닫기</LhButton>
  <!-- '스크롤 잠금 안 함' 서랍 열기 -->
  <LhButton @click="adv2 = true">스크롤 잠금 안 함</LhButton>
  <!-- 오버레이 클릭과 Esc로는 닫히지 않음 -->
  <LhDrawer
    v-model="adv1"
    title="버튼으로만 닫기"
    :mask-closable="false"
    :show-close="false"
    :close-on-esc="false"
  >
    <p>오버레이 클릭과 Esc로는 닫히지 않고, 버튼으로만 닫을 수 있습니다.</p>
    <template #footer>
      <LhButton type="primary" @click="adv1 = false">닫기</LhButton>
    </template>
  </LhDrawer>
  <!-- 열려 있을 때도 페이지를 스크롤할 수 있음 -->
  <LhDrawer v-model="adv2" title="스크롤 잠금 안 함" :lock-scroll="false">
    <p>서랍이 열려 있을 때도 페이지를 스크롤할 수 있습니다.</p>
  </LhDrawer>
  <!-- 지정한 컨테이너에 마운트 -->
  <LhDrawer v-model="adv3" title="마운트 컨테이너 지정" teleport-target="#drawer-host">
    <p>패널이 id가 drawer-host인 컨테이너 안으로 전송됩니다.</p>
  </LhDrawer>
</template>
vue
<script>
export default {
  data() {
    return { adv1: false, adv2: false, adv3: false }
  },
}
</script>

<template>
  <!-- '버튼으로만 닫기' 서랍 열기 -->
  <LhButton @click="adv1 = true">버튼으로만 닫기</LhButton>
  <!-- '스크롤 잠금 안 함' 서랍 열기 -->
  <LhButton @click="adv2 = true">스크롤 잠금 안 함</LhButton>
  <!-- 오버레이 클릭과 Esc로는 닫히지 않음 -->
  <LhDrawer
    v-model="adv1"
    title="버튼으로만 닫기"
    :mask-closable="false"
    :show-close="false"
    :close-on-esc="false"
  >
    <p>오버레이 클릭과 Esc로는 닫히지 않고, 버튼으로만 닫을 수 있습니다.</p>
    <template #footer>
      <LhButton type="primary" @click="adv1 = false">닫기</LhButton>
    </template>
  </LhDrawer>
  <!-- 열려 있을 때도 페이지를 스크롤할 수 있음 -->
  <LhDrawer v-model="adv2" title="스크롤 잠금 안 함" :lock-scroll="false">
    <p>서랍이 열려 있을 때도 페이지를 스크롤할 수 있습니다.</p>
  </LhDrawer>
  <!-- 지정한 컨테이너에 마운트 -->
  <LhDrawer v-model="adv3" title="마운트 컨테이너 지정" teleport-target="#drawer-host">
    <p>패널이 id가 drawer-host인 컨테이너 안으로 전송됩니다.</p>
  </LhDrawer>
</template>

Drawer API ​

Props ​

속성설명타입기본값
modelValue표시 여부(v-model)boolean—
title제목(header 슬롯으로도 사용 가능)string—
direction방향"right" | "left" | "top" | "bottom"'right'
size크기: 숫자는 px, 문자열은 백분율 지원string | number'30%'
maskClosable오버레이 클릭 시 닫기 여부booleantrue
showClose닫기 버튼 표시 여부booleantrue
teleportTargetTeleport 대상(선택자 또는 요소, 기본값 body)string | HTMLElement'body'
lockScroll페이지 스크롤 잠금 여부booleantrue
closeOnEscEsc 닫기 응답 여부booleantrue
themeVars컴포넌트 수준 테마 변수, ConfigProvider 설정을 덮어씀{ [x: string]: string; }—
bgPattern컴포넌트 수준 배경 문양, ConfigProvider 설정을 덮어씀string—
sxsx 빠른 레이아웃 속성(패널에 적용)LhSxProps—

Events ​

이벤트설명매개변수
update:model-value표시/숨김 변화(value: boolean)
open열린 후 발생—
close닫힌 후 발생—

Slots ​

슬롯설명
default본문 내용
header커스텀 머리글(제목 영역 포함)
footer커스텀 하단 조작 영역

Methods ​

메서드설명
open프로그래밍 방식 열기
close프로그래밍 방식 닫기

접근성 ​

  • role="dialog" + aria-modal="true". 제목이 있으면 aria-labelledby로 연결하고, 없으면 언어 팩 drawer.title을 aria-label로 사용합니다
  • 열릴 때 초점이 패널의 첫 번째 포커스 가능 요소로 이동하며, Tab은 패널 안에서 순환하고 닫힌 후 트리거로 초점을 돌려줍니다
  • Esc로 닫기(close-on-esc로 비활성화 가능). 오버레이는 장식용(aria-hidden)입니다
  • 닫기 버튼은 실제 <button>이며, aria-label은 언어 팩 drawer.close를 사용합니다
  • 미끄러짐 애니메이션은 prefers-reduced-motion에서 페이드 인/아웃으로 낮아집니다

타입 정의 ​

이 컴포넌트가 외부로 내보내는 TypeScript 타입 구조는 다음과 같습니다(packages/components/drawer/src/types.ts와 @lh-ui/share/core/* 소스에서 자동 추출, 각 항목을 펼쳐 전체 정의를 확인하세요. 속성 설명은 위쪽 Props 표를 참고하세요).

LhDrawerProps
ts
/**
 * Drawer 컴포넌트 Props
 */
export interface LhDrawerProps {
  /** 표시 여부(v-model) */
  modelValue?: boolean
  /** 제목 */
  title?: string
  /** 방향 */
  direction?: LhDrawerDirection
  /** 크기: 숫자는 px, 문자열은 백분율 지원 */
  size?: string | number
  /** 오버레이 클릭 시 닫기 여부 */
  maskClosable?: boolean
  /** 닫기 버튼 표시 여부 */
  showClose?: boolean
  /** Teleport 대상(선택자 또는 요소, 기본값 body) */
  teleportTarget?: string | HTMLElement
  /** 페이지 스크롤 잠금 여부 */
  lockScroll?: boolean
  /** Esc 닫기 응답 여부 */
  closeOnEsc?: boolean
  /** 컴포넌트 수준 테마 변수, ConfigProvider 설정을 덮어씀 */
  themeVars?: Record<string, string>
  /** 컴포넌트 수준 배경 문양, ConfigProvider 설정을 덮어씀 */
  bgPattern?: string
  /** sx 빠른 레이아웃 속성(패널에 적용) */
  sx?: LhSxProps
}
LhDrawerDirection (@lh-ui/share/core/drawer (logic.ts)에서 가져옴)
ts
/**
 * Drawer 순수 함수 로직(프레임워크 의존 없음)
 */

/** 서랍 방향 */
export type LhDrawerDirection = 'right' | 'left' | 'top' | 'bottom'

국풍의 아름다움으로 코드의 토대를 세웁니다