Popover
Popover는 트리거 요소 근처에 부가 정보나 상호작용 콘텐츠를 표시하는 오버레이 컴포넌트입니다. 툴팁, 메뉴, 폼, 상세 정보 등을 표시할 때 사용합니다.'use client';
import { Button, Popover } from '@vapor-ui/core';
export default function DefaultPopover() {
return (
<div className="flex justify-center p-20">
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>팝오버 열기</Popover.Trigger>
<Popover.Content>
<Popover.Title>알림</Popover.Title>
<Popover.Description>
새로운 메시지 3개와 알림 1개가 있습니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
</div>
);
}
Property
PositionerProps
Popover가 나타날 위치와 정렬을 설정할 수 있습니다. 기본값은 'bottom'입니다.
'use client';
import { Button, Popover } from '@vapor-ui/core';
export default function PopoverPositioning() {
return (
<div className="grid grid-cols-2 gap-8 p-20">
<div className="space-y-6">
<h3 className="text-sm font-medium text-foreground-hint">방향 설정</h3>
<div className="grid grid-cols-2 gap-4">
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
상단 팝오버
</Popover.Trigger>
<Popover.Content positionerProps={{ side: 'top' }}>
<Popover.Title>상단 팝오버</Popover.Title>
<Popover.Description>
트리거 위쪽에 표시되는 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
우측 팝오버
</Popover.Trigger>
<Popover.Content positionerProps={{ side: 'right' }}>
<Popover.Title>우측 팝오버</Popover.Title>
<Popover.Description>
트리거 오른쪽에 표시되는 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
하단 팝오버
</Popover.Trigger>
<Popover.Content positionerProps={{ side: 'bottom' }}>
<Popover.Title>하단 팝오버</Popover.Title>
<Popover.Description>
트리거 아래쪽에 표시되는 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
좌측 팝오버
</Popover.Trigger>
<Popover.Content positionerProps={{ side: 'left' }}>
<Popover.Title>좌측 팝오버</Popover.Title>
<Popover.Description>
트리거 왼쪽에 표시되는 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
</div>
</div>
<div className="space-y-6">
<h3 className="text-sm font-medium text-foreground-hint">정렬 설정</h3>
<div className="space-y-4">
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
시작점 정렬
</Popover.Trigger>
<Popover.Content positionerProps={{ align: 'start' }}>
<Popover.Title>시작점 정렬</Popover.Title>
<Popover.Description>
트리거의 시작점에 정렬된 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
중앙 정렬
</Popover.Trigger>
<Popover.Content positionerProps={{ align: 'center' }}>
<Popover.Title>중앙 정렬</Popover.Title>
<Popover.Description>
트리거의 중앙에 정렬된 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
끝점 정렬
</Popover.Trigger>
<Popover.Content positionerProps={{ align: 'end' }}>
<Popover.Title>끝점 정렬</Popover.Title>
<Popover.Description>
트리거의 끝점에 정렬된 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
</div>
</div>
</div>
);
}
Controlled State
Popover의 열림/닫힘 상태를 외부에서 제어할 수 있습니다.
'use client';
import { useState } from 'react';
import { Button, Popover } from '@vapor-ui/core';
export default function PopoverControlled() {
const [isOpen, setIsOpen] = useState(false);
return (
<div className="flex flex-col items-center gap-4 p-20">
<div className="flex gap-2">
<Button variant="outline" onClick={() => setIsOpen(true)}>
팝오버 열기
</Button>
<Button variant="outline" onClick={() => setIsOpen(false)}>
팝오버 닫기
</Button>
</div>
<p className="text-sm text-foreground-hint">현재 상태: {isOpen ? '열림' : '닫힘'}</p>
<Popover.Root open={isOpen} onOpenChange={setIsOpen}>
<Popover.Trigger render={<Button />}>제어되는 팝오버</Popover.Trigger>
<Popover.Content>
<Popover.Title>제어되는 팝오버</Popover.Title>
<Popover.Description>
이 팝오버는 외부 상태에 의해 제어됩니다. 위의 버튼으로 열고 닫을 수
있습니다.
</Popover.Description>
<div className="mt-4 flex gap-2">
<Button size="sm" onClick={() => setIsOpen(false)}>
닫기
</Button>
</div>
</Popover.Content>
</Popover.Root>
</div>
);
}
Offset
Popover와 트리거 간의 거리를 세밀하게 조정할 수 있습니다.
'use client';
import { Button, Popover } from '@vapor-ui/core';
export default function PopoverOffset() {
return (
<div className="flex flex-col items-center gap-8 p-20">
<div className="space-y-6">
<h3 className="text-center text-sm font-medium text-foreground-hint">
오프셋 조정
</h3>
<div className="flex gap-4">
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
기본 오프셋
</Popover.Trigger>
<Popover.Content>
<Popover.Title>기본 오프셋</Popover.Title>
<Popover.Description>
기본 8px 오프셋이 적용된 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
사이드 오프셋 16px
</Popover.Trigger>
<Popover.Content positionerProps={{ sideOffset: 16 }}>
<Popover.Title>사이드 오프셋 16px</Popover.Title>
<Popover.Description>
트리거로부터 16px 떨어진 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
정렬 오프셋 20px
</Popover.Trigger>
<Popover.Content positionerProps={{ alignOffset: 20 }}>
<Popover.Title>정렬 오프셋 20px</Popover.Title>
<Popover.Description>
정렬 축에서 20px 이동한 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
복합 오프셋
</Popover.Trigger>
<Popover.Content positionerProps={{ sideOffset: 24, alignOffset: -10 }}>
<Popover.Title>복합 오프셋</Popover.Title>
<Popover.Description>
사이드 24px, 정렬 -10px 오프셋이 적용된 팝오버입니다.
</Popover.Description>
</Popover.Content>
</Popover.Root>
</div>
</div>
</div>
);
}
Examples
Content Variations
팝오버는 간단한 텍스트부터 복잡한 상호작용 요소까지 다양한 콘텐츠를 담을 수 있습니다.
'use client';
import { Button, Popover } from '@vapor-ui/core';
export default function PopoverContent() {
return (
<div className="flex flex-wrap gap-4 p-20">
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
간단한 텍스트
</Popover.Trigger>
<Popover.Content>간단한 팝오버 메시지입니다.</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>제목과 설명</Popover.Trigger>
<Popover.Content>
<Popover.Title>알림</Popover.Title>
<Popover.Description>
새로운 업데이트가 있습니다. 확인해보세요.
</Popover.Description>
</Popover.Content>
</Popover.Root>
<Popover.Root>
<Popover.Trigger render={<Button variant="outline" />}>
상호작용 콘텐츠
</Popover.Trigger>
<Popover.Content>
<Popover.Title>설정</Popover.Title>
<Popover.Description>원하는 설정을 선택하세요.</Popover.Description>
<div className="mt-4 space-y-2">
<Button size="sm" className="w-full">
옵션 1
</Button>
<Button size="sm" variant="outline" className="w-full">
옵션 2
</Button>
</div>
</Popover.Content>
</Popover.Root>
</div>
);
}
Props Table
Popover.Root
Popover의 루트 컨테이너로, 전체 Popover 컴포넌트의 상태와 동작을 관리합니다.
Prop | Default | Type |
---|---|---|
open? | undefined | boolean |
defaultOpen? | false | boolean |
onOpenChange? | undefined | (open: boolean, event?: Event, reason?: string) => void |
modal? | true | boolean{ trapFocus?: boolean } |
openOnHover? | false | boolean |
delay? | 0 | number{ open?: number; close?: number } |
Popover.Trigger
Popover를 여는 트리거 요소입니다.
Prop | Default | Type |
---|---|---|
render? | undefined | React.ReactElement(props: TriggerProps, state: TriggerState) => React.ReactElement |
disabled? | false | boolean |
Popover.Content
Popover의 실제 콘텐츠를 담는 컨테이너입니다. Portal과 Positioner를 조합하여 구성됩니다.
Prop | Default | Type |
---|---|---|
portalProps? | undefined | PopoverPortalProps |
positionerProps? | undefined | PopoverPositionerProps |
className? | undefined | string |
Popover.Portal
Popover를 DOM의 다른 위치에 렌더링하는 포털 컴포넌트입니다.
Prop | Default | Type |
---|---|---|
container? | document.body | HTMLElement() => HTMLElementnull |
keepMounted? | false | boolean |
Popover.Positioner
Popover의 위치를 설정하는 컴포넌트입니다.
Prop | Default | Type |
---|---|---|
side? | 'bottom' | 'top''right''bottom''left' |
align? | 'center' | 'start''center''end' |
sideOffset? | 8 | number(side: Side) => number |
alignOffset? | 0 | number(side: Side) => number |
collisionPadding? | 8 | number{ top?: number; right?: number; bottom?: number; left?: number } |
collisionAvoidance? | true | boolean{ boundary?: HTMLElement; rootBoundary?: 'viewport' | 'document'; padding?: number } |
arrowPadding? | 4 | number |
sticky? | false | boolean'partial' |
Popover.Popup
Popover의 실제 팝업 콘텐츠 영역입니다.
Prop | Default | Type |
---|---|---|
className? | undefined | string |
style? | undefined | CSSProperties |
render? | undefined | React.ReactElement(props: PopupProps, state: PopupState) => React.ReactElement |
Popover.Title
Popover의 제목을 표시하는 컴포넌트입니다.
Prop | Default | Type |
---|---|---|
render? | undefined | React.ReactElement(props: TitleProps) => React.ReactElement |
Popover.Description
Popover의 설명을 표시하는 컴포넌트입니다.
Prop | Default | Type |
---|---|---|
render? | undefined | React.ReactElement(props: DescriptionProps) => React.ReactElement |
Popover.Close
Popover를 닫는 버튼 컴포넌트입니다.
Prop | Default | Type |
---|---|---|
render? | undefined | React.ReactElement(props: CloseProps) => React.ReactElement |