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-18
08-18
08-14
08-18
08-18
08-18
08-18
08-18
File name
Commit message
Commit date
"use client";
import type { KeyboardEvent, ReactNode, Ref } from "react";
import { cx } from "../../utils";
export interface FoxTabItemProps {
children: ReactNode;
/** 이 탭을 가리키는 값. `FoxTab`의 `value`와 같으면 고른 탭이 된다. */
value: string;
/**
* 주면 `<a>`가 된다 — 주소가 바뀌는 탭(라우팅)일 때 쓴다. 그때는 목록이 탭 역할을
* 갖지 않는다(그건 `FoxTab`이 판단한다).
*/
href?: string;
/** 고른 탭이 보여 줄 영역의 id. `role="tab"`일 때만 쓰인다. */
controls?: string;
disabled?: boolean;
/** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
hidden?: boolean;
/** 배치 조정용. */
className?: string;
/** FoxTab이 채운다 — 고른 탭인지. 직접 넘기지 않는다. */
active?: boolean;
/** FoxTab이 채운다 — 탭 역할을 갖는 목록인지. 직접 넘기지 않는다. */
tabRole?: boolean;
/** FoxTab이 채운다. 직접 넘기지 않는다. */
onSelect?: (value: string) => void;
/** FoxTab이 채운다 — 화살표 이동. 직접 넘기지 않는다. */
onNavigate?: (event: KeyboardEvent<HTMLElement>) => void;
/** FoxTab이 채운다. 직접 넘기지 않는다. */
ref?: Ref<HTMLElement>;
}
/**
* @fox 탭 하나. 고른 상태는 `FoxTab`이 소유한다 — 항목이 스스로 뒤집지 않는다.
*
* `href`를 주면 `<a>`가 되고, 없으면 `<button role="tab">`이 된다. 주소가 바뀌는 탭에
* `role="tab"`을 붙이면 스크린리더가 같은 화면 안의 전환으로 잘못 안내한다.
*
* ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
* (또는 개별 파티셜)로 한 번 불러와야 한다.
*/
export function FoxTabItem({
children,
value,
href,
controls,
disabled = false,
hidden = false,
className,
active = false,
tabRole = true,
onSelect,
onNavigate,
ref,
}: FoxTabItemProps) {
if (hidden) {
return null;
}
const classes = cx("fox-tab-item", active && "fox-tab-item--active", className);
if (href) {
return (
<a
ref={ref as Ref<HTMLAnchorElement>}
className={classes}
href={href}
aria-current={active ? "page" : undefined}
aria-disabled={disabled || undefined}
onClick={() => onSelect?.(value)}
onKeyDown={onNavigate}
>
{children}
</a>
);
}
return (
<button
ref={ref as Ref<HTMLButtonElement>}
className={classes}
type="button"
role={tabRole ? "tab" : undefined}
aria-selected={tabRole ? active : undefined}
aria-controls={tabRole ? controls : undefined}
// 고른 탭만 탭키로 닿고 나머지는 화살표로 옮긴다(ARIA 탭 규약).
tabIndex={tabRole && !active ? -1 : undefined}
disabled={disabled}
onClick={() => onSelect?.(value)}
onKeyDown={onNavigate}
>
{children}
</button>
);
}