@olundot/ui · Surfaces
Dialog
집중된 결정, 확인, 폼 입력, 중요 운영 분기를 위한 모달 서피스입니다.
- 슬롯
- DialogTrigger · DialogContent · DialogHeader · DialogTitle · DialogDescription · DialogFooter · DialogClose
- ConfirmDialog
- open · onOpenChange · tone(default|destructive) · confirmLabel · onConfirm(async)
- 포커스 트랩
- Radix 자동 — 열릴 때 첫 포커스 이동, 닫힐 때 트리거로 복귀
- showCloseButton
- DialogContent prop — X 버튼 표시 여부 (기본 false)
- 라이브 검증
- DialogTrigger asChild + ConfirmDialog open/onOpenChange 패턴으로 실제 열기/닫기 확인 필요
- Dismiss policy
- X 버튼을 사용하지 않습니다. 닫기 방법은 footer 버튼(explicit) + 바깥 클릭(passive) + Esc(keyboard) 세 가지입니다. simple은 확인 1버튼, standard는 confirm pair, 파괴적 액션은 `<ConfirmDialog>`를 사용합니다.
Simple (안내형)
라이브 (X button 없음 — 확인 버튼만. 단순 안내 · 정보 전달용)코드 보기tsx
import {
Dialog, DialogTrigger, DialogContent,
DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose,
} from "@olundot/ui";
import { Button } from "@olundot/ui";
// simple variant: showCloseButton 없음 (기본 false) — X 버튼 미표시.
// 단순 안내 · 정보 전달에 사용. Cancel 버튼 하나로 dismiss.
// outside click / Esc 로도 닫힘 (Radix 기본).
// DialogContent 기본 패딩 = var(--space-4) (32px).
export function DialogSimple() {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">안내 보기</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>응시 안내</DialogTitle>
<DialogDescription>
시험은 60분 제한이며, 제한 시간 종료 시 자동 제출됩니다.
안정적인 네트워크 환경에서 응시하세요.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">확인</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}Standard (표준)
라이브 (X 버튼 없음 — Cancel + Submit + 바깥 클릭 / Esc 로 닫기)코드 보기tsx
import { useState } from "react";
import {
Dialog, DialogTrigger, DialogContent,
DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose,
} from "@olundot/ui";
import { Button } from "@olundot/ui";
// standard variant: 닫기 버튼 미사용 정책 (showCloseButton 사용 안 함).
// dismiss = footer Cancel (explicit user intent) + outside click (passive) + Esc (keyboard).
// 폼 입력 · 데이터 등록 등 일반적인 작업에 사용.
export function DialogStandard() {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="primary">메모 추가</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>학습 메모 추가</DialogTitle>
<DialogDescription>이 문항에 대한 학습 노트를 남깁니다.</DialogDescription>
</DialogHeader>
<div style={{ display: "grid", gap: "12px" }}>
<label style={{ display: "grid", gap: "4px", fontSize: "0.875rem" }}>
<span>제목</span>
<input
type="text"
placeholder="메모 제목"
style={{
padding: "8px 12px",
borderRadius: "var(--radius-md)",
border: "1px solid var(--border-subtle)",
background: "var(--bg-surface)",
color: "var(--text-primary)",
}}
/>
</label>
<label style={{ display: "grid", gap: "4px", fontSize: "0.875rem" }}>
<span>내용</span>
<textarea
rows={3}
placeholder="학습 메모를 입력하세요"
style={{
padding: "8px 12px",
borderRadius: "var(--radius-md)",
border: "1px solid var(--border-subtle)",
background: "var(--bg-surface)",
color: "var(--text-primary)",
resize: "vertical",
}}
/>
</label>
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">취소</Button>
</DialogClose>
<Button variant="primary">저장</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}Destructive (ConfirmDialog)
라이브 (ConfirmDialog — destructive tone + async onConfirm + loading 상태)코드 보기tsx
import { useState } from "react";
import { ConfirmDialog } from "@olundot/ui";
import { Button } from "@olundot/ui";
// destructive variant: ConfirmDialog 전용 컴포넌트 사용.
// 비가역 액션(삭제·취소·초기화)에서 명시적 confirm 요구.
// onConfirm async 지원 — loading 상태 자동 처리.
export function DialogDestructive() {
const [open, setOpen] = useState(false);
return (
<div style={{ display: "flex", gap: "8px" }}>
<Button variant="destructive" onClick={() => setOpen(true)}>
수강 취소
</Button>
<ConfirmDialog
open={open}
onOpenChange={setOpen}
title="수강을 취소하시겠습니까?"
description="취소 후에는 해당 강의에 접근할 수 없게 됩니다."
tone="destructive"
confirmLabel="수강 취소"
cancelLabel="돌아가기"
onConfirm={async () => {
await new Promise((r) => setTimeout(r, 1500));
}}
/>
</div>
);
}