Vue3 기반 팝오버(Popover) 컴포넌트 구현

아래와 같은 결과를 생성합니다:

라이브 미리보기

API

Popover

매개변수 설명 타입 기본값
label 팝오버의 제목 string | slot undefined
labelStyle 제목 스타일 정의 CSSProperties {}
info 팝오버 본문 내용 string | slot undefined
infoStyle 본문 스타일 정의 CSSProperties {}
keyControl 키보드 입력(enter 표시; esc 닫기) 지원 여부 boolean true
popupStyle 팝오버 전체 스타일 설정 CSSProperties {}

추가적인 속성은 Tooltip 참조.

Slots

이름 설명 타입
label 사용자 정의 제목 v-slot:label
info 사용자 정의 본문 v-slot:info
default 기본 컨텐츠 v-solt:default

이벤트

이름 설명 타입
visibilityChange 보이거나 숨김 상태 변경 시 콜백 (visible: boolean) => void

팝오버 컴포넌트 만들기 Popover.vue

아래 도구 함수들을 사용합니다:

  • useSlotCheck: 슬롯 존재 확인
  • useAnimationFrame: requestAnimationFrame을 활용한 setTimeout 및 setInterval 대체
<script setup lang="ts">
import { computed } from 'vue'
import type { CSSProperties } from 'vue'
import Overlay from 'components/overlay'
import { useSlotCheck } from 'components/utils'

export interface PopoverProps {
  label?: string // 제목
  labelStyle?: CSSProperties // 제목 스타일
  info?: string // 본문 내용
  infoStyle?: CSSProperties // 본문 스타일
  keyControl?: boolean // 키보드 조작 가능 여부
  popupStyle?: CSSProperties // 팝오버 스타일
}

const props = withDefaults(defineProps<PopoverProps>(), {
  label: undefined,
  labelStyle: () => ({}),
  info: undefined,
  infoStyle: () => ({}),
  keyControl: true,
  popupStyle: () => ({})
})

const slotCheck = useSlotCheck(['label', 'info'])
const hasLabel = computed(() => slotCheck.label || props.label)
const hasInfo = computed(() => slotCheck.info || props.info)
</script>

<template>
  <Overlay
    max-width="auto"
    bg-color="#fff"
    :popup-style="{
      padding: '12px',
      borderRadius: '8px',
      textAlign: 'start',
      ...popupStyle
    }"
    :key-control="keyControl"
    :transition-duration="200"
  >
    <template #content>
      <div v-if="hasLabel" class="popover-label" :class="{ mb8: hasInfo }" :style="labelStyle">
        <slot name="label">{{ label }}</slot>
      </div>
      <div v-if="hasInfo" class="popover-info" :style="infoStyle">
        <slot name="info">{{ info }}</slot>
      </div>
    </template>
    <slot></slot>
  </Overlay>
</template>

<style lang="less" scoped>
.popover-label {
  min-width: 176px;
  color: rgba(0, 0, 0, 0.88);
  font-weight: 600;
}
.mb8 {
  margin-bottom: 8px;
}
.popover-info {
  color: rgba(0, 0, 0, 0.88);
}
</style>

페이지에서 컴포넌트 사용하기

다음과 같은 구성 요소들이 필요합니다:

  • Vue3 버튼(Button)
  • Vue3 간격(Space)
<script setup lang="ts">
import Popover from './Popover.vue'
import { ref } from 'vue'

const visible = ref(false)
function onVisibilityChange(visible: boolean) {
  console.log('visible', visible)
}
</script>

<template>
  <div class="ml60">
    <h1>{{ $route.name }} {{ $route.meta.title }}</h1>
    <h2 class="mt30 mb10">기본 사용법</h2>
    <Popover label="제목" @visibility-change="onVisibilityChange">
      <template #info>
        <p>내용</p>
        <p>내용</p>
      </template>
      <Button type="primary">마우스오버</Button>
    </Popover>
    
    <h2 class="mt30 mb10">사용자 정의 스타일</h2>
    <Popover
      :max-width="180"
      bg-color="#000"
      :label-style="{ fontSize: '16px', fontWeight: 'bold', color: '#1677ff' }"
      :info-style="{ color: '#fff' }"
      :popup-style="{ padding: '12px 18px', borderRadius: '12px' }"
    >
      <template #label> 사용자 정의 제목 </template>
      <template #info>
        <p>사용자 정의 내용</p>
        <p>사용자 정의 내용</p>
      </template>
      <Button type="primary">마우스오버</Button>
    </Popover>
    
    <h2 class="mt30 mb10">다양한 트리거 방식</h2>
    <Space>
      <Popover label="호버 제목">
        <template #info>
          <p>내용</p>
          <p>내용</p>
        </template>
        <Button type="primary">호버</Button>
      </Popover>
      <Popover label="클릭 제목" trigger="click">
        <template #info>
          <p>내용</p>
          <p>내용</p>
        </template>
        <Button type="primary">클릭</Button>
      </Popover>
    </Space>
    
    <h2 class="mt30 mb10">팝업 내에서 닫기</h2>
    <h3 class="mb10">show 속성을 통해 표시/숨김 제어</h3>
    <Popover v-model:visible="visible" label="클릭 제목" trigger="click">
      <template #info>
        <a @click="visible = false">닫기</a>
      </template>
      <Button type="primary">클릭</Button>
    </Popover>
    
    <h2 class="mt30 mb10">사용자 정의 전환 시간</h2>
    <Popover label="전환 시간 300ms" :transition-duration="300">
      <template #info>
        <p>내용</p>
        <p>내용</p>
      </template>
      <Button type="primary">전환 시간 300ms</Button>
    </Popover>
    
    <h2 class="mt30 mb10">지연 표시/숨김</h2>
    <Space>
      <Popover :show-delay="300" :hide-delay="300" label="지연 300ms" info="Vue Amazing UI">
        <Button type="primary">지연 300ms 팝오버</Button>
      </Popover>
      <Popover :show-delay="500" :hide-delay="500" label="지연 500ms" info="Vue Amazing UI">
        <Button type="primary">지연 500ms 팝오버</Button>
      </Popover>
    </Space>
    
    <h2 class="mt30 mb10">화살표 숨기기</h2>
    <Popover :arrow="false" label="제목">
      <template #info>
        <p>내용</p>
        <p>내용</p>
      </template>
      <Button type="primary">화살표 숨기기</Button>
    </Popover>
  </div>
</template>

태그: vue3 Popover Component

8월 1일 14:12에 게시됨