File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
08-14
08-14
08-18
08-14
08-18
08-18
File name
Commit message
Commit date
import type { ReactNode, Ref } from "react";
import { cx } from "../../utils";
export type FoxAlertType =
| "default"
| "information"
| "success"
| "warning"
| "danger";
export interface FoxAlertProps {
/**
* 알림의 성격. 아이콘·배경·테두리·제목 색이 여기서 갈린다.
*
* 계열이 바꾸는 것은 배경 · 테두리 · 제목 글자색과 아이콘이다. 본문 글자색은 계열과
* 무관하게 같다.
*
* 아이콘은 계열이 정하고 굵기는 시안대로 `duotone`이며, 색은 제목 글자와 같은 계열색이다.
*/
type?: FoxAlertType;
/** 굵은 제목 줄. 넘기지 않으면 영역을 렌더링하지 않는다. */
title?: ReactNode;
/** 제목 아래 본문. 넘기지 않으면 영역을 렌더링하지 않는다. */
message?: ReactNode;
/**
* 보조기술이 읽는 방식을 정한다(`FoxToast`와 같은 규칙).
*
* - `false`(기본): `role="status"` — 하던 말을 끊지 않는다. 화면과 함께 처음부터 놓여
* 있는 알림은 이쪽이다.
* - `true`: `role="alert"` — 읽던 것을 끊고 바로 읽는다. 사용자의 동작 결과로 **그 자리에
* 나타난** 알림에만 쓴다.
*
* `type="danger"`가 자동으로 이걸 켜지 않는다 — 위험한 내용인 것과 지금 끼어들어야 하는
* 것은 다른 문제이고, 페이지와 함께 그려지는 경고까지 낭독을 끊으면 방해만 된다.
*/
urgent?: boolean;
/** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
hidden?: boolean;
id?: string;
/** 배치 조정용. 시안의 폭(720px)을 바꿔야 할 때도 여기로 준다. */
className?: string;
ref?: Ref<HTMLDivElement>;
}
/** `Record`로 고정해 계열을 추가하면 항목 누락이 타입 에러가 되게 한다. */
const TYPE_CLASS: Record<FoxAlertType, string> = {
default: "fox-alert--default",
information: "fox-alert--information",
success: "fox-alert--success",
warning: "fox-alert--warning",
danger: "fox-alert--danger",
};
/**
* 계열이 정하는 아이콘. 호출부가 넘기지 않는다 — 무엇을 그릴지는 계열이 이미 답한다
* (`FoxTag`와 같은 규칙).
*
* 시안 `ico/*`가 Phosphor 원본이라 이름이 그대로 붙는다(`ico/Prohibit` →
* `FoxProhibitIcon`). 손으로 SVG를 그려 넣지 않는다 — 아이콘은 생성물이 단일 출처다.
*
* 굵기는 시안이 `Weight=Duotone`이라 다섯 개 모두 `duotone`이다. 기본값(`regular`)으로 두면
* 속이 빈 선 그림이 되어 시안과 달라지므로 반드시 적는다.
*
* duotone은 연한 면(`opacity` 0.2)과 진한 선을 겹쳐 그리는데 **둘 다 `currentColor`다** —
* 색은 스타일이 `.fox-alert__icon`에 준 하나를 쓰고, 연한 쪽만 알아서 흐려진다.
*/
const TYPE_ICON: Record<FoxAlertType, ReactNode> = {
default: <i className="fox-ico fox-ico--duotone fox-ico-ChatDots" aria-hidden="true" />,
information: <i className="fox-ico fox-ico--duotone fox-ico-Info" aria-hidden="true" />,
success: <i className="fox-ico fox-ico--duotone fox-ico-CheckCircle" aria-hidden="true" />,
warning: <i className="fox-ico fox-ico--duotone fox-ico-Warning" aria-hidden="true" />,
danger: <i className="fox-ico fox-ico--duotone fox-ico-Prohibit" aria-hidden="true" />,
};
/**
* @fox 알럿. 화면 안에 놓이는 띠 모양 알림이다 — 아이콘 상자 · 제목 · 본문이 한 줄로 선다.
*
* **모달이 아니다.** 화면을 덮지도, 포커스를 가두지도, 확인 버튼을 갖지도 않는다. 내용 흐름
* 안에 그대로 놓이고, 언제 나타나고 사라질지는 호출부가 정한다(`FoxToast`와 같은 방식).
*
* 상호작용이 없어 `"use client"`가 아니다 — 서버 컴포넌트로 렌더된다.
*
* ⚠️ 시안 주석: **본문은 두 줄 이내**로 쓴다. 길이를 코드가 막지는 않는다 — 세 줄이 되면
* 아이콘 상자만 가운데 남고 띠가 늘어난다.
*
* ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
* (또는 개별 파티셜)로 한 번 불러와야 한다.
*/
export function FoxAlert({
type = "default",
title,
message,
urgent = false,
hidden = false,
id,
className,
ref,
}: FoxAlertProps) {
if (hidden) {
return null;
}
return (
<div
ref={ref}
id={id}
role={urgent ? "alert" : "status"}
className={cx("fox-alert", TYPE_CLASS[type], className)}
>
{/* 아이콘이 정해지기 전에도 상자는 그린다 — 시안이 흰 판을 배치의 일부로 준다. */}
<span className="fox-alert__icon" aria-hidden="true">
{TYPE_ICON[type]}
</span>
<div className="fox-alert__content">
{title === undefined ? null : (
<strong className="fox-alert__title">{title}</strong>
)}
{message === undefined ? null : (
<span className="fox-alert__message">{message}</span>
)}
</div>
</div>
);
}