Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

143 changes: 143 additions & 0 deletions docs/content/react/components/popover.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
---
title: Popover
description: 트리거 요소에 앵커링되어 화면 위에 떠 있는 컨테이너입니다. Header/Body/Footer 구조로 부가 정보나 액션을 제공할 때 사용됩니다.
---

<ComponentExample name="react/popover/preview">
```json doc-gen:file
{
"file": "examples/react/popover/preview.tsx",
"codeblock": true
}
```
</ComponentExample>

## Installation

```package-install
npx @seed-design/cli@latest add ui:popover
```

<ManualInstallation name="popover" />

## Props

### `PopoverRoot`

<react-type-table
path="./registry/react/ui/popover.tsx"
name="PopoverRootProps"
/>

### `PopoverTrigger`

<react-type-table
path="./registry/react/ui/popover.tsx"
name="PopoverTriggerProps"
/>

### `PopoverAnchor`

<react-type-table
path="./registry/react/ui/popover.tsx"
name="PopoverAnchorProps"
/>

### `PopoverContent`

<react-type-table
path="./registry/react/ui/popover.tsx"
name="PopoverContentProps"
/>

### `PopoverBody`

<react-type-table
path="./registry/react/ui/popover.tsx"
name="PopoverBodyProps"
/>

### `PopoverFooter`

<react-type-table
path="./registry/react/ui/popover.tsx"
name="PopoverFooterProps"
/>

## Examples

### Trigger

`<PopoverTrigger>`는 `asChild` 패턴을 사용해 자식 요소가 Popover를 열 수 있도록 합니다.

<ComponentExample name="react/popover/trigger">
```json doc-gen:file
{
"file": "examples/react/popover/trigger.tsx",
"codeblock": true
}
```
</ComponentExample>

### Controlled

Trigger 외의 방식으로 Popover를 열고 닫을 수 있습니다. 이 경우 `open` prop을 사용하여 Popover의 상태를 제어합니다.

<ComponentExample name="react/popover/controlled">
```json doc-gen:file
{
"file": "examples/react/popover/controlled.tsx",
"codeblock": true
}
```
</ComponentExample>

### Placement

`<PopoverRoot>`에 `placement` prop을 설정하여 트리거 기준 위치를 지정합니다. 뷰포트 경계를 벗어나면 자동으로 뒤집히거나(flip) 이동합니다(shift). safe-area가 있는 기기에서는 노치와 홈 인디케이터 안쪽에 배치됩니다.

<ComponentExample name="react/popover/placement">
```json doc-gen:file
{
"file": "examples/react/popover/placement.tsx",
"codeblock": true
}
```
</ComponentExample>

### Anchor

`<PopoverAnchor>`를 사용하면 트리거와 분리된 요소를 기준으로 Popover의 위치를 잡을 수 있습니다. 이 경우 열림 상태는 `open` prop으로 직접 제어합니다.

<ComponentExample name="react/popover/anchor">
```json doc-gen:file
{
"file": "examples/react/popover/anchor.tsx",
"codeblock": true
}
```
</ComponentExample>

### Scroll

`<PopoverContent>`의 `max-height`(600px)를 넘는 긴 콘텐츠는 `<PopoverBody>`가 스크롤됩니다. 스크롤 시 Body 상단에 divider가 나타나고, 하단에는 항상 scroll fog가 표시됩니다. 뷰포트가 작으면 `max-height`는 가용 높이에 맞춰 줄어듭니다.

<ComponentExample name="react/popover/scroll">
```json doc-gen:file
{
"file": "examples/react/popover/scroll.tsx",
"codeblock": true
}
```
</ComponentExample>

### Show Close Button

`<PopoverContent>`에 `showCloseButton` prop을 전달하여 닫기 버튼 표시 여부를 제어합니다. 기본값은 `true`이며, 표시될 때 Header 우측에 여백이 확보됩니다.

## Keyboard Interactions

| Key | Behavior |
| --- | --- |
| `Escape` | Popover를 닫습니다. |
| `Tab` | Popover 내부의 포커스 가능한 요소로 이동합니다. non-modal이므로 포커스가 트랩되지 않습니다. |
33 changes: 33 additions & 0 deletions docs/examples/react/popover/anchor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import { PopoverAnchor, PopoverBody, PopoverContent, PopoverRoot } from "seed-design/ui/popover";

const PopoverAnchorExample = () => {
const [open, setOpen] = useState(false);

return (
<PopoverRoot open={open} onOpenChange={setOpen}>
<PopoverAnchor asChild>
<div
style={{
padding: 16,
borderRadius: 8,
border: "1px dashed var(--seed-color-stroke-neutral-muted)",
}}
>
이 영역을 기준으로 배치됩니다
</div>
</PopoverAnchor>
<ActionButton variant="neutralSolid" onClick={() => setOpen((prev) => !prev)}>
{open ? "닫기" : "열기"}
</ActionButton>
<PopoverContent title="Anchor">
<PopoverBody>
트리거와 분리된 요소를 기준으로 Popover의 위치를 잡을 수 있습니다.
</PopoverBody>
</PopoverContent>
</PopoverRoot>
);
};

export default PopoverAnchorExample;
20 changes: 20 additions & 0 deletions docs/examples/react/popover/controlled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from "seed-design/ui/popover";

const PopoverControlled = () => {
const [open, setOpen] = useState(false);

return (
<PopoverRoot open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<ActionButton variant="neutralSolid">{open ? "닫기" : "열기"}</ActionButton>
</PopoverTrigger>
<PopoverContent title="제어 상태">
<PopoverBody>open prop으로 Popover의 열림 상태를 직접 제어합니다.</PopoverBody>
</PopoverContent>
</PopoverRoot>
);
};

export default PopoverControlled;
20 changes: 20 additions & 0 deletions docs/examples/react/popover/placement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ActionButton } from "seed-design/ui/action-button";
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from "seed-design/ui/popover";

const PopoverPlacement = () => {
return (
<PopoverRoot placement="right-start">
<PopoverTrigger asChild>
<ActionButton variant="neutralSolid">right-start</ActionButton>
</PopoverTrigger>
<PopoverContent title="Placement">
<PopoverBody>
placement prop으로 트리거 기준 위치를 지정합니다. 뷰포트를 벗어나면 자동으로 뒤집히거나
이동합니다.
</PopoverBody>
</PopoverContent>
</PopoverRoot>
);
};

export default PopoverPlacement;
28 changes: 28 additions & 0 deletions docs/examples/react/popover/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ActionButton } from "seed-design/ui/action-button";
import {
PopoverBody,
PopoverContent,
PopoverFooter,
PopoverRoot,
PopoverTrigger,
} from "seed-design/ui/popover";

const PopoverPreview = () => {
return (
<PopoverRoot>
<PopoverTrigger asChild>
<ActionButton variant="neutralSolid">Open Popover</ActionButton>
</PopoverTrigger>
<PopoverContent title="제목" description="설명을 작성할 수 있어요">
<PopoverBody>
Popover 본문에는 사용자가 확인해야 할 내용이나 추가 액션을 배치할 수 있습니다.
</PopoverBody>
<PopoverFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</PopoverFooter>
</PopoverContent>
</PopoverRoot>
);
};

export default PopoverPreview;
33 changes: 33 additions & 0 deletions docs/examples/react/popover/scroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ActionButton } from "seed-design/ui/action-button";
import {
PopoverBody,
PopoverContent,
PopoverFooter,
PopoverRoot,
PopoverTrigger,
} from "seed-design/ui/popover";

const PopoverScroll = () => {
return (
<PopoverRoot>
<PopoverTrigger asChild>
<ActionButton variant="neutralSolid">긴 콘텐츠 Popover</ActionButton>
</PopoverTrigger>
<PopoverContent title="약관 동의" description="아래 내용을 확인해주세요">
<PopoverBody>
{Array.from({ length: 20 }, (_, index) => (
<p key={index} style={{ margin: 0 }}>
{index + 1}. 본문이 길어지면 Body가 스크롤되고, 스크롤 시 상단 divider와 하단 scroll
fog가 나타납니다.
</p>
))}
</PopoverBody>
<PopoverFooter>
<ActionButton variant="neutralSolid">동의</ActionButton>
</PopoverFooter>
</PopoverContent>
</PopoverRoot>
);
};

export default PopoverScroll;
17 changes: 17 additions & 0 deletions docs/examples/react/popover/trigger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ActionButton } from "seed-design/ui/action-button";
import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from "seed-design/ui/popover";

const PopoverTriggerExample = () => {
return (
<PopoverRoot>
<PopoverTrigger asChild>
<ActionButton variant="neutralSolid">Trigger</ActionButton>
</PopoverTrigger>
<PopoverContent title="제목">
<PopoverBody>트리거를 눌러 Popover를 열 수 있습니다.</PopoverBody>
</PopoverContent>
</PopoverRoot>
);
};

export default PopoverTriggerExample;
13 changes: 13 additions & 0 deletions docs/public/__docs__/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,19 @@
}
]
},
{
"id": "popover",
"title": "Popover",
"description": "트리거 요소에 앵커링되어 화면 위에 떠 있는 컨테이너입니다. Header/Body/Footer 구조로 부가 정보나 액션을 제공할 때 사용됩니다.",
"docUrl": "/react/components/popover",
"snippetKey": "react/ui:popover",
"snippets": [
{
"label": "react",
"path": "popover.tsx"
}
]
},
{
"id": "progress-circle",
"title": "Progress Circle",
Expand Down
16 changes: 16 additions & 0 deletions docs/public/__registry__/react/ui/index.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions docs/public/__registry__/react/ui/popover.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions docs/registry/react/registry-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,15 @@ export const registryUI: Registry = {
},
],
},
{
id: "popover",
snippets: [
{
path: "popover.tsx",
dependencies: { "@seed-design/react": "^2.1.0", "@seed-design/css": "^2.2.0" },
},
],
},
{
id: "tabs",
snippets: [
Expand Down
Loading