File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
"use client";
import { cx } from "../../utils";
/** `dot`은 시안 carousel-dot, `number`는 carousel-number다. */
export type FoxCarouselIndicatorType = "dot" | "number";
export interface FoxCarouselIndicatorProps {
type?: FoxCarouselIndicatorType;
/** 1부터 센다. */
current: number;
total: number;
/**
* 점을 눌러 옮길 수 있게 한다. 넘기지 않으면 표시만 하는 조각이라 누를 곳이 없다.
* `number`형에는 누를 점이 없어 쓰이지 않는다.
*/
onSelect?: (page: number) => void;
/** 스크린리더가 읽을 이름. */
label?: string;
/** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
hidden?: boolean;
/** 배치 조정용. */
className?: string;
}
/**
* @fox 캐러셀 현재 위치 표시. 흰 알약 안에 점 또는 숫자가 들어간다.
*
* 시안의 carousel-dot과 carousel-number를 하나로 둔다 — 껍데기(흰 알약·높이·좌우 여백·
* 간격)가 같고 안에 든 것만 다르다.
*
* ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
* (또는 개별 파티셜)로 한 번 불러와야 한다.
*/
export function FoxCarouselIndicator({
type = "dot",
current,
total,
onSelect,
label = "현재 위치",
hidden = false,
className,
}: FoxCarouselIndicatorProps) {
if (hidden || total < 1) {
return null;
}
const safe = Math.min(Math.max(current, 1), total);
if (type === "number") {
return (
<p
className={cx("fox-carousel-indicator", "fox-carousel-indicator--number", className)}
aria-label={`${label} ${safe} / ${total}`}
>
<span className="fox-carousel-indicator__current" aria-hidden="true">
{safe}
</span>
<span className="fox-carousel-indicator__separator" aria-hidden="true" />
<span className="fox-carousel-indicator__total" aria-hidden="true">
{total}
</span>
</p>
);
}
const dots = Array.from({ length: total }, (unused, index) => index + 1);
return (
<div
className={cx("fox-carousel-indicator", "fox-carousel-indicator--dot", className)}
role="group"
aria-label={`${label} ${safe} / ${total}`}
>
{dots.map((page) => {
const active = page === safe;
const dotClass = cx(
"fox-carousel-indicator__dot",
active && "fox-carousel-indicator__dot--active"
);
// 옮길 수 없으면 버튼으로 두지 않는다 — 눌리지 않는 버튼이 초점을 먹는다.
return onSelect ? (
<button
key={page}
className={dotClass}
type="button"
aria-label={`${page}번째로 이동`}
aria-current={active || undefined}
onClick={() => onSelect(page)}
/>
) : (
<span key={page} className={dotClass} />
);
})}
</div>
);
}