Motile UI

Popover

트리거 요소 주변에 표시되는 경량 Popover 컴포넌트입니다. 4가지 위치(top, bottom, left, right)와 3가지 정렬(start, center, end)을 지원하며, 화살표, 커스텀 색상, 바운스 애니메이션 등 다양한 옵션을 제공합니다.

미리보기

사용법

1"use client";
2
3import { Button, Popover } from "motile-ui";
4
5export default function PreviewExample() {
6  return (
7    <Popover.Root position="bottom" align="center">
8      <Popover.Trigger asChild>
9        <Button variant="primary">Open Popover</Button>
10      </Popover.Trigger>
11      <Popover.Content>
12        <h3 style={{ margin: "0 0 8px 0", fontSize: "16px", fontWeight: 600, color: "#fff" }}>
13          Popover 제목
14        </h3>
15        <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)", lineHeight: 1.6 }}>
16          Popover 컴포넌트입니다. ESC 키를 누르거나 외부를 클릭하여 닫을 수 있습니다.
17        </p>
18      </Popover.Content>
19    </Popover.Root>
20  );
21}

API 레퍼런스

Popover.Root

Popover의 상태를 관리하는 최상위 컨텍스트 프로바이더

속성타입기본값설명
position"top" | "bottom" | "left" | "right""bottom"Popover 위치 (top, bottom, left, right)
align"start" | "center" | "end""center"Popover 정렬 방식 (start, center, end)
variant"filled" | "outlined""filled"Popover 스타일 variant (filled, outlined)
showArrowbooleanfalse화살표 표시 여부
zIndexnumber10z-index 값
colorstring-커스텀 색상 (CSS 색상 값)
bounceCountnumber | "infinite"0통통 튀는 애니메이션 횟수 (0이면 비활성화, 'infinite'로 무한 반복 가능)
openboolean-Popover 열림/닫힘 상태 (제어 컴포넌트)
defaultOpenbooleanfalse초기 열림 상태 (비제어 컴포넌트)
onOpenChange(open: boolean) => void-상태가 변경될 때 실행되는 함수
autoClosebooleantrueESC 키와 외부 클릭으로 자동으로 닫기
onClickOutside(event: PointerEvent) => void-외부 클릭 시 실행되는 함수
onDismiss(event: Event) => void-Popover가 닫힐 때 실행되는 함수 (ESC 키 또는 외부 클릭)

Popover.Trigger

Popover를 여는 트리거 요소

속성타입기본값설명
asChildbooleanfalse래퍼 없이 자식 요소만 렌더링

기본 <code>button</code> HTML 속성을 모두 사용할 수 있습니다.

Popover.Content

Popover 콘텐츠 컨테이너

속성타입기본값설명
classNamestring-CSS 클래스명
styleReact.CSSProperties-인라인 스타일 객체

기본 <code>div</code> HTML 속성을 모두 사용할 수 있습니다.

예제

화살표 포함

1"use client";
2
3import { Button, Popover } from "motile-ui";
4
5export default function WithArrowExample() {
6  return (
7    <Popover.Root position="top" align="center" showArrow>
8      <Popover.Trigger asChild>
9        <Button variant="secondary">With Arrow</Button>
10      </Popover.Trigger>
11      <Popover.Content>
12        <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
13          화살표가 트리거 버튼을 가리킵니다.
14        </p>
15      </Popover.Content>
16    </Popover.Root>
17  );
18}

다양한 위치

1"use client";
2
3import { Button, Popover } from "motile-ui";
4
5export default function PositionsExample() {
6  const positions = ["top", "bottom", "left", "right"] as const;
7
8  return (
9    <div style={{ display: "grid", gridTemplateColumns: "repeat(2, 1fr)", gap: "16px" }}>
10      {positions.map((position) => (
11        <Popover.Root key={position} position={position} align="center">
12          <Popover.Trigger asChild>
13            <Button variant="secondary">
14              {position.charAt(0).toUpperCase() + position.slice(1)}
15            </Button>
16          </Popover.Trigger>
17          <Popover.Content>
18            <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
19              Position: {position}
20            </p>
21          </Popover.Content>
22        </Popover.Root>
23      ))}
24    </div>
25  );
26}

정렬 방식

1"use client";
2
3import { Button, Popover } from "motile-ui";
4
5export default function AlignmentsExample() {
6  const alignments = ["start", "center", "end"] as const;
7
8  return (
9    <div style={{ display: "flex", gap: "16px", flexWrap: "wrap" }}>
10      {alignments.map((align) => (
11        <Popover.Root key={align} position="top" align={align} showArrow>
12          <Popover.Trigger asChild>
13            <Button variant="secondary">
14              Align: {align.charAt(0).toUpperCase() + align.slice(1)}
15            </Button>
16          </Popover.Trigger>
17          <Popover.Content>
18            <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
19              정렬 방식: <strong>{align}</strong>
20            </p>
21          </Popover.Content>
22        </Popover.Root>
23      ))}
24    </div>
25  );
26}

Variant 스타일

1"use client";
2
3import { Button, Popover } from "motile-ui";
4
5export default function VariantsExample() {
6  return (
7    <div style={{ display: "flex", gap: "16px" }}>
8      <Popover.Root position="bottom" align="center" variant="filled">
9        <Popover.Trigger asChild>
10          <Button variant="primary">Filled</Button>
11        </Popover.Trigger>
12        <Popover.Content>
13          <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
14            Filled 스타일입니다.
15          </p>
16        </Popover.Content>
17      </Popover.Root>
18
19      <Popover.Root position="bottom" align="center" variant="outlined">
20        <Popover.Trigger asChild>
21          <Button variant="secondary">Outlined</Button>
22        </Popover.Trigger>
23        <Popover.Content>
24          <p style={{ margin: 0, fontSize: "14px", color: "#374151" }}>
25            Outlined 스타일입니다.
26          </p>
27        </Popover.Content>
28      </Popover.Root>
29    </div>
30  );
31}

커스텀 색상

1"use client";
2
3import { Button, Popover } from "motile-ui";
4
5export default function CustomColorExample() {
6  return (
7    <div style={{ display: "flex", gap: "16px", flexWrap: "wrap" }}>
8      <Popover.Root position="bottom" align="center" color="#10b981" showArrow>
9        <Popover.Trigger asChild>
10          <Button variant="secondary" color="#10b981">
11            Green
12          </Button>
13        </Popover.Trigger>
14        <Popover.Content>
15          <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
16            커스텀 색상: 녹색
17          </p>
18        </Popover.Content>
19      </Popover.Root>
20
21      <Popover.Root position="bottom" align="center" color="#f59e0b" showArrow>
22        <Popover.Trigger asChild>
23          <Button variant="secondary" color="#f59e0b">
24            Orange
25          </Button>
26        </Popover.Trigger>
27        <Popover.Content>
28          <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
29            커스텀 색상: 주황색
30          </p>
31        </Popover.Content>
32      </Popover.Root>
33
34      <Popover.Root position="bottom" align="center" color="#8b5cf6" showArrow>
35        <Popover.Trigger asChild>
36          <Button variant="secondary" color="#8b5cf6">
37            Purple
38          </Button>
39        </Popover.Trigger>
40        <Popover.Content>
41          <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
42            커스텀 색상: 보라색
43          </p>
44        </Popover.Content>
45      </Popover.Root>
46    </div>
47  );
48}

수동으로 닫기

1"use client";
2
3import { useState } from "react";
4import { Button, Popover } from "motile-ui";
5
6export default function NoAutoCloseExample() {
7  const [open, setOpen] = useState(false);
8
9  return (
10    <Popover.Root
11      open={open}
12      onOpenChange={setOpen}
13      position="bottom"
14      align="center"
15      variant="outlined"
16      autoClose={false}
17    >
18      <Popover.Trigger asChild>
19        <Button variant="primary">No Auto Close</Button>
20      </Popover.Trigger>
21      <Popover.Content>
22        <h3 style={{ margin: "0 0 8px 0", fontSize: "16px", fontWeight: 600, color: "#374151" }}>
23          수동으로 닫기
24        </h3>
25        <p style={{ margin: "0 0 12px 0", fontSize: "14px", color: "#374151", lineHeight: 1.6 }}>
26          ESC 키나 외부 클릭으로 닫히지 않습니다. 버튼을 클릭해주세요.
27        </p>
28        <Button
29          variant="secondary"
30          size="small"
31          onClick={() => setOpen(false)}
32        >
33          닫기
34        </Button>
35      </Popover.Content>
36    </Popover.Root>
37  );
38}

바운스 애니메이션

1"use client";
2
3import { Button, Popover } from "motile-ui";
4
5export default function BounceAnimationExample() {
6  return (
7    <div style={{ display: "flex", gap: "16px", flexWrap: "wrap" }}>
8      <Popover.Root position="top" align="center" showArrow bounceCount={1}>
9        <Popover.Trigger asChild>
10          <Button variant="secondary">Bounce: 1</Button>
11        </Popover.Trigger>
12        <Popover.Content>
13          <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
14            1번 통통 튑니다.
15          </p>
16        </Popover.Content>
17      </Popover.Root>
18
19      <Popover.Root position="top" align="center" showArrow bounceCount={3}>
20        <Popover.Trigger asChild>
21          <Button variant="secondary">Bounce: 3</Button>
22        </Popover.Trigger>
23        <Popover.Content>
24          <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
25            3번 통통 튑니다.
26          </p>
27        </Popover.Content>
28      </Popover.Root>
29
30      <Popover.Root position="top" align="center" showArrow bounceCount="infinite">
31        <Popover.Trigger asChild>
32          <Button variant="secondary">Bounce: Infinite</Button>
33        </Popover.Trigger>
34        <Popover.Content>
35          <p style={{ margin: 0, fontSize: "14px", color: "rgba(255, 255, 255, 0.9)" }}>
36            무한 통통 튑니다.
37          </p>
38        </Popover.Content>
39      </Popover.Root>
40    </div>
41  );
42}
Popover | Motile UI