Popover
Feedback & Status
트리거 자리에서 짧은 정보나 설정을 띄우는 작은 패널.
Usage
클릭으로 열어 그 자리에서 짧은 정보·간단한 설정을 끝내고 닫는 작은 패널. 호버로 띄우는 부가 정보는 HoverCard, 한 줄 설명은 Tooltip
import
import
import { Popover } from '@mildang/design-system/Popover';예제를 복사해 쓸 때 필요한 준비
• @mildang/styled-system 은 이 저장소에서 Panda 가 생성하는 산출물이다. 저장소 안에서는 turbo run ship 이후 쓸 수 있고, 패키지 소비자는 자기 Panda 산출물이나 다른 레이아웃 수단으로 바꿔야 한다.
API Reference
Popover Props
Prop
Type
Default
ReactNode
지정 안 함
Element | DocumentFragment
지정 안 함
ReactNode
지정 안 함
Omit<PopoverContentProps & RefAttributes<HTMLDivElement>, "ref">
지정 안 함
boolean
false
boolean
false
boolean
true
SystemStyleObject
지정 안 함
Omit<PopoverTriggerProps & RefAttributes<HTMLButtonElement>, "ref">
지정 안 함
"standard" | "custom"
standard
너비 방식 — standard vs custom
type="standard"(기본)는 너비가 312px 로 고정된다. 다만 항상 가용 폭 상한이 함께 걸려
있어서, 뷰포트가 312px 보다 좁아지면 패널도 그 폭에 맞춰 함께 줄어든다.
type="custom" 은 폭을 지정하지 않아 content 가 필요한 만큼 넓어진다. 콘텐츠가 길거나
폭을 직접 잡아야 하면 Popover 에 maxWidth="500px" 처럼 panda style prop 을 얹어 상한만
준다. 그래도 복잡해지면 Popover 대신 Dialog 로 올린다.
예제
기본 사용
버튼을 눌러 여는 실제 사용 형태다. 호버로 열어야 하면 HoverCard 를 쓴다.
import type { ComponentProps } from 'react';
import { Button } from '@mildang/design-system/Button';
import { Popover } from '@mildang/design-system/Popover';
export type PopoverDefaultExampleProps = Pick<ComponentProps<typeof Popover>, 'type' | 'hideArrow'>;
export default function PopoverExample({ type = 'standard', hideArrow = false }: PopoverDefaultExampleProps) {
return (
<Popover
type={type}
hideArrow={hideArrow}
content={<div>팝오버 내용을 자유롭게 채울 수 있습니다.</div>}
ContentProps={{ 'aria-label': 'Default 팝오버' }}
>
<Button variant="tertiary">Open Popover</Button>
</Popover>
);
}
Type
import { Button } from '@mildang/design-system/Button';
import { Popover } from '@mildang/design-system/Popover';
import { css } from '@mildang/styled-system/css';
const LONG_TEXT =
'이 텍스트는 maxWidth 한계에서 줄바꿈이 일어나는지 확인하기 위한 두세 줄 길이의 본문입니다.';
export default function PopoverTypeExample() {
return (
<div className={css({ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'flex-start' })}>
<Popover type="standard" content={<div>짧은 콘텐츠 (standard 312px)</div>} ContentProps={{ side: 'right', 'aria-label': 'Standard 짧은 콘텐츠' }}>
<Button variant="tertiary">standard (312px)</Button>
</Popover>
<Popover type="standard" content={<div>{LONG_TEXT}</div>} ContentProps={{ side: 'right', 'aria-label': 'Standard 긴 콘텐츠' }}>
<Button variant="tertiary">standard — 긴 콘텐츠</Button>
</Popover>
<Popover type="custom" maxWidth="500px" content={<div>짧은 콘텐츠 (custom maxWidth 500px)</div>} ContentProps={{ side: 'right', 'aria-label': 'Custom 짧은 콘텐츠' }}>
<Button variant="tertiary">custom (maxWidth 500)</Button>
</Popover>
<Popover type="custom" maxWidth="500px" content={<div>{LONG_TEXT}</div>} ContentProps={{ side: 'right', 'aria-label': 'Custom 긴 콘텐츠' }}>
<Button variant="tertiary">custom (maxWidth 500) — 긴 콘텐츠</Button>
</Popover>
</div>
);
}
MinMaxWidth
import { Button } from '@mildang/design-system/Button';
import { Popover } from '@mildang/design-system/Popover';
import { css } from '@mildang/styled-system/css';
const LONG_TEXT =
'이 텍스트는 maxWidth 한계에서 줄바꿈이 일어나는지 확인하기 위한 두세 줄 길이의 본문입니다.';
export default function PopoverMinMaxWidthExample() {
return (
<div className={css({ display: 'flex', flexDirection: 'column', gap: '16px', alignItems: 'flex-start' })}>
<Popover type="custom" minWidth="320px" content={<div>짧은 콘텐츠 (minWidth 320만)</div>} ContentProps={{ side: 'right', 'aria-label': 'custom minWidth 320 짧은 콘텐츠' }}>
<Button variant="tertiary">custom + minWidth 320 (짧은)</Button>
</Popover>
<Popover type="custom" minWidth="320px" content={<div>{LONG_TEXT}</div>} ContentProps={{ side: 'right', 'aria-label': 'custom minWidth 320 긴 콘텐츠' }}>
<Button variant="tertiary">custom + minWidth 320 (긴)</Button>
</Popover>
<Popover type="custom" minWidth="320px" maxWidth="400px" content={<div>{LONG_TEXT}</div>} ContentProps={{ side: 'right', 'aria-label': 'custom minWidth 320 maxWidth 400' }}>
<Button variant="tertiary">custom + minWidth 320 + maxWidth 400</Button>
</Popover>
</div>
);
}
Position
import { Button } from '@mildang/design-system/Button';
import { Popover } from '@mildang/design-system/Popover';
import { css } from '@mildang/styled-system/css';
const POSITIONS = [
{ key: 'top-start', label: 'TOP-START', side: 'top', align: 'start', gridColumn: 1, gridRow: 1 },
{ key: 'top', label: 'TOP', side: 'top', align: 'center', gridColumn: 2, gridRow: 1 },
{ key: 'top-end', label: 'TOP-END', side: 'top', align: 'end', gridColumn: 3, gridRow: 1 },
{ key: 'left-start', label: 'LEFT-START', side: 'left', align: 'start', gridColumn: 1, gridRow: 3 },
{ key: 'left', label: 'LEFT', side: 'left', align: 'center', gridColumn: 1, gridRow: 5 },
{ key: 'left-end', label: 'LEFT-END', side: 'left', align: 'end', gridColumn: 1, gridRow: 7 },
{ key: 'right-start', label: 'RIGHT-START', side: 'right', align: 'start', gridColumn: 3, gridRow: 3 },
{ key: 'right', label: 'RIGHT', side: 'right', align: 'center', gridColumn: 3, gridRow: 5 },
{ key: 'right-end', label: 'RIGHT-END', side: 'right', align: 'end', gridColumn: 3, gridRow: 7 },
{ key: 'bottom-start', label: 'BOTTOM-START', side: 'bottom', align: 'start', gridColumn: 1, gridRow: 9 },
{ key: 'bottom', label: 'BOTTOM', side: 'bottom', align: 'center', gridColumn: 2, gridRow: 9 },
{ key: 'bottom-end', label: 'BOTTOM-END', side: 'bottom', align: 'end', gridColumn: 3, gridRow: 9 },
] as const;
export default function PopoverPositionExample() {
return (
<div className={css({ position: 'relative', width: '1200px', padding: '100px', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gridTemplateRows: 'auto 16px auto 120px auto 120px auto 16px auto', columnGap: '16px' })}>
{POSITIONS.map(({ key, label, side, align, gridColumn, gridRow }) => (
<div key={key} style={{ gridColumn, gridRow }}>
<Popover content={<div className={css({ width: '250px' })}>{label} 위치 팝오버</div>} ContentProps={{ side, align, 'aria-label': `${label} 팝오버` }}>
<Button variant="tertiary">{label}</Button>
</Popover>
</div>
))}
</div>
);
}